<?php
/**
 * 统一API客户端封装模块
 * 处理所有第三方API请求，包含请求封装、响应处理、错误处理等功能
 */

class ApiClient {
    private $baseUrl;
    private $apiKey;
    private $timeout = 10;
    private $retryCount = 2;
    private $retryDelay = 1000; // ms
    
    // API配置
    private static $configs = [
        'weather' => [
            'name' => '天气API',
            'providers' => [
                [
                    'name' => 'wttr.in',
                    'base_url' => 'https://wttr.in',
                    'api_key' => '',
                    'status' => 'available',
                    'docs' => 'https://wttr.in/:help'
                ],
                [
                    'name' => 'Open-Meteo',
                    'base_url' => 'https://api.open-meteo.com/v1',
                    'api_key' => '',
                    'status' => 'available',
                    'docs' => 'https://open-meteo.com/en/docs'
                ],
                [
                    'name' => '中国天气网',
                    'base_url' => 'http://www.weather.com.cn/data',
                    'api_key' => '',
                    'status' => 'available',
                    'docs' => 'http://www.weather.com.cn'
                ]
            ]
        ],
        'flight' => [
            'name' => '机票API',
            'providers' => [
                [
                    'name' => '携程机票API',
                    'base_url' => 'https://flights.ctrip.com/itinerary/api',
                    'api_key' => 'YOUR_API_KEY',
                    'status' => 'require_auth',
                    'docs' => 'https://developer.ctrip.com/'
                ],
                [
                    'name' => '飞猪机票API',
                    'base_url' => 'https://eco.taobao.com/router/rest',
                    'api_key' => 'YOUR_API_KEY',
                    'status' => 'require_auth',
                    'docs' => 'https://open.taobao.com/'
                ],
                [
                    'name' => '12306高铁API',
                    'base_url' => 'https://kyfw.12306.cn/otn',
                    'api_key' => '',
                    'status' => 'limited',
                    'docs' => 'https://www.12306.cn/'
                ]
            ]
        ]
    ];
    
    public function __construct($baseUrl, $apiKey = '') {
        $this->baseUrl = rtrim($baseUrl, '/');
        $this->apiKey = $apiKey;
    }
    
    /**
     * 执行GET请求
     */
    public function get($endpoint, $params = [], $headers = []) {
        return $this->request('GET', $endpoint, $params, $headers);
    }
    
    /**
     * 执行POST请求
     */
    public function post($endpoint, $params = [], $headers = []) {
        return $this->request('POST', $endpoint, $params, $headers);
    }
    
    /**
     * 统一请求方法
     */
    public function request($method, $endpoint, $params = [], $headers = []) {
        $url = $this->buildUrl($endpoint, $params, $method);
        $ch = curl_init($url);
        
        // 设置请求选项
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method));
        curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        
        // 设置请求头
        $defaultHeaders = [
            'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            'Accept' => 'application/json, text/plain, */*',
            'Accept-Language' => 'zh-CN,zh;q=0.9,en;q=0.8'
        ];
        
        if (!empty($this->apiKey)) {
            $defaultHeaders['Authorization'] = 'Bearer ' . $this->apiKey;
        }
        
        $allHeaders = array_merge($defaultHeaders, $headers);
        $curlHeaders = [];
        foreach ($allHeaders as $key => $value) {
            $curlHeaders[] = "$key: $value";
        }
        curl_setopt($ch, CURLOPT_HTTPHEADER, $curlHeaders);
        
        // POST请求处理
        if (strtoupper($method) === 'POST' && !empty($params)) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
        }
        
        // 重试机制
        $attempt = 0;
        $response = null;
        $error = null;
        
        do {
            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            
            if ($response === false) {
                $error = curl_error($ch);
                $attempt++;
                if ($attempt < $this->retryCount) {
                    usleep($this->retryDelay * 1000);
                }
            } else {
                break;
            }
        } while ($attempt < $this->retryCount);
        
        if ($response === false) {
            return $this->createErrorResponse('CURL_ERROR', $error);
        }
        
        // 解析响应
        $result = $this->parseResponse($response, $httpCode);
        
        curl_close($ch);
        return $result;
    }
    
    /**
     * 构建请求URL
     */
    private function buildUrl($endpoint, $params, $method) {
        $url = $this->baseUrl . '/' . ltrim($endpoint, '/');
        
        if (strtoupper($method) === 'GET' && !empty($params)) {
            $url .= '?' . http_build_query($params);
        }
        
        return $url;
    }
    
    /**
     * 解析响应数据
     */
    private function parseResponse($response, $httpCode) {
        // 尝试JSON解析
        $data = json_decode($response, true);
        
        if (json_last_error() === JSON_ERROR_NONE) {
            return $this->createSuccessResponse($data, $httpCode);
        }
        
        // 返回原始响应
        return $this->createSuccessResponse(['raw' => $response], $httpCode);
    }
    
    /**
     * 创建成功响应
     */
    private function createSuccessResponse($data, $httpCode) {
        return [
            'success' => true,
            'http_code' => $httpCode,
            'data' => $data,
            'message' => '请求成功',
            'timestamp' => time()
        ];
    }
    
    /**
     * 创建错误响应
     */
    private function createErrorResponse($code, $message, $httpCode = 0) {
        return [
            'success' => false,
            'error_code' => $code,
            'error_message' => $message,
            'http_code' => $httpCode,
            'timestamp' => time()
        ];
    }
    
    /**
     * 获取API配置信息
     */
    public static function getConfig($serviceType = null) {
        if ($serviceType && isset(self::$configs[$serviceType])) {
            return self::$configs[$serviceType];
        }
        return self::$configs;
    }
    
    /**
     * 创建特定服务的客户端实例
     */
    public static function create($serviceType, $providerIndex = 0) {
        if (!isset(self::$configs[$serviceType])) {
            throw new Exception("Unknown service type: $serviceType");
        }
        
        $providers = self::$configs[$serviceType]['providers'];
        if (!isset($providers[$providerIndex])) {
            throw new Exception("Provider index $providerIndex not found");
        }
        
        $provider = $providers[$providerIndex];
        return new self($provider['base_url'], $provider['api_key']);
    }
}

/**
 * 天气API服务类
 */
class WeatherApiService {
    private static $cityCoords = [
        '阳泉' => ['lat' => 37.87, 'lon' => 112.55],
        '北京' => ['lat' => 39.90, 'lon' => 116.40],
        '上海' => ['lat' => 31.23, 'lon' => 121.47],
        '广州' => ['lat' => 23.13, 'lon' => 113.26],
        '深圳' => ['lat' => 22.54, 'lon' => 114.06],
        '杭州' => ['lat' => 30.27, 'lon' => 120.15],
        '成都' => ['lat' => 30.57, 'lon' => 104.07],
        '重庆' => ['lat' => 29.56, 'lon' => 106.55],
        '西安' => ['lat' => 34.26, 'lon' => 108.94],
        '南京' => ['lat' => 32.06, 'lon' => 118.80],
        '武汉' => ['lat' => 30.59, 'lon' => 114.31],
        '天津' => ['lat' => 39.08, 'lon' => 117.20],
        '苏州' => ['lat' => 31.30, 'lon' => 120.62],
        '郑州' => ['lat' => 34.75, 'lon' => 113.65],
        '长沙' => ['lat' => 28.23, 'lon' => 112.94],
        '青岛' => ['lat' => 36.07, 'lon' => 120.38],
        '大连' => ['lat' => 38.91, 'lon' => 121.60],
        '厦门' => ['lat' => 24.48, 'lon' => 118.09],
        '昆明' => ['lat' => 25.04, 'lon' => 102.68],
        '丽江' => ['lat' => 26.87, 'lon' => 100.23],
        '贵阳' => ['lat' => 26.65, 'lon' => 106.63],
        '南宁' => ['lat' => 22.82, 'lon' => 108.37],
        '哈尔滨' => ['lat' => 45.75, 'lon' => 126.65],
        '沈阳' => ['lat' => 41.80, 'lon' => 123.43],
        '济南' => ['lat' => 36.65, 'lon' => 116.98],
        '合肥' => ['lat' => 31.82, 'lon' => 117.23],
        '太原' => ['lat' => 37.87, 'lon' => 112.55],
        '石家庄' => ['lat' => 38.04, 'lon' => 114.51],
        '晋城' => ['lat' => 35.49, 'lon' => 112.85],
        '桂林' => ['lat' => 25.27, 'lon' => 110.29],
        '福州' => ['lat' => 26.07, 'lon' => 119.30],
        '拉萨' => ['lat' => 29.65, 'lon' => 91.10],
        '乌鲁木齐' => ['lat' => 43.82, 'lon' => 87.62],
        '兰州' => ['lat' => 36.06, 'lon' => 103.83],
        '西宁' => ['lat' => 36.62, 'lon' => 101.78],
        '银川' => ['lat' => 38.49, 'lon' => 106.23],
        '呼和浩特' => ['lat' => 40.84, 'lon' => 111.75],
        '长春' => ['lat' => 43.88, 'lon' => 125.32],
        '南昌' => ['lat' => 28.68, 'lon' => 115.86],
        '海口' => ['lat' => 20.04, 'lon' => 110.35],
        '三亚' => ['lat' => 18.25, 'lon' => 109.50],
        '温州' => ['lat' => 28.00, 'lon' => 120.67],
        '宁波' => ['lat' => 29.87, 'lon' => 121.55],
        '无锡' => ['lat' => 31.49, 'lon' => 120.31],
        '佛山' => ['lat' => 23.02, 'lon' => 113.12],
        '东莞' => ['lat' => 23.04, 'lon' => 113.75],
        '珠海' => ['lat' => 22.27, 'lon' => 113.58],
        '中山' => ['lat' => 22.52, 'lon' => 113.39],
        '烟台' => ['lat' => 37.46, 'lon' => 121.45],
        '威海' => ['lat' => 37.51, 'lon' => 122.12],
    ];

