"""
AkShare 数据源封装。

统一封装 AkShare 调用，返回标准化的 DataFrame。
所有网络请求都在这一层处理，包含重试和容错。

支持个股和指数两种数据接口。
"""

# 指数代码识别：.SH 后缀为主板指数，399xxx 为深证指数
# 注意区分：000001.SZ 是平安银行(个股)，000001.SH 是上证指数
def is_index_code(ts_code: str) -> bool:
    """判断是否为指数代码。排除 000xxx.SZ 个股。"""
    if not ts_code:
        return False
    code = ts_code.upper()
    # .SH 开头 000 是指数
    if code.endswith(".SH") and (code.startswith("000") or code.startswith("399")):
        return True
    # 深交所 399xxx 指数
    if code.startswith("399"):
        return True
    # 无后缀的纯数字 000xxx（上证指数常见写法）
    if code == "000001":
        return True
    return False

import time
import pandas as pd
import akshare as ak

from config.settings import AKSHARE_CONFIG


class AkShareSource:
    """AkShare 数据源。"""

    def __init__(self):
        self._timeout = AKSHARE_CONFIG["request_timeout"]
        self._retry = AKSHARE_CONFIG["retry_times"]
        self._delay = AKSHARE_CONFIG["retry_delay"]

    def _retry_call(self, fn, name: str, **kwargs):
        """带重试的 API 调用包装。重试间隔递增。"""
        last_err = None
        for i in range(self._retry):
            try:
                return fn(**kwargs)
            except Exception as e:
                last_err = e
                wait = self._delay * (i + 1)
                print(f"  [retry] {name} 失败 ({e})，{wait}s 后重试 ({i + 1}/{self._retry})...")
                if i < self._retry - 1:
                    time.sleep(wait)
        raise last_err  # type: ignore

    # ── 股票列表 ──────────────────────────────────────────

    def fetch_stock_list(self) -> pd.DataFrame:
        """获取 A 股股票列表。"""
        df = self._retry_call(ak.stock_info_a_code_name, "stock_list")
        df = df.rename(columns={
            "code": "ts_code",
            "name": "name",
        })
        return df[["ts_code", "name"]]

    # ── 日线数据 ──────────────────────────────────────────

    def fetch_daily(
        self, ts_code: str, start: str, end: str | None = None
    ) -> pd.DataFrame:
        """
        获取单只股票日线行情。

        参数:
            ts_code: 股票代码，如 '000001'（纯数字格式，AkShare 要求）
            start: 起始日期 'YYYYMMDD'
            end: 结束日期 'YYYYMMDD'，None 表示今天
        """
        symbol = ts_code.replace(".SZ", "").replace(".SH", "").replace(".BJ", "")
        end = end or time.strftime("%Y%m%d")
        df = self._retry_call(
            ak.stock_zh_a_hist,
            "daily",
            symbol=symbol,
            period="daily",
            start_date=start,
            end_date=end,
            adjust="qfq",  # 前复权
        )
        if df.empty:
            return df

        df = df.rename(columns={
            "日期": "trade_date",
            "开盘": "open",
            "收盘": "close",
            "最高": "high",
            "最低": "low",
            "成交量": "vol",
            "成交额": "amount",
            "振幅": "amplitude",
            "涨跌幅": "pct_chg",
            "涨跌额": "change",
            "换手率": "turnover_rate",
        })
        df["ts_code"] = ts_code
        # AkShare 返回 'YYYY-MM-DD'，统一转为 'YYYYMMDD'
        df["trade_date"] = df["trade_date"].astype(str).str.replace("-", "")
        return df

    # ── 指数日线 ──────────────────────────────────────────

    def fetch_index_daily(
        self, ts_code: str, start: str, end: str | None = None
    ) -> pd.DataFrame:
        """
        获取指数日线行情。

        AkShare index_zh_a_hist 接口，symbol 为纯数字（如 '000001'）。
        """
        symbol = ts_code.replace(".SH", "").replace(".SZ", "").replace(".BJ", "")
        end = end or time.strftime("%Y%m%d")
        df = self._retry_call(
            ak.index_zh_a_hist,
            "index_daily",
            symbol=symbol, period="daily",
            start_date=start, end_date=end,
        )
        if df.empty:
            return df

        df = df.rename(columns={
            "日期": "trade_date",
            "开盘": "open",
            "收盘": "close",
            "最高": "high",
            "最低": "low",
            "成交量": "vol",
            "成交额": "amount",
            "涨跌幅": "pct_chg",
            "涨跌额": "change",
        })
        df["ts_code"] = ts_code
        df["trade_date"] = df["trade_date"].astype(str)
        return df

    # ── 财务数据 ──────────────────────────────────────────

    def fetch_financial(self, ts_code: str) -> pd.DataFrame:
        """获取单只股票核心财务指标（同花顺接口）。"""
        symbol = ts_code.replace(".SZ", "").replace(".SH", "").replace(".BJ", "")
        try:
            df = ak.stock_financial_abstract_ths(symbol=symbol)
            if df.empty:
                return pd.DataFrame()

            df = df.rename(columns={
                "报告期": "end_date",
                "净利润": "net_profit",
                "净利润同比增长率": "net_profit_yoy",
                "扣非净利润": "deducted_net_profit",
                "扣非净利润同比增长率": "deducted_net_profit_yoy",
                "营业总收入": "total_revenue",
                "营业总收入同比增长率": "total_revenue_yoy",
                "基本每股收益": "eps",
                "每股净资产": "bvps",
                "每股资本公积金": "capital_reserve_ps",
                "每股未分配利润": "undistributed_profit_ps",
                "每股经营现金流": "ocf_ps",
                "销售净利率": "net_profit_margin",
                "净资产收益率": "roe",
                "净资产收益率-摊薄": "roe_diluted",
                "营业周期": "operating_cycle",
                "应收账款周转天数": "receivables_days",
                "流动比率": "current_ratio",
                "速动比率": "quick_ratio",
                "保守速动比率": "conservative_quick_ratio",
                "产权比率": "equity_ratio",
                "资产负债率": "debt_to_assets",
            })
            df["ts_code"] = ts_code
            # 日期格式统一
            df["end_date"] = df["end_date"].astype(str).str.replace("-", "")
            # 数值列清洗：去掉 万/亿/% 等单位
            for col in df.columns:
                if col in ("ts_code", "end_date"):
                    continue
                df[col] = self._parse_financial_value(df[col])
            return df
        except Exception:
            return pd.DataFrame()

    @staticmethod
    def _parse_financial_value(series: pd.Series) -> pd.Series:
        """解析财务数值字符串 '4302.00万', '64.75%', 'False' → float"""
        def _parse(v):
            if v is None or v == "False" or v == "":
                return None
            if isinstance(v, (int, float)):
                return float(v)
            s = str(v).strip()
            if not s:
                return None
            try:
                if s.endswith("%"):
                    return float(s[:-1])
                if "万" in s:
                    return float(s.replace("万", "")) * 1e4
                if "亿" in s:
                    return float(s.replace("亿", "")) * 1e8
                return float(s)
            except ValueError:
                return None
        return series.apply(_parse).astype("float64")
