"""Wind Linker 客户端"""

import os
import json
import requests
import re
from typing import Any, List, Optional
from datetime import datetime, date
from .config import get_api_url

try:
    import pandas as pd
    HAS_PANDAS = True
except ImportError:
    HAS_PANDAS = False


def deserialize_wind_object(obj):
    """反序列化 Wind 对象 - 智能识别并还原日期/时间类型

    支持的格式:
    - ISO 日期: "2025-01-02" -> datetime.date(2025, 1, 2)
    - ISO 时间: "2025-01-02T10:30:00" -> datetime.datetime(2025, 1, 2, 10, 30, 0)
    - Wind 时间: "20251030 11:30:22" -> datetime.datetime(2025, 10, 30, 11, 30, 22)
    """
    if obj is None:
        return None

    # 字符串 - 尝试解析为日期/时间
    if isinstance(obj, str):
        # 尝试解析 ISO 格式的日期时间: "2025-01-02T10:30:00"
        if 'T' in obj:
            try:
                return datetime.fromisoformat(obj)
            except:
                pass

        # 尝试解析 ISO 格式的日期: "2025-01-02"
        if re.match(r'^\d{4}-\d{2}-\d{2}$', obj):
            try:
                return date.fromisoformat(obj)
            except:
                pass

        # 尝试解析 Wind 格式的时间: "20251030 11:30:22"
        match = re.match(r'^(\d{8})\s+(\d{2}):(\d{2}):(\d{2})$', obj)
        if match:
            try:
                date_str = match.group(1)
                year = int(date_str[0:4])
                month = int(date_str[4:6])
                day = int(date_str[6:8])
                hour = int(match.group(2))
                minute = int(match.group(3))
                second = int(match.group(4))
                return datetime(year, month, day, hour, minute, second)
            except:
                pass

        # 无法解析，返回原字符串
        return obj

    # 列表/元组，递归处理
    if isinstance(obj, (list, tuple)):
        return [deserialize_wind_object(item) for item in obj]

    # 字典，递归处理
    if isinstance(obj, dict):
        return {k: deserialize_wind_object(v) for k, v in obj.items()}

    # 其他类型直接返回
    return obj


class WindData:
    """Wind 数据结果 - 完全对齐原始 Wind SDK"""

    def __init__(self, data: dict):
        # 兼容两种格式: 服务端返回的小写字段和标准的大写字段
        error_code = data.get("error_code", data.get("ErrorCode", -1))
        codes = data.get("codes", data.get("Codes", []))
        fields = data.get("fields", data.get("Fields", []))
        times = data.get("times", data.get("Times", []))
        data_values = data.get("data", data.get("Data", []))

        # 反序列化，还原日期对象
        self.ErrorCode = deserialize_wind_object(error_code)
        self.Codes = deserialize_wind_object(codes)
        self.Fields = deserialize_wind_object(fields)
        self.Times = deserialize_wind_object(times)
        self.Data = deserialize_wind_object(data_values)

        self.raw = data

    def to_df(self):
        """转换为 DataFrame"""
        if not HAS_PANDAS:
            raise ImportError("需要安装 pandas: pip install pandas")

        if not self.Data:
            return pd.DataFrame()

        # 构建 DataFrame
        df_data = {}

        # 添加字段数据
        for i, field in enumerate(self.Fields):
            if i < len(self.Data):
                df_data[field] = self.Data[i]

        # 创建 DataFrame
        df = pd.DataFrame(df_data)

        # 添加时间列
        if self.Times and len(self.Times) == len(df):
            df.insert(0, 'time', self.Times)

        # 添加代码列
        if self.Codes:
            if len(self.Codes) == 1:
                # 单个股票代码,重复填充
                df.insert(0, 'code', self.Codes[0])
            elif len(self.Codes) == len(df):
                # 多个股票代码,一一对应
                df.insert(0, 'code', self.Codes)

        return df

    def __repr__(self):
        """对齐原始 Wind SDK 的输出格式

        原始格式示例:
        .ErrorCode=0
        .Codes=[PG2512.CZC]
        .Fields=[RT_LAST,RT_LATEST,RT_HIGH,RT_LOW,RT_OPEN,RT_VOL,RT_AMT]
        .Times=[20251030 11:30:22]
        .Data=[[0.0],[0.0],[0.0],[0.0],[0.0],[0.0],[0.0]]
        """
        lines = []
        lines.append(f".ErrorCode={self.ErrorCode}")

        # Codes - 格式化为 [code1,code2]，不带引号
        if self.Codes:
            codes_str = ','.join(str(c) for c in self.Codes)
            lines.append(f".Codes=[{codes_str}]")

        # Fields - 格式化为 [field1,field2]，不带引号
        if self.Fields:
            fields_str = ','.join(str(f) for f in self.Fields)
            lines.append(f".Fields=[{fields_str}]")

        # Times - 格式化为 Wind 格式 (YYYYMMDD HH:MM:SS)
        if self.Times:
            formatted_times = []
            for t in self.Times:
                if isinstance(t, datetime):
                    # datetime.datetime -> "YYYYMMDD HH:MM:SS"
                    formatted_times.append(t.strftime("%Y%m%d %H:%M:%S"))
                elif isinstance(t, date):
                    # datetime.date -> "YYYY-MM-DD"
                    formatted_times.append(str(t))
                else:
                    # 其他类型直接转字符串
                    formatted_times.append(str(t))
            times_str = ','.join(formatted_times)
            lines.append(f".Times=[{times_str}]")

        # Data - 格式化为紧凑格式（无空格）
        if self.Data:
            # 使用 json.dumps 然后移除空格
            import json
            data_str = json.dumps(self.Data, separators=(',', ':'))
            # 但我们需要的是 Python 列表格式，不是 JSON
            # 手动构建紧凑格式
            data_str = str(self.Data).replace(', ', ',').replace(' ', '')
            lines.append(f".Data={data_str}")

        return '\n'.join(lines)