    private static $weatherCodeMap = [
        0 => '晴', 1 => '大部晴朗', 2 => '多云', 3 => '阴天',
        45 => '雾', 48 => '雾凇', 51 => '小毛毛雨', 53 => '毛毛雨', 55 => '大毛毛雨',
        56 => '冻毛毛雨', 57 => '大冻毛毛雨',
        61 => '小雨', 63 => '中雨', 65 => '大雨', 66 => '冻雨', 67 => '大冻雨',
        71 => '小雪', 73 => '中雪', 75 => '大雪', 77 => '米雪',
        80 => '小阵雨', 81 => '中阵雨', 82 => '大阵雨',
        85 => '小阵雪', 86 => '大阵雪',
        95 => '雷阵雨', 96 => '雷阵雨伴冰雹', 99 => '强雷阵雨伴冰雹'
    ];

    private static $weatherIconMap = [
        0 => '☀️', 1 => '🌤', 2 => '⛅', 3 => '☁️',
        45 => '🌫', 48 => '🌫', 51 => '🌦', 53 => '🌦', 55 => '🌧',
        56 => '🌧', 57 => '🌧',
        61 => '🌧', 63 => '🌧', 65 => '🌧', 66 => '🌧', 67 => '🌧',
        71 => '🌨', 73 => '🌨', 75 => '❄️', 77 => '🌨',
        80 => '🌦', 81 => '🌧', 82 => '⛈',
        85 => '🌨', 86 => '❄️',
        95 => '⛈', 96 => '⛈', 99 => '⛈'
    ];

    private static $windDirMap = [
        'N' => '北风', 'NNE' => '北东北风', 'NE' => '东北风', 'ENE' => '东东北风',
        'E' => '东风', 'ESE' => '东东南风', 'SE' => '东南风', 'SSE' => '南东南风',
        'S' => '南风', 'SSW' => '南西南风', 'SW' => '西南风', 'WSW' => '西西南风',
        'W' => '西风', 'WNW' => '西西北风', 'NW' => '西北风', 'NNW' => '北西北风'
    ];

    private static $wttrDescMap = [
        'Sunny' => '晴', 'Clear' => '晴', 'Partly cloudy' => '多云',
        'Cloudy' => '阴', 'Overcast' => '阴天', 'Mist' => '雾',
        'Fog' => '雾', 'Light rain' => '小雨', 'Moderate rain' => '中雨',
        'Heavy rain' => '大雨', 'Light snow' => '小雪', 'Moderate snow' => '中雪',
        'Heavy snow' => '大雪', 'Thunderstorm' => '雷暴', 'Patchy rain possible' => '可能有零星小雨',
        'Light drizzle' => '毛毛雨', 'Freezing fog' => '冻雾',
        'Patchy light drizzle' => '零星毛毛雨', 'Light rain shower' => '小阵雨',
        'Moderate or heavy rain shower' => '中到大阵雨', 'Torrential rain shower' => '暴雨',
        'Light sleet' => '小雨夹雪', 'Moderate or heavy sleet' => '中到大雨夹雪',
        'Patchy light snow' => '零星小雪', 'Patchy moderate snow' => '零星中雪',
        'Patchy heavy snow' => '零星大雪', 'Blowing snow' => '风吹雪',
        'Blizzard' => '暴风雪', 'Light freezing rain' => '小冻雨',
        'Moderate or heavy freezing rain' => '中到大冻雨',
        'Light rain with thunderstorm' => '小雷阵雨',
        'Moderate or heavy rain with thunder' => '中到大雷阵雨',
        'Patchy rain nearby' => '附近零星小雨',
        'Smoky haze' => '烟霾',
        'Haze' => '霾',
        'Smoke' => '烟雾',
        'Dust' => '沙尘',
        'Widespread dust' => '大范围沙尘',
        'Sand' => '扬沙',
        'Volcanic ash' => '火山灰',
        'Squalls' => '飑',
        'Tornado' => '龙卷风',
        'Patchy snow possible' => '可能有零星小雪',
        'Patchy sleet possible' => '可能有零星雨夹雪',
        'Patchy freezing drizzle possible' => '可能有零星冻毛毛雨',
        'Thundery outbreaks possible' => '可能有雷暴',
        'Blowing spray' => '飞沫',
        'Ice pellets' => '冰粒',
        'Light showers of ice pellets' => '小冰粒阵雨',
        'Moderate or heavy showers of ice pellets' => '中到大冰粒阵雨',
        'Patchy light rain with thunder' => '零星小雷阵雨',
        'Moderate or heavy rain with thunder' => '中到大雷阵雨',
        'Patchy light snow with thunder' => '零星小雪伴雷暴',
        'Moderate or heavy snow with thunder' => '中到大雪伴雷暴',
    ];

    public function getWeatherByCity($cityName) {
        $result = $this->fetchFromWttr($cityName);
        if ($result['success']) {
            return $result;
        }

        $result = $this->fetchFromOpenMeteo($cityName);
        if ($result['success']) {
            return $result;
        }

        return ['success' => false, 'message' => '所有天气数据源均不可用'];
    }

    public function getWeatherByCoords($lat, $lon) {
        return $this->fetchFromOpenMeteoByCoords($lat, $lon);
    }

    public function getForecast($cityName, $days = 7) {
        $result = $this->fetchForecastFromWttr($cityName);
        if ($result['success']) {
            return $result;
        }

        $result = $this->fetchForecastFromOpenMeteo($cityName, $days);
        if ($result['success']) {
            return $result;
        }

        return ['success' => false, 'message' => '所有天气预报数据源均不可用'];
    }

    public function getCurrentAndForecast($cityName) {
        $wttrResult = $this->fetchFromWttrAll($cityName);
        if ($wttrResult['success']) {
            return $wttrResult;
        }

        $openMeteoResult = $this->fetchFromOpenMeteo($cityName);
        if ($openMeteoResult['success']) {
            $forecastResult = $this->fetchForecastFromOpenMeteo($cityName, 7);
            return [
                'success' => true,
                'current' => $openMeteoResult,
                'forecast' => $forecastResult['success'] ? $forecastResult : null,
                'source' => 'open-meteo'
            ];
        }

        return ['success' => false, 'message' => '所有天气数据源均不可用'];
    }

    private function fetchFromWttrAll($cityName) {
        $url = 'https://wttr.in/' . urlencode($cityName) . '?format=j1&lang=zh';

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 8,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
            CURLOPT_HTTPHEADER => ['Accept: application/json']
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($response === false || $httpCode !== 200) {
            return ['success' => false, 'message' => 'wttr.in请求失败'];
        }

        $data = json_decode($response, true);
        if (!$data || !isset($data['current_condition'])) {
            return ['success' => false, 'message' => 'wttr.in数据解析失败'];
        }

        $current = $data['current_condition'][0];

        $currentResult = [
            'success' => true,
            'city' => $cityName,
            'temperature' => intval($current['temp_C']),
            'feels_like' => intval($current['FeelsLikeC']),
            'condition' => $this->translateWttrDesc($current['lang_zh'][0]['value'] ?? $current['weatherDesc'][0]['value'] ?? 'N/A'),
            'icon' => $this->getWttrIcon(intval($current['weatherCode'])),
            'wind' => $this->translateWindDir($current['winddir16Point'] ?? '') . ' ' . $this->beaufortScale(intval($current['windspeedKmph'])),
            'humidity' => intval($current['humidity']),
            'visibility' => intval($current['visibility']) . 'km',
            'pressure' => intval($current['pressure']) . 'hPa',
            'last_update' => date('Y-m-d H:i:s'),
            'source' => 'wttr.in'
        ];

        $forecastResult = null;
        if (isset($data['weather'])) {
            $forecasts = [];
            foreach ($data['weather'] as $day) {
                $forecasts[] = [
                    'date' => $day['date'],
                    'high' => intval($day['maxtempC']),
                    'low' => intval($day['mintempC']),
                    'condition' => $this->translateWttrDesc($day['hourly'][4]['lang_zh'][0]['value'] ?? $day['hourly'][4]['weatherDesc'][0]['value'] ?? 'N/A'),
                    'icon' => $this->getWttrIcon(intval($day['hourly'][4]['weatherCode'] ?? 0)),
                    'wind' => $this->translateWindDir($day['hourly'][4]['winddir16Point'] ?? '') . ' ' . $this->beaufortScale(intval($day['hourly'][4]['windspeedKmph'] ?? 0)),
                    'humidity' => intval($day['hourly'][4]['humidity'] ?? 0),
                ];
            }
            $forecastResult = [
                'success' => true,
                'city' => $cityName,
                'forecasts' => $forecasts,
                'source' => 'wttr.in'
            ];
        }

        return [
            'success' => true,
            'current' => $currentResult,
            'forecast' => $forecastResult,
            'source' => 'wttr.in'
        ];
    }

    private function fetchFromWttr($cityName) {
        $url = 'https://wttr.in/' . urlencode($cityName) . '?format=j1&lang=zh';

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 8,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
            CURLOPT_HTTPHEADER => ['Accept: application/json']
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($response === false || $httpCode !== 200) {
            return ['success' => false, 'message' => 'wttr.in请求失败'];
        }

        $data = json_decode($response, true);
        if (!$data || !isset($data['current_condition'])) {
            return ['success' => false, 'message' => 'wttr.in数据解析失败'];
        }

        $current = $data['current_condition'][0];
        $area = isset($data['nearest_area'][0]) ? $data['nearest_area'][0] : [];

        return [
            'success' => true,
            'city' => $cityName,
            'temperature' => intval($current['temp_C']),
            'feels_like' => intval($current['FeelsLikeC']),
            'condition' => $this->translateWttrDesc($current['lang_zh'][0]['value'] ?? $current['weatherDesc'][0]['value'] ?? 'N/A'),
            'icon' => $this->getWttrIcon(intval($current['weatherCode'])),
            'wind' => $this->translateWindDir($current['winddir16Point'] ?? '') . ' ' . $this->beaufortScale(intval($current['windspeedKmph'])),
            'humidity' => intval($current['humidity']),
            'visibility' => intval($current['visibility']) . 'km',
            'pressure' => intval($current['pressure']) . 'hPa',
            'last_update' => date('Y-m-d H:i:s'),
            'source' => 'wttr.in'
        ];
    }

    private function fetchForecastFromWttr($cityName) {
        $url = 'https://wttr.in/' . urlencode($cityName) . '?format=j1&lang=zh';

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 8,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
            CURLOPT_HTTPHEADER => ['Accept: application/json']
        ]);

        $response = curl_exec($ch);
        curl_close($ch);

        if ($response === false) {
            return ['success' => false];
        }

        $data = json_decode($response, true);
        if (!$data || !isset($data['weather'])) {
            return ['success' => false];
        }

        $forecasts = [];
        foreach ($data['weather'] as $day) {
            $forecasts[] = [
                'date' => $day['date'],
                'high' => intval($day['maxtempC']),
                'low' => intval($day['mintempC']),
                'condition' => $this->translateWttrDesc($day['hourly'][4]['lang_zh'][0]['value'] ?? $day['hourly'][4]['weatherDesc'][0]['value'] ?? 'N/A'),
                'icon' => $this->getWttrIcon(intval($day['hourly'][4]['weatherCode'] ?? 0)),
                'wind' => $this->translateWindDir($day['hourly'][4]['winddir16Point'] ?? '') . ' ' . $this->beaufortScale(intval($day['hourly'][4]['windspeedKmph'] ?? 0)),
                'humidity' => intval($day['hourly'][4]['humidity'] ?? 0),
            ];
        }

        return [
            'success' => true,
            'city' => $cityName,
            'forecasts' => $forecasts,
            'source' => 'wttr.in'
        ];
    }

    private function fetchFromOpenMeteo($cityName) {
        $coords = $this->getCityCoords($cityName);
        if (!$coords) {
            return ['success' => false, 'message' => '未找到城市坐标: ' . $cityName];
        }
        return $this->fetchFromOpenMeteoByCoords($coords['lat'], $coords['lon'], $cityName);
    }

    private function fetchFromOpenMeteoByCoords($lat, $lon, $cityName = '') {
        $params = http_build_query([
            'latitude' => $lat,
            'longitude' => $lon,
            'current' => 'temperature_2m,relative_humidity_2m,apparent_temperature,weather_code,wind_speed_10m,wind_direction_10m,surface_pressure',
            'timezone' => 'Asia/Shanghai'
        ]);
        $url = 'https://api.open-meteo.com/v1/forecast?' . $params;

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 8,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($response === false || $httpCode !== 200) {
            return ['success' => false, 'message' => 'Open-Meteo请求失败'];
        }

        $data = json_decode($response, true);
        if (!$data || !isset($data['current'])) {
            return ['success' => false, 'message' => 'Open-Meteo数据解析失败'];
        }

        $current = $data['current'];
        $weatherCode = intval($current['weather_code'] ?? 0);

        return [
            'success' => true,
            'city' => $cityName ?: ($lat . ',' . $lon),
            'temperature' => round($current['temperature_2m']),
            'feels_like' => round($current['apparent_temperature']),
            'condition' => self::$weatherCodeMap[$weatherCode] ?? '未知',
            'icon' => self::$weatherIconMap[$weatherCode] ?? '🌈',
            'wind' => $this->windDirectionDeg($current['wind_direction_10m'] ?? 0) . ' ' . $this->beaufortScaleFromMs(floatval($current['wind_speed_10m'] ?? 0)),
            'humidity' => intval($current['relative_humidity_2m'] ?? 0),
            'visibility' => 'N/A',
            'pressure' => round($current['surface_pressure'] ?? 0) . 'hPa',
            'last_update' => date('Y-m-d H:i:s'),
            'source' => 'open-meteo'
        ];
    }

    private function fetchForecastFromOpenMeteo($cityName, $days = 7) {
        $coords = $this->getCityCoords($cityName);
        if (!$coords) {
            return ['success' => false, 'message' => '未找到城市坐标: ' . $cityName];
        }

        $params = http_build_query([
            'latitude' => $coords['lat'],
            'longitude' => $coords['lon'],
            'daily' => 'weather_code,temperature_2m_max,temperature_2m_min,wind_speed_10m_max,wind_direction_10m_dominant',
            'timezone' => 'Asia/Shanghai',
            'forecast_days' => min($days, 16)
        ]);
        $url = 'https://api.open-meteo.com/v1/forecast?' . $params;

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 8,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
        ]);

        $response = curl_exec($ch);
        curl_close($ch);

        if ($response === false) {
            return ['success' => false];
        }

        $data = json_decode($response, true);
        if (!$data || !isset($data['daily'])) {
            return ['success' => false];
        }

        $forecasts = [];
        $daily = $data['daily'];
        $count = count($daily['time'] ?? []);
        for ($i = 0; $i < $count; $i++) {
            $weatherCode = intval($daily['weather_code'][$i] ?? 0);
            $forecasts[] = [
                'date' => $daily['time'][$i],
                'high' => round($daily['temperature_2m_max'][$i] ?? 0),
                'low' => round($daily['temperature_2m_min'][$i] ?? 0),
                'condition' => self::$weatherCodeMap[$weatherCode] ?? '未知',
                'icon' => self::$weatherIconMap[$weatherCode] ?? '🌈',
                'wind' => $this->windDirectionDeg($daily['wind_direction_10m_dominant'][$i] ?? 0) . ' ' . $this->beaufortScaleFromMs(floatval($daily['wind_speed_10m_max'][$i] ?? 0)),
                'humidity' => 0,
            ];
        }

        return [
            'success' => true,
            'city' => $cityName,
            'forecasts' => $forecasts,
            'source' => 'open-meteo'
        ];
    }

    private function getCityCoords($cityName) {
        $cityName = trim($cityName);
        if (isset(self::$cityCoords[$cityName])) {
            return self::$cityCoords[$cityName];
        }

        foreach (self::$cityCoords as $name => $coords) {
            if (strpos($name, $cityName) !== false || strpos($cityName, $name) !== false) {
                return $coords;
            }
        }

        return null;
    }

    private function getWttrIcon($code) {
        $map = [
            113 => '☀️', 116 => '⛅', 119 => '☁️', 122 => '☁️',
            143 => '🌫', 176 => '🌦', 179 => '🌨', 182 => '🌨',
            185 => '🌧', 200 => '⛈', 227 => '🌨', 230 => '❄️',
            248 => '🌫', 260 => '🌫', 263 => '🌦', 266 => '🌧',
            281 => '🌧', 284 => '🌧', 293 => '🌦', 296 => '🌧',
            299 => '🌧', 302 => '🌧', 305 => '🌧', 308 => '🌧',
            311 => '🌧', 314 => '🌧', 317 => '🌨', 320 => '❄️',
            323 => '🌨', 326 => '🌨', 329 => '❄️', 332 => '❄️',
            335 => '❄️', 338 => '❄️',
            350 => '🌧', 353 => '🌦', 356 => '🌧', 359 => '🌧',
            362 => '🌨', 365 => '🌨', 368 => '🌨', 371 => '❄️',
            374 => '🌨', 377 => '🌨',
            386 => '⛈', 389 => '⛈', 392 => '⛈', 395 => '❄️'
        ];
        return $map[$code] ?? '🌈';
    }

    private function beaufortScale($kmh) {
        if ($kmh < 1) return '0级';
        if ($kmh < 6) return '1级';
        if ($kmh < 12) return '2级';
        if ($kmh < 20) return '3级';
        if ($kmh < 29) return '4级';
        if ($kmh < 39) return '5级';
        if ($kmh < 50) return '6级';
        if ($kmh < 62) return '7级';
        if ($kmh < 75) return '8级';
        if ($kmh < 89) return '9级';
        if ($kmh < 103) return '10级';
        if ($kmh < 117) return '11级';
        return '12级';
    }

    private function beaufortScaleFromMs($ms) {
        return $this->beaufortScale($ms * 3.6);
    }

    private function translateWindDir($dir) {
        return self::$windDirMap[$dir] ?? $dir;
    }

    private function translateWttrDesc($desc) {
        if (isset(self::$wttrDescMap[$desc])) {
            return self::$wttrDescMap[$desc];
        }
        if (preg_match('/[\x{4e00}-\x{9fff}]/u', $desc)) {
            return $desc;
        }
        foreach (self::$wttrDescMap as $en => $zh) {
            if (stripos($desc, $en) !== false) {
                return $zh;
            }
        }
        return $desc;
    }

    private function windDirectionDeg($deg) {
        $dirs = ['北风', '东北风', '东风', '东南风', '南风', '西南风', '西风', '西北风'];
        $index = round($deg / 45) % 8;
        return $dirs[$index];
    }
}