class WindAPI:
    """Wind API 客户端"""
    
    def __init__(self, base_url: Optional[str] = None, debug: bool = False):
        """
        初始化客户端

        Args:
            base_url: API 地址,默认从配置文件或环境变量读取
            debug: 是否开启调试模式
        """
        self.base_url = base_url or get_api_url()
        self.debug = debug
        self.session = requests.Session()
        self.session.headers.update({"Content-Type": "application/json"})
    
    def _execute(self, code: str) -> WindData:
        """执行 Wind 代码"""
        try:
            if self.debug:
                print(f"[DEBUG] 请求 URL: {self.base_url}/execute")
                print(f"[DEBUG] 请求代码: {code}")

            response = self.session.post(
                f"{self.base_url}/execute",
                json={"code": code},
                timeout=30
            )

            if self.debug:
                print(f"[DEBUG] 响应状态: {response.status_code}")
                print(f"[DEBUG] 响应内容: {response.text[:500]}")

            response.raise_for_status()
            result = response.json()

            # 如果返回的是错误信息,抛出异常
            if isinstance(result, dict) and result.get("ErrorCode", 0) != 0:
                error_msg = result.get("error", f"Wind API 错误 (ErrorCode: {result.get('ErrorCode')})")
                raise Exception(error_msg)

            return WindData(result)
        except requests.exceptions.RequestException as e:
            raise Exception(f"API 调用失败: {str(e)}")
        except Exception as e:
            if "API 调用失败" in str(e):
                raise
            raise Exception(f"处理响应失败: {str(e)}")
    
    def wsd(self, codes, fields, begin_time: str, end_time: str, options: str = "") -> WindData:
        """
        获取历史数据

        Args:
            codes: 股票代码,可以是字符串(逗号分隔)或列表
            fields: 字段,可以是字符串(逗号分隔)或列表
            begin_time: 开始日期
            end_time: 结束日期
            options: 可选参数

        Returns:
            WindData 对象

        Example:
            data = w.wsd("000001.SZ", "close", "2025-01-01", "2025-01-10")
            data = w.wsd(["000001.SZ", "600000.SH"], ["close"], "2025-01-01", "2025-01-10")
            df = data.to_df()
        """
        # 处理 codes 和 fields，支持字符串或列表
        if isinstance(codes, list):
            codes = ','.join(codes)
        if isinstance(fields, list):
            fields = ','.join(fields)

        code = f'w.wsd("{codes}", "{fields}", "{begin_time}", "{end_time}"'
        if options:
            code += f', "{options}"'
        code += ')'
        return self._execute(code)
    
    def wsi(self, codes, fields, begin_time: str, end_time: str, options: str = "") -> WindData:
        """
        获取分钟数据

        Args:
            codes: 股票代码,可以是字符串(逗号分隔)或列表
            fields: 字段,可以是字符串(逗号分隔)或列表
            begin_time: 开始时间
            end_time: 结束时间
            options: 可选参数

        Returns:
            WindData 对象

        Example:
            data = w.wsi("000001.SZ", "close,volume", "2025-01-10 09:30:00", "2025-01-10 15:00:00")
            data = w.wsi(["000001.SZ"], ["close", "volume"], "2025-01-10 09:30:00", "2025-01-10 15:00:00")
            df = data.to_df()
        """
        # 处理 codes 和 fields，支持字符串或列表
        if isinstance(codes, list):
            codes = ','.join(codes)
        if isinstance(fields, list):
            fields = ','.join(fields)

        code = f'w.wsi("{codes}", "{fields}", "{begin_time}", "{end_time}"'
        if options:
            code += f', "{options}"'
        code += ')'
        return self._execute(code)
    
    def wsq(self, codes, fields: str = "rt_last") -> WindData:
        """
        获取实时行情

        Args:
            codes: 股票代码,可以是字符串(逗号分隔)或列表
            fields: 字段,可以是字符串(逗号分隔)或列表

        Returns:
            WindData 对象

        Example:
            data = w.wsq("000001.SZ,600000.SH", "rt_last,rt_vol")
            data = w.wsq(["000001.SZ", "600000.SH"], ["rt_last", "rt_vol"])
            df = data.to_df()
        """
        # 处理 codes 和 fields，支持字符串或列表
        if isinstance(codes, list):
            codes = ','.join(codes)
        if isinstance(fields, list):
            fields = ','.join(fields)

        code = f'w.wsq("{codes}", "{fields}")'
        return self._execute(code)
    
    def wset(self, tablename: str, options: str = "") -> WindData:
        """
        获取板块成分、指标数据

        Args:
            tablename: 数据集名称
            options: 可选参数

        Returns:
            WindData 对象

        Example:
            data = w.wset("sectorconstituent", "date=2025-01-10;sectorid=a001010100000000")
            df = data.to_df()
        """
        code = f'w.wset("{tablename}"'
        if options:
            code += f', "{options}"'
        code += ')'
        return self._execute(code)

    def edb(self, codes, begin_time: str, end_time: str, options: str = "") -> WindData:
        """
        获取经济数据库数据

        Args:
            codes: 指标代码,可以是字符串(逗号分隔)或列表
            begin_time: 开始日期
            end_time: 结束日期
            options: 可选参数,如 Fill=Previous

        Returns:
            WindData 对象

        Example:
            data = w.edb("M5567876", "2024-10-30", "2025-10-30", "Fill=Previous")
            data = w.edb(["M0017126", "M0017127"], "2024-01-01", "2024-12-31", "Fill=Previous")
            df = data.to_df()
        """
        # 处理 codes，支持字符串或列表
        if isinstance(codes, list):
            codes = ','.join(codes)

        code = f'w.edb("{codes}", "{begin_time}", "{end_time}"'
        if options:
            code += f', "{options}"'
        code += ')'
        return self._execute(code)
    
    def execute(self, code: str) -> WindData:
        """
        执行自定义 Wind 代码
        
        Args:
            code: Wind 代码
        
        Returns:
            WindData 对象
        
        Example:
            data = w.execute('w.wsd("000001.SZ", "close", "2025-01-01", "2025-01-10")')
            df = data.to_df()
        """
        return self._execute(code)
    
    def batch(self, codes: List[str]) -> List[WindData]:
        """
        批量执行
        
        Args:
            codes: Wind 代码列表
        
        Returns:
            WindData 对象列表
        
        Example:
            results = w.batch([
                'w.wsd("000001.SZ", "close", "2025-01-01", "2025-01-10")',
                'w.wsd("600000.SH", "close", "2025-01-01", "2025-01-10")'
            ])
        """
        try:
            response = self.session.post(
                f"{self.base_url}/batch",
                json={"codes": codes},
                timeout=60
            )
            response.raise_for_status()
            return [WindData(item) for item in response.json()]
        except Exception as e:
            raise Exception(f"批量调用失败: {str(e)}")
    
    def health(self) -> dict:
        """健康检查"""
        try:
            response = self.session.get(f"{self.base_url}/health", timeout=5)
            response.raise_for_status()
            return response.json()
        except Exception as e:
            raise Exception(f"健康检查失败: {str(e)}")