/**
 * 票务API服务类
 */
class TicketApiService {
    private $trainBaseUrl = 'https://kyfw.12306.cn/otn';
    private $flightBaseUrl = 'https://flights.ctrip.com';
    private static $stationCodeCache = null;
    private static $stationCodeCacheFile = '';

    private static $cityStationCode = [
        '阳泉' => 'AQP', '北京' => 'BJP', '上海' => 'SHH', '广州' => 'GZQ',
        '深圳' => 'SZQ', '杭州' => 'HZQ', '成都' => 'CDW', '重庆' => 'CQW',
        '西安' => 'XAQ', '南京' => 'NJN', '武汉' => 'WHN', '昆明' => 'KMM',
        '丽江' => 'LJI', '天津' => 'TJP', '苏州' => 'SZH', '郑州' => 'ZZF',
        '长沙' => 'CSQ', '青岛' => 'QDK', '大连' => 'DLD', '厦门' => 'XMN',
        '贵阳' => 'GYM', '南宁' => 'NNZ', '哈尔滨' => 'HBB', '沈阳' => 'SYT',
        '济南' => 'JNK', '合肥' => 'HFH', '太原' => 'TYV', '桂林' => 'GLZ',
        '福州' => 'FZS', '拉萨' => 'LSO', '乌鲁木齐' => 'WAR', '兰州' => 'LZJ',
        '西宁' => 'XNO', '银川' => 'YCJ', '呼和浩特' => 'HHC', '长春' => 'CCT',
        '南昌' => 'NXG', '海口' => 'HKA', '三亚' => 'SEQ', '温州' => 'RZH',
        '宁波' => 'NGH', '无锡' => 'WXH', '石家庄' => 'SJP', '晋城' => 'JCF',
        '佛山' => 'FSQ', '东莞' => 'RTQ', '珠海' => 'ZHQ', '烟台' => 'YTK',
        '威海' => 'WKK',
    ];

    private static $seatTypeMap = [
        'A9' => '商务座', 'P' => '特等座', 'M' => '一等座', 'O' => '二等座',
        'A3' => '硬卧', 'A4' => '软卧', 'A1' => '硬座', 'A6' => '高级软卧',
        'WZ' => '无座',
    ];

    private static $trainTypeMap = [
        'G' => '高铁', 'D' => '动车', 'C' => '城际', 'Z' => '直达特快',
        'T' => '特快', 'K' => '快速', 'L' => '临时', 'Y' => '旅游',
    ];

    public function queryTrainTickets($from, $to, $date) {
        $result = $this->query12306Tickets($from, $to, $date);
        if ($result['success']) {
            return $result;
        }
        return $this->generateMockTrainData($from, $to, $date);
    }

    public function queryFlights($from, $to, $date) {
        return $this->generateMockFlightData($from, $to, $date);
    }

    private function query12306Tickets($from, $to, $date) {
        $fromCode = $this->getStationCode($from);
        $toCode = $this->getStationCode($to);

        if (!$fromCode || !$toCode) {
            return ['success' => false, 'message' => '无法识别城市: ' . (!$fromCode ? $from : $to)];
        }

        $cookieFile = sys_get_temp_dir() . '/12306_cookie_' . md5($fromCode . $toCode . $date) . '.txt';

        $dynamicEndpoint = $this->fetch12306Init($cookieFile);
        if (!$dynamicEndpoint) {
            return ['success' => false, 'message' => '无法获取12306会话'];
        }

        $params = http_build_query([
            'leftTicketDTO.train_date' => $date,
            'leftTicketDTO.from_station' => $fromCode,
            'leftTicketDTO.to_station' => $toCode,
            'purpose_codes' => 'ADULT',
        ]);

        $url = 'https://kyfw.12306.cn/otn/' . $dynamicEndpoint . '?' . $params;

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            CURLOPT_HTTPHEADER => [
                'Accept: application/json, text/javascript, */*; q=0.01',
                'X-Requested-With: XMLHttpRequest',
                'Referer: https://kyfw.12306.cn/otn/leftTicket/init',
            ],
            CURLOPT_COOKIEJAR => $cookieFile,
            CURLOPT_COOKIEFILE => $cookieFile,
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($response && $httpCode === 302) {
            $redirectData = json_decode($response, true);
            if (isset($redirectData['c_url'])) {
                $retryUrl = 'https://kyfw.12306.cn/otn/' . $redirectData['c_url'] . '?' . $params;
                $ch = curl_init($retryUrl);
                curl_setopt_array($ch, [
                    CURLOPT_RETURNTRANSFER => true,
                    CURLOPT_TIMEOUT => 10,
                    CURLOPT_SSL_VERIFYPEER => false,
                    CURLOPT_SSL_VERIFYHOST => false,
                    CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
                    CURLOPT_HTTPHEADER => [
                        'Accept: application/json, text/javascript, */*; q=0.01',
                        'X-Requested-With: XMLHttpRequest',
                        'Referer: https://kyfw.12306.cn/otn/leftTicket/init',
                    ],
                    CURLOPT_COOKIEJAR => $cookieFile,
                    CURLOPT_COOKIEFILE => $cookieFile,
                ]);
                $response = curl_exec($ch);
                $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                curl_close($ch);
            }
        }

        if (file_exists($cookieFile)) {
            @unlink($cookieFile);
        }

        if ($response === false || $httpCode !== 200) {
            return ['success' => false, 'message' => '12306接口请求失败(HTTP' . $httpCode . ')'];
        }

        $data = json_decode($response, true);
        if (!$data || !isset($data['data']['result'])) {
            return ['success' => false, 'message' => '12306返回数据异常'];
        }

        $stationMap = $data['data']['map'] ?? [];
        $trains = $this->parse12306Result($data['data']['result'], $stationMap, $from, $to);

        return [
            'success' => true,
            'from' => $from,
            'to' => $to,
            'date' => $date,
            'count' => count($trains),
            'data' => $trains,
            'source' => '12306',
            'note' => '数据来源于12306，仅供参考，请以官方为准'
        ];
    }

    private function fetch12306Init(&$cookieFile) {
        $ch = curl_init('https://kyfw.12306.cn/otn/leftTicket/init');
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            CURLOPT_COOKIEJAR => $cookieFile,
            CURLOPT_COOKIEFILE => $cookieFile,
            CURLOPT_FOLLOWLOCATION => true,
        ]);

        $response = curl_exec($ch);
        curl_close($ch);

        if ($response === false) {
            return null;
        }

        if (preg_match('/CLeftTicketUrl\s*=\s*[\'"]([^\'"]+)[\'"]/', $response, $m)) {
            return $m[1];
        }

        return 'leftTicket/queryZ';
    }

    private function parse12306Result($results, $stationMap, $fromCity, $toCity) {
        $trains = [];
        foreach ($results as $item) {
            $fields = explode('|', $item);
            if (count($fields) < 36) {
                continue;
            }

            $trainCode = $fields[3] ?? '';
            $fromStationCode = $fields[6] ?? '';
            $toStationCode = $fields[7] ?? '';
            $fromStationName = $stationMap[$fromStationCode] ?? $fromCity;
            $toStationName = $stationMap[$toStationCode] ?? $toCity;
            $departTime = $fields[8] ?? '';
            $arriveTime = $fields[9] ?? '';
            $duration = $fields[10] ?? '';
            $trainNo = $fields[2] ?? '';

            $typeCode = strtoupper(substr($trainCode, 0, 1));
            $typeName = self::$trainTypeMap[$typeCode] ?? '其他';

            $seats = [];
            $seatFields = [
                32 => 'A9', 31 => 'P', 30 => 'M', 29 => 'O',
                28 => 'A6', 27 => 'A4', 26 => 'A3', 25 => 'A1',
                33 => 'WZ',
            ];
            $priceRef = [
                'A9' => '商务座', 'P' => '特等座', 'M' => '一等座', 'O' => '二等座',
                'A6' => '高级软卧', 'A4' => '软卧', 'A3' => '硬卧', 'A1' => '硬座',
                'WZ' => '无座',
            ];

            foreach ($seatFields as $idx => $code) {
                $val = $fields[$idx] ?? '';
                $seatName = $priceRef[$code] ?? $code;
                if ($val !== '' && $val !== '*' && $val !== '无') {
                    if (is_numeric($val)) {
                        $seats[$seatName] = intval($val) > 0 ? intval($val) . '张' : '有票';
                    } elseif ($val === '有') {
                        $seats[$seatName] = '有票';
                    } else {
                        $seats[$seatName] = $val;
                    }
                }
            }

            $durationMinutes = 0;
            if (strpos($duration, ':') !== false) {
                $parts = explode(':', $duration);
                $durationMinutes = intval($parts[0]) * 60 + intval($parts[1]);
            }

            $trains[] = [
                'train_no' => $trainCode,
                'train_real_no' => $trainNo,
                'from_station' => $fromStationName,
                'to_station' => $toStationName,
                'from_station_code' => $fromStationCode,
                'to_station_code' => $toStationCode,
                'depart_time' => $departTime,
                'arrive_time' => $arriveTime,
                'duration_minutes' => $durationMinutes,
                'duration' => $this->formatDuration($durationMinutes),
                'seats' => $seats,
                'type' => $typeCode,
                'type_name' => $typeName,
                'transit' => 0,
                'transit_info' => '',
            ];
        }

        usort($trains, function($a, $b) {
            return strcmp($a['depart_time'], $b['depart_time']);
        });

        return $trains;
    }

    private function getStationCode($cityName) {
        $cityName = trim($cityName);
        if (isset(self::$cityStationCode[$cityName])) {
            return self::$cityStationCode[$cityName];
        }

        foreach (self::$cityStationCode as $name => $code) {
            if (strpos($name, $cityName) !== false || strpos($cityName, $name) !== false) {
                return $code;
            }
        }

        $remoteCode = $this->fetchStationCodeFrom12306($cityName);
        if ($remoteCode) {
            return $remoteCode;
        }

        return null;
    }

    private function fetchStationCodeFrom12306($cityName) {
        if (self::$stationCodeCache === null) {
            self::$stationCodeCacheFile = sys_get_temp_dir() . '/12306_stations.json';
            if (file_exists(self::$stationCodeCacheFile) && (time() - filemtime(self::$stationCodeCacheFile)) < 86400) {
                $cached = file_get_contents(self::$stationCodeCacheFile);
                self::$stationCodeCache = json_decode($cached, true) ?: [];
            } else {
                self::$stationCodeCache = $this->fetchAndParseStationNames();
            }
        }

        return self::$stationCodeCache[$cityName] ?? null;
    }

    private function fetchAndParseStationNames() {
        $url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js';
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        ]);

        $response = curl_exec($ch);
        curl_close($ch);

        if ($response === false) {
            return [];
        }

        $stations = [];
        if (preg_match("/var station_names\s*=\s*'(.+)';/", $response, $matches)) {
            $entries = explode('@', $matches[1]);
            foreach ($entries as $entry) {
                $parts = explode('|', $entry);
                if (count($parts) >= 3) {
                    $stations[$parts[1]] = $parts[2];
                }
            }
        }

        if (!empty($stations) && self::$stationCodeCacheFile) {
            file_put_contents(self::$stationCodeCacheFile, json_encode($stations, JSON_UNESCAPED_UNICODE));
        }

        return $stations;
    }

    public function getStationInfo($cityName) {
        $stations = [
            '北京' => ['name' => '北京', 'code' => 'BJP', 'stations' => ['北京西站', '北京南站', '北京站', '北京北站', '北京朝阳站']],
            '上海' => ['name' => '上海', 'code' => 'SHH', 'stations' => ['上海虹桥站', '上海站', '上海南站', '上海西站']],
            '广州' => ['name' => '广州', 'code' => 'GZQ', 'stations' => ['广州南站', '广州站', '广州东站', '广州北站']],
            '深圳' => ['name' => '深圳', 'code' => 'SZQ', 'stations' => ['深圳北站', '深圳站', '深圳东站', '福田站']],
            '杭州' => ['name' => '杭州', 'code' => 'HZQ', 'stations' => ['杭州东站', '杭州站', '杭州南站', '杭州西站']],
            '成都' => ['name' => '成都', 'code' => 'CDW', 'stations' => ['成都东站', '成都站', '成都南站', '成都西站']],
            '重庆' => ['name' => '重庆', 'code' => 'CQW', 'stations' => ['重庆北站', '重庆西站', '重庆站', '沙坪坝站']],
            '西安' => ['name' => '西安', 'code' => 'XAQ', 'stations' => ['西安北站', '西安站', '西安南站']],
            '南京' => ['name' => '南京', 'code' => 'NJN', 'stations' => ['南京南站', '南京站']],
            '武汉' => ['name' => '武汉', 'code' => 'WHN', 'stations' => ['武汉站', '汉口站', '武昌站', '武汉东站']],
            '昆明' => ['name' => '昆明', 'code' => 'KMM', 'stations' => ['昆明南站', '昆明站']],
            '丽江' => ['name' => '丽江', 'code' => 'LJI', 'stations' => ['丽江站']],
            '天津' => ['name' => '天津', 'code' => 'TJP', 'stations' => ['天津站', '天津西站', '天津南站']],
            '苏州' => ['name' => '苏州', 'code' => 'SZH', 'stations' => ['苏州站', '苏州北站', '苏州园区站']],
            '郑州' => ['name' => '郑州', 'code' => 'ZZF', 'stations' => ['郑州东站', '郑州站', '郑州西站']],
            '长沙' => ['name' => '长沙', 'code' => 'CSQ', 'stations' => ['长沙南站', '长沙站']],
            '青岛' => ['name' => '青岛', 'code' => 'QDK', 'stations' => ['青岛站', '青岛北站', '青岛西站']],
            '大连' => ['name' => '大连', 'code' => 'DLD', 'stations' => ['大连站', '大连北站']],
            '厦门' => ['name' => '厦门', 'code' => 'XMN', 'stations' => ['厦门站', '厦门北站']],
            '贵阳' => ['name' => '贵阳', 'code' => 'GYM', 'stations' => ['贵阳北站', '贵阳站', '贵阳东站']],
            '南宁' => ['name' => '南宁', 'code' => 'NNZ', 'stations' => ['南宁站', '南宁东站']],
            '哈尔滨' => ['name' => '哈尔滨', 'code' => 'HBB', 'stations' => ['哈尔滨站', '哈尔滨西站', '哈尔滨北站']],
            '沈阳' => ['name' => '沈阳', 'code' => 'SYT', 'stations' => ['沈阳站', '沈阳北站', '沈阳南站']],
            '济南' => ['name' => '济南', 'code' => 'JNK', 'stations' => ['济南站', '济南西站', '济南东站']],
            '合肥' => ['name' => '合肥', 'code' => 'HFH', 'stations' => ['合肥站', '合肥南站']],
            '太原' => ['name' => '太原', 'code' => 'TYV', 'stations' => ['太原站', '太原南站']],
            '桂林' => ['name' => '桂林', 'code' => 'GLZ', 'stations' => ['桂林站', '桂林北站', '桂林西站']],
            '阳泉' => ['name' => '阳泉', 'code' => 'AQP', 'stations' => ['阳泉北站', '阳泉东站']],
            '石家庄' => ['name' => '石家庄', 'code' => 'SJP', 'stations' => ['石家庄站', '石家庄北站']],
            '晋城' => ['name' => '晋城', 'code' => 'JCF', 'stations' => ['晋城站', '晋城东站']],
        ];
        
        $cityName = trim($cityName);
        
        if (isset($stations[$cityName])) {
            return ['success' => true, 'data' => $stations[$cityName]];
        }
        
        $cityNameUpper = mb_convert_case($cityName, MB_CASE_UPPER, 'UTF-8');
        foreach ($stations as $key => $info) {
            $keyUpper = mb_convert_case($key, MB_CASE_UPPER, 'UTF-8');
            if (strpos($keyUpper, $cityNameUpper) !== false || strpos($cityNameUpper, $keyUpper) !== false) {
                return ['success' => true, 'data' => $info];
            }
        }
        
        return ['success' => false, 'message' => '未找到相关车站信息'];
    }
    
    /**
     * 获取热门城市列表
     */
    public function getHotCities() {
        return [
            'success' => true,
            'data' => [
                ['name' => '北京', 'code' => 'BJP', 'icon' => '🏛️'],
                ['name' => '上海', 'code' => 'SHH', 'icon' => '🏙️'],
                ['name' => '广州', 'code' => 'GZQ', 'icon' => '🌆'],
                ['name' => '深圳', 'code' => 'SZQ', 'icon' => '🌃'],
                ['name' => '杭州', 'code' => 'HZQ', 'icon' => '🏯'],
                ['name' => '成都', 'code' => 'CDW', 'icon' => '🕌'],
                ['name' => '重庆', 'code' => 'CQW', 'icon' => '🌉'],
                ['name' => '武汉', 'code' => 'WHN', 'icon' => '🏯'],
                ['name' => '西安', 'code' => 'XAQ', 'icon' => '🗼'],
                ['name' => '南京', 'code' => 'NJN', 'icon' => '🏯'],
                ['name' => '天津', 'code' => 'TJP', 'icon' => '🌆'],
                ['name' => '苏州', 'code' => 'SZH', 'icon' => '🏮'],
                ['name' => '郑州', 'code' => 'ZZF', 'icon' => '🏯'],
                ['name' => '长沙', 'code' => 'CSQ', 'icon' => '🏯'],
                ['name' => '青岛', 'code' => 'QDK', 'icon' => '🌊'],
                ['name' => '大连', 'code' => 'DLD', 'icon' => '⛵'],
                ['name' => '厦门', 'code' => 'XMN', 'icon' => '🏝️'],
                ['name' => '昆明', 'code' => 'KMM', 'icon' => '🕌'],
                ['name' => '贵阳', 'code' => 'GYM', 'icon' => '🏯'],
                ['name' => '南宁', 'code' => 'NNZ', 'icon' => '🌴'],
                ['name' => '丽江', 'code' => 'LJI', 'icon' => '❄️'],
                ['name' => '桂林', 'code' => 'GLZ', 'icon' => '🏞️'],
                ['name' => '阳泉', 'code' => 'AQP', 'icon' => '⛰️'],
                ['name' => '石家庄', 'code' => 'SJP', 'icon' => '🏛️'],
                ['name' => '太原', 'code' => 'TYV', 'icon' => '🏯'],
            ]
        ];
    }
    
    /**
     * 生成模拟火车票数据（增强版，含真实车次前缀、中转信息、多席别等）
     */
    private function generateMockTrainData($from, $to, $date) {
        $trains = [];
        
        // 根据城市距离估算运行时长（近似）
        $cityDistances = [
            '北京-上海' => 310, '北京-广州' => 480, '北京-成都' => 420, '北京-西安' => 270,
            '北京-武汉' => 240, '北京-杭州' => 270, '北京-昆明' => 520, '北京-深圳' => 510,
            '上海-广州' => 360, '上海-成都' => 480, '上海-西安' => 300, '上海-武汉' => 180,
            '上海-杭州' => 60, '上海-南京' => 80, '广州-深圳' => 30, '广州-昆明' => 300,
            '成都-重庆' => 45, '成都-昆明' => 240, '成都-西安' => 180, '西安-武汉' => 180,
            '昆明-丽江' => 120, '北京-天津' => 30, '北京-太原' => 100, '郑州-武汉' => 120,
            '长沙-广州' => 150, '南京-杭州' => 90, '济南-青岛' => 120, '沈阳-大连' => 120,
            '厦门-深圳' => 150, '贵阳-昆明' => 120, '南宁-昆明' => 200, '太原-西安' => 120,
            '太原-北京' => 100, '太原-郑州' => 120, '哈尔滨-沈阳' => 120
        ];
        
        $routeKey = "$from-$to";
        $routeKey2 = "$to-$from";
        $baseMinutes = isset($cityDistances[$routeKey]) ? $cityDistances[$routeKey] : 
                       (isset($cityDistances[$routeKey2]) ? $cityDistances[$routeKey2] : rand(60, 400));
        
        // 高铁车次前缀（按铁路局划分）
        $gPrefixes = [
            '北京' => ['G1', 'G2', 'G3', 'G4', 'G5', 'G6', 'G7'],
            '上海' => ['G1', 'G2', 'G3', 'G4', 'G13', 'G14', 'G15'],
            '广州' => ['G6', 'G7', 'G8', 'G61', 'G62', 'G63'],
            '武汉' => ['G5', 'G6', 'G7', 'G10', 'G11']
        ];
        
        // 高铁（G字头）- 6班
        for ($i = 1; $i <= 6; $i++) {
            $departHour = 6 + $i * 2;
            $duration = $baseMinutes + rand(-10, 30);
            $basePrice = max(30, round($baseMinutes * 0.45 + 30));
            
            $trains[] = [
                'train_no' => 'G' . rand(10, 99) . str_pad(rand(1, 99), 2, '0', STR_PAD_LEFT),
                'from_station' => $from,
                'to_station' => $to,
                'depart_time' => str_pad($departHour % 24, 2, '0', STR_PAD_LEFT) . ':' . str_pad(rand(0, 59), 2, '0', STR_PAD_LEFT),
                'arrive_time' => date('H:i', strtotime("today " . str_pad($departHour % 24, 2, '0', STR_PAD_LEFT) . ':00') + $duration * 60),
                'duration_minutes' => $duration,
                'duration' => $this->formatDuration($duration),
                'price_second' => $basePrice,
                'price_first' => round($basePrice * 1.6),
                'price_business' => round($basePrice * 2.8),
                'price_sleeper' => 0,
                'seats' => [
                    '二等座' => rand(0, 200) . '张',
                    '一等座' => rand(0, 50) . '张',
                    '商务座' => rand(0, 10) . '张'
                ],
                'type' => 'G',
                'type_name' => '高铁',
                'on_time_rate' => 90 + rand(0, 9),
                'transit' => 0,
                'transit_info' => '',
                'stations_count' => rand(2, 8)
            ];
        }
        
        // 动车（D字头）- 4班
        for ($i = 1; $i <= 4; $i++) {
            $departHour = 7 + $i * 3;
            $duration = $baseMinutes + rand(10, 60);
            $basePrice = max(25, round($baseMinutes * 0.35 + 20));
            
            $trains[] = [
                'train_no' => 'D' . rand(2, 9) . str_pad(rand(1, 99), 2, '0', STR_PAD_LEFT),
                'from_station' => $from,
                'to_station' => $to,
                'depart_time' => str_pad($departHour % 24, 2, '0', STR_PAD_LEFT) . ':' . str_pad(rand(0, 59), 2, '0', STR_PAD_LEFT),
                'arrive_time' => date('H:i', strtotime("today " . str_pad($departHour % 24, 2, '0', STR_PAD_LEFT) . ':00') + $duration * 60),
                'duration_minutes' => $duration,
                'duration' => $this->formatDuration($duration),
                'price_second' => $basePrice,
                'price_first' => round($basePrice * 1.5),
                'price_business' => 0,
                'price_sleeper' => round($basePrice * 2.0),
                'seats' => [
                    '二等座' => rand(0, 150) . '张',
                    '一等座' => rand(0, 30) . '张',
                    '卧铺' => rand(0, 20) . '张'
                ],
                'type' => 'D',
                'type_name' => '动车',
                'on_time_rate' => 88 + rand(0, 11),
                'transit' => 0,
                'transit_info' => '',
                'stations_count' => rand(3, 12)
            ];
        }
        
        // 直达特快/特快（Z/T字头）- 2班（仅长途路线）
        if ($baseMinutes > 120) {
            for ($i = 0; $i < 2; $i++) {
                $type = rand(0, 1) ? 'Z' : 'T';
                $departHour = rand(18, 22);
                $duration = $baseMinutes + rand(30, 90);
                $basePrice = max(40, round($baseMinutes * 0.18 + 15));
                
                $trains[] = [
                    'train_no' => $type . rand(10, 99),
                    'from_station' => $from,
                    'to_station' => $to,
                    'depart_time' => $departHour . ':' . str_pad(rand(0, 59), 2, '0', STR_PAD_LEFT),
                    'arrive_time' => date('H:i', strtotime("today $departHour:00") + $duration * 60),
                    'duration_minutes' => $duration,
                    'duration' => $this->formatDuration($duration),
                    'price_second' => 0,
                    'price_first' => 0,
                    'price_business' => 0,
                    'price_sleeper' => $basePrice,
                    'seats' => [
                        '硬座' => rand(0, 100) . '张',
                        '硬卧' => rand(0, 80) . '张',
                        '软卧' => rand(0, 20) . '张'
                    ],
                    'type' => $type,
                    'type_name' => $type === 'Z' ? '直达特快' : '特快',
                    'on_time_rate' => 80 + rand(0, 15),
                    'transit' => 0,
                    'transit_info' => '',
                    'stations_count' => rand(1, 5)
                ];
            }
        }
        
        // 按出发时间排序
        usort($trains, function($a, $b) {
            return strcmp($a['depart_time'], $b['depart_time']);
        });
        
        return [
            'success' => true,
            'from' => $from,
            'to' => $to,
            'date' => $date,
            'count' => count($trains),
            'data' => $trains,
            'source' => 'mock_data',
            'note' => '此为模拟数据，实际票务信息请以12306官方为准'
        ];
    }
    
    /**
     * 生成模拟机票数据（增强版，含真实航司、机场代码、中转航班等）
     */
    private function generateMockFlightData($from, $to, $date) {
        $flights = [];
        
        $airlines = [
            ['code' => 'MU', 'name' => '东方航空', 'color' => '#0066cc'],
            ['code' => 'CZ', 'name' => '南方航空', 'color' => '#ff6666'],
            ['code' => 'CA', 'name' => '中国国航', 'color' => '#cc0000'],
            ['code' => 'HU', 'name' => '海南航空', 'color' => '#ff9900'],
            ['code' => 'ZH', 'name' => '深圳航空', 'color' => '#009900'],
            ['code' => '3U', 'name' => '四川航空', 'color' => '#cc6600'],
            ['code' => 'MF', 'name' => '厦门航空', 'color' => '#006699'],
            ['code' => 'SC', 'name' => '山东航空', 'color' => '#3366cc']
        ];
        
        // 机场三字码
        $airports = [
            '北京' => ['PEK', 'PKX'], '上海' => ['PVG', 'SHA'], '广州' => ['CAN'],
            '深圳' => ['SZX'], '杭州' => ['HGH'], '成都' => ['CTU', 'TFU'],
            '重庆' => ['CKG'], '西安' => ['XIY'], '南京' => ['NKG'],
            '武汉' => ['WUH'], '昆明' => ['KMG'], '丽江' => ['LJG'],
            '天津' => ['TSN'], '长沙' => ['CSX'], '青岛' => ['TAO'],
            '大连' => ['DLC'], '厦门' => ['XMN'], '贵阳' => ['KWE'],
            '南宁' => ['NNG'], '哈尔滨' => ['HRB'], '沈阳' => ['SHE'],
            '济南' => ['TNA'], '合肥' => ['HFE'], '太原' => ['TYN'],
            '郑州' => ['CGO'], '苏州' => ['SHA'], '桂林' => ['KWL']
        ];
        
        $fromCode = isset($airports[$from]) ? $airports[$from][0] : strtoupper(substr($from, 0, 3));
        $toCode = isset($airports[$to]) ? $airports[$to][0] : strtoupper(substr($to, 0, 3));
        
        // 估算飞行时间
        $cityDistances = [
            '北京-上海' => 130, '北京-广州' => 195, '北京-成都' => 160, '北京-昆明' => 210,
            '上海-广州' => 150, '上海-成都' => 185, '广州-昆明' => 120, '成都-昆明' => 70,
            '北京-丽江' => 210, '昆明-丽江' => 60, '北京-西安' => 110, '深圳-北京' => 190
        ];
        $routeKey = "$from-$to";
        $routeKey2 = "$to-$from";
        $baseMinutes = isset($cityDistances[$routeKey]) ? $cityDistances[$routeKey] : 
                       (isset($cityDistances[$routeKey2]) ? $cityDistances[$routeKey2] : rand(90, 240));
        
        // 直飞航班 - 6班
        for ($i = 0; $i < 6; $i++) {
            $airline = $airlines[array_rand($airlines)];
            $departHour = 7 + $i * 2 + rand(0, 1);
            $duration = $baseMinutes + rand(-15, 25);
            $basePrice = max(300, round($baseMinutes * 5.5 + rand(200, 500)));
            
            $flights[] = [
                'flight_no' => $airline['code'] . str_pad(rand(100, 999), 3, '0', STR_PAD_LEFT) . (rand(0, 1) ? rand(0, 9) : ''),
                'airline' => $airline['name'],
                'airline_code' => $airline['code'],
                'airline_color' => $airline['color'],
                'from_city' => $from,
                'from_airport' => $fromCode,
                'to_city' => $to,
                'to_airport' => $toCode,
                'depart_time' => str_pad($departHour % 24, 2, '0', STR_PAD_LEFT) . ':' . str_pad(rand(0, 11) * 5, 2, '0', STR_PAD_LEFT),
                'arrive_time' => date('H:i', strtotime("today " . str_pad($departHour % 24, 2, '0', STR_PAD_LEFT) . ':00') + $duration * 60),
                'duration_minutes' => $duration,
                'duration' => $this->formatDuration($duration),
                'price_economy' => $basePrice,
                'price_business' => round($basePrice * 2.5),
                'price_first' => round($basePrice * 4.5),
                'discount' => rand(5, 35),
                'seats' => [
                    '经济舱' => rand(0, 120) . '座',
                    '商务舱' => rand(0, 16) . '座',
                    '头等舱' => rand(0, 6) . '座'
                ],
                'aircraft' => ['Boeing 737-800', 'Airbus A320', 'Airbus A330', 'Boeing 787'][rand(0, 3)],
                'on_time_rate' => 82 + rand(0, 16),
                'transit' => 0,
                'transit_info' => '',
                'meal' => rand(0, 1) ? '含餐' : '含轻食',
                'luggage' => '20kg免费托运'
            ];
        }
        
        // 中转航班 - 2班
        $transitCities = ['长沙', '武汉', '郑州', '西安', '贵阳', '重庆'];
        for ($i = 0; $i < 2; $i++) {
            $airline = $airlines[array_rand($airlines)];
            $transitCity = $transitCities[array_rand($transitCities)];
            while ($transitCity == $from || $transitCity == $to) {
                $transitCity = $transitCities[array_rand($transitCities)];
            }
            
            $departHour = 8 + $i * 4;
            $duration = $baseMinutes + rand(40, 90);
            $basePrice = max(250, round($baseMinutes * 3.5 + rand(100, 300)));
            
            $flights[] = [
                'flight_no' => $airline['code'] . str_pad(rand(100, 999), 3, '0', STR_PAD_LEFT),
                'airline' => $airline['name'],
                'airline_code' => $airline['code'],
                'airline_color' => $airline['color'],
                'from_city' => $from,
                'from_airport' => $fromCode,
                'to_city' => $to,
                'to_airport' => $toCode,
                'depart_time' => str_pad($departHour % 24, 2, '0', STR_PAD_LEFT) . ':' . str_pad(rand(0, 11) * 5, 2, '0', STR_PAD_LEFT),
                'arrive_time' => date('H:i', strtotime("today " . str_pad($departHour % 24, 2, '0', STR_PAD_LEFT) . ':00') + $duration * 60),
                'duration_minutes' => $duration,
                'duration' => $this->formatDuration($duration),
                'price_economy' => $basePrice,
                'price_business' => round($basePrice * 2.2),
                'price_first' => round($basePrice * 3.8),
                'discount' => rand(10, 40),
                'seats' => [
                    '经济舱' => rand(0, 100) . '座',
                    '商务舱' => rand(0, 10) . '座',
                    '头等舱' => '已售罄'
                ],
                'aircraft' => ['Airbus A320', 'Airbus A319'][rand(0, 1)],
                'on_time_rate' => 75 + rand(0, 18),
                'transit' => 1,
                'transit_info' => "经停{$transitCity}，停留约" . rand(45, 90) . "分钟",
                'meal' => '含简餐',
                'luggage' => '20kg免费托运'
            ];
        }
        
        // 按出发时间排序
        usort($flights, function($a, $b) {
            return strcmp($a['depart_time'], $b['depart_time']);
        });
        
        return [
            'success' => true,
            'from' => $from,
            'to' => $to,
            'date' => $date,
            'count' => count($flights),
            'data' => $flights,
            'source' => 'mock_data',
            'note' => '此为模拟数据，实际票务信息请以航空公司官方为准'
        ];
    }
    
    /**
     * 格式化时长
     */
    private function formatDuration($minutes) {
        $hours = floor($minutes / 60);
        $mins = $minutes % 60;
        return $hours . '小时' . ($mins > 0 ? $mins . '分' : '');
    }
}

/**
 * MCP工具桥接层
 * 用于与MCP（Model Context Protocol）工具服务进行交互
 * 整合12306票务查询、聚合数据老黄历等外部MCP服务
 */
class McpBridge {
    private static $mcpServerUrl = '';
    private static $cache = null;
    
    /**
     * 初始化MCP桥接
     */
    public static function init($serverUrl = '') {
        self::$mcpServerUrl = $serverUrl;
        if (self::$cache === null) {
            self::$cache = new ApiCache();
        }
    }
    
    /**
     * 调用MCP工具
     * @param string $serverName MCP服务器名称 (mcp_12306-mcp / mcp_juhe-mcp-server)
     * @param string $toolName 工具名称
     * @param array $args 工具参数
     * @return array 响应结果
     */
    public static function callTool($serverName, $toolName, $args = []) {
        $cacheKey = "mcp_{$serverName}_{$toolName}_" . md5(json_encode($args));
        
        // 尝试从缓存获取
        $cached = self::$cache ? self::$cache->get($cacheKey) : null;
        if ($cached !== null) {
            return $cached;
        }
        
        // 检查MCP服务可用性
        if (empty(self::$mcpServerUrl)) {
            return self::getFallbackData($serverName, $toolName, $args);
        }
        
        try {
            $client = new ApiClient(self::$mcpServerUrl);
            $response = $client->post('tools/call', [
                'server_name' => $serverName,
                'tool_name' => $toolName,
                'args' => $args
            ]);
            
            if ($response['success']) {
                // 缓存结果（票务数据缓存10分钟，老黄历缓存1天）
                $ttl = ($toolName === 'get_almanac') ? 86400 : 600;
                if (self::$cache) {
                    self::$cache->set($cacheKey, $response, $ttl);
                }
                return $response;
            }
            
            return self::getFallbackData($serverName, $toolName, $args);
        } catch (Exception $e) {
            return self::getFallbackData($serverName, $toolName, $args);
        }
    }
    
    /**
     * 获取降级数据（MCP服务不可用时使用）
     */
    private static function getFallbackData($serverName, $toolName, $args) {
        switch ($serverName) {
            case 'mcp_12306-mcp':
                return self::get12306Fallback($toolName, $args);
            case 'mcp_juhe-mcp-server':
                return self::getJuheFallback($toolName, $args);
            default:
                return ['success' => false, 'message' => 'MCP服务暂不可用，请稍后重试'];
        }
    }
    
    /**
     * 12306 MCP降级处理
     */
    private static function get12306Fallback($toolName, $args) {
        switch ($toolName) {
            case 'get-stations-code-in-city':
                $ticketService = new TicketApiService();
                return $ticketService->getStationInfo($args['city'] ?? '');
                
            case 'get-station-code-by-names':
                return [
                    'success' => true,
                    'data' => ['stations' => $args['stationNames'] ?? '']
                ];
                
            case 'get-tickets':
                $ticketService = new TicketApiService();
                return $ticketService->queryTrainTickets(
                    $args['fromStation'] ?? '',
                    $args['toStation'] ?? '',
                    $args['date'] ?? date('Y-m-d')
                );
                
            case 'get-current-date':
                return [
                    'success' => true,
                    'data' => [
                        'date' => date('Y-m-d'),
                        'weekday' => date('l'),
                        'timestamp' => time()
                    ]
                ];
                
            default:
                return ['success' => false, 'message' => "未知的12306工具: $toolName"];
        }
    }
    
    /**
     * 聚合数据MCP降级处理
     */
    private static function getJuheFallback($toolName, $args) {
        switch ($toolName) {
            case 'get_almanac':
                $date = $args['date'] ?? date('Y-m-d');
                return [
                    'success' => true,
                    'data' => [
                        'date' => $date,
                        'lunar' => self::getLunarDate($date),
                        'suit' => '祭祀,祈福,出行,结婚,订婚',
                        'avoid' => '开业,搬家,动土,安葬',
                        'note' => '此为本地模拟数据，仅供参考'
                    ]
                ];
            default:
                return ['success' => false, 'message' => "未知聚合数据工具: $toolName"];
        }
    }
    
    /**
     * 简易农历日期获取
     */
    private static function getLunarDate($date) {
        $lunarMonths = ['正月', '二月', '三月', '四月', '五月', '六月',
                        '七月', '八月', '九月', '十月', '冬月', '腊月'];
        $lunarDays = ['初一', '初二', '初三', '初四', '初五', '初六', '初七', '初八', '初九', '初十',
                      '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十',
                      '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十'];
        
        $ts = strtotime($date);
        $month = (int)date('n', $ts);
        $day = (int)date('j', $ts);
        
        // 简化农历计算（使用月日近似值）
        $lunarMonth = ($month + 1) % 12;
        if ($lunarMonth == 0) $lunarMonth = 12;
        $lunarDay = min($day, 30);
        
        return $lunarMonths[$lunarMonth - 1] . $lunarDays[$lunarDay - 1];
    }
    
    /**
     * 获取API服务状态
     */
    public static function getServiceStatus() {
        return [
            'mcp_12306' => [
                'name' => '12306票务服务',
                'status' => empty(self::$mcpServerUrl) ? 'mock' : 'online',
                'tools' => ['get-tickets', 'get-stations-code-in-city', 'get-station-code-by-names', 'get-current-date']
            ],
            'mcp_juhe' => [
                'name' => '聚合数据服务',
                'status' => empty(self::$mcpServerUrl) ? 'mock' : 'online',
                'tools' => ['get_almanac']
            ],
            'weather' => [
                'name' => '天气服务',
                'status' => 'online',
                'providers' => ['wttr.in', 'Open-Meteo'],
                'note' => '使用免费API，无需API Key，支持50+城市'
            ],
            'flight' => [
                'name' => '机票服务',
                'status' => 'mock',
                'note' => '使用内置模拟数据，支持8家航司、27个机场'
            ]
        ];
    }
}

/**
 * API缓存管理类
 */
class ApiCache {
    private $cacheDir = 'cache/';
    private $defaultTtl = 3600; // 1小时
    
    public function __construct() {
        if (!file_exists($this->cacheDir)) {
            mkdir($this->cacheDir, 0755, true);
        }
    }
    
    /**
     * 获取缓存
     */
    public function get($key) {
        $file = $this->getCacheFile($key);
        
        if (!file_exists($file)) {
            return null;
        }
        
        $data = file_get_contents($file);
        $data = json_decode($data, true);
        
        if ($data && $data['expire'] > time()) {
            return $data['value'];
        }
        
        // 删除过期缓存
        unlink($file);
        return null;
    }
    
    /**
     * 设置缓存
     */
    public function set($key, $value, $ttl = null) {
        $file = $this->getCacheFile($key);
        $ttl = $ttl ?? $this->defaultTtl;
        
        $data = [
            'value' => $value,
            'expire' => time() + $ttl,
            'created' => time()
        ];
        
        return file_put_contents($file, json_encode($data));
    }
    
    /**
     * 删除缓存
     */
    public function delete($key) {
        $file = $this->getCacheFile($key);
        if (file_exists($file)) {
            return unlink($file);
        }
        return false;
    }
    
    /**
     * 清理过期缓存
     */
    public function cleanExpired() {
        $files = glob($this->cacheDir . '*.cache');
        $count = 0;
        
        foreach ($files as $file) {
            $data = file_get_contents($file);
            $data = json_decode($data, true);
            
            if ($data && $data['expire'] <= time()) {
                unlink($file);
                $count++;
            }
        }
        
        return $count;
    }
    
    /**
     * 获取缓存文件名
     */
    private function getCacheFile($key) {
        return $this->cacheDir . md5($key) . '.cache';
    }
}
