from __future__ import annotations

import re
from datetime import datetime, timedelta
from typing import Any

from backend.bootstrap.config import (
    normalize_date,
    tushare_code,
    validate_stock_code,
    validate_text,
)
from backend.data.providers.tushare_client import TushareError


class HeavenMarketContextMixin:
    def _resolve_heaven_stock_code(self, query: str) -> str:
        raw = validate_text(query, "股票代码或名称", 30, required=True)
        code_match = re.fullmatch(r"(\d{6})(?:\.(?:SH|SZ|BJ))?", raw.upper())
        if code_match:
            return validate_stock_code(code_match.group(1))

        candidates = self.database.search_stock_master(raw)
        exact = [item for item in candidates if str(item.get("name") or "").casefold() == raw.casefold()]
        if not exact and self.configured:
            try:
                rows = self._tushare_client().query(
                    "stock_basic",
                    {"name": raw, "list_status": "L"},
                    "ts_code,symbol,name,industry,market,list_date",
                )
            except TushareError:
                rows = []
            if rows:
                self.database.upsert_stock_master(rows)
                candidates = self.database.search_stock_master(raw)
                exact = [
                    item
                    for item in candidates
                    if str(item.get("name") or "").casefold() == raw.casefold()
                ]

        matches = exact or candidates
        if len(matches) == 1:
            return validate_stock_code(str(matches[0].get("code") or ""))
        if len(matches) > 1:
            choices = "、".join(
                f"{item.get('name') or '--'}（{item.get('code') or '--'}）"
                for item in matches[:5]
            )
            raise ValueError(f"匹配到多只股票：{choices}。请输入六位股票代码。")
        raise ValueError(f"未找到股票“{raw}”，请检查名称或输入六位股票代码。")

    def _heaven_stock_context(
        self,
        stock_code: str,
        trade_date: str,
        dashboard: dict[str, Any],
        market_mode: str,
    ) -> dict[str, Any]:
        """Return the only stock contract accepted by heaven trend."""
        pool_row = next(
            (
                dict(row) for key in ("limits", "broken", "down_limits")
                for row in dashboard.get(key) or []
                if str(row.get("code") or "") == stock_code
            ),
            {},
        )
        if market_mode == "intraday":
            if self.configured:
                try:
                    quote = self._tushare_client().realtime_stock_quote(
                        tushare_code(stock_code),
                        trade_date,
                    )
                    return {
                        **quote,
                        "status": pool_row.get("status") or "普通",
                        "seal_amount_million": pool_row.get("seal_amount_million") or 0,
                        "open_times": pool_row.get("open_times") or 0,
                        "streak": pool_row.get("streak") or 0,
                        "precise": True,
                    }
                except TushareError:
                    pass
            if pool_row:
                return {
                    **pool_row,
                    "data_source": "dashboard_rt" if dashboard.get("meta", {}).get("realtime") else "dashboard",
                    "trade_date": trade_date,
                    "realtime": bool(dashboard.get("meta", {}).get("realtime")),
                    "precise": False,
                }
            return {
                "code": stock_code,
                "name": "--",
                "sector": "其他",
                "trade_date": trade_date,
                "realtime": False,
                "precise": False,
            }

        detail = self.get_stock_detail(stock_code, trade_date, force=True)
        detail_meta = detail.get("meta") or {}
        stock = detail.get("stock") or {}
        resolved_date = normalize_date(str(detail_meta.get("trade_date") or trade_date))
        source = str(detail_meta.get("source") or "")
        return {
            "code": stock_code,
            "name": stock.get("name") or pool_row.get("name") or "--",
            "sector": stock.get("industry") or pool_row.get("sector") or "其他",
            "status": pool_row.get("status") or "普通",
            "change": stock.get("change") or 0,
            "turnover_rate": stock.get("turnover_rate") or 0,
            "amount_billion": stock.get("amount_billion") or 0,
            "seal_amount_million": pool_row.get("seal_amount_million") or 0,
            "open_times": pool_row.get("open_times") or 0,
            "streak": pool_row.get("streak") or 0,
            "data_source": source,
            "trade_date": resolved_date,
            "realtime": False,
            "precise": source == "tushare" and resolved_date == trade_date,
        }

    def _heaven_index_context(
        self,
        trade_date: str,
        dashboard: dict[str, Any],
        market_mode: str = "historical",
    ) -> dict[str, Any]:
        cached = self.database.get_data_snapshot("heaven_indices", trade_date)
        cached_valid = False
        if cached:
            cached_rows = list(cached.get("indices") or [])
            cached_dates = {
                str(row.get("trade_date") or "").replace("-", "")
                for row in cached_rows
            }
            cached_valid = (
                len(cached_rows) == 3
                and cached_dates == {trade_date}
                and bool(cached.get("precise"))
                and not cached.get("realtime")
                and str(cached.get("source") or "") == "tushare"
                and int(cached.get("schema_version") or 0) >= 3
            )
        if market_mode != "intraday" and cached_valid:
            return cached

        if not self.configured:
            error = "Tushare Token 未配置"
        else:
            try:
                client = self._tushare_client()
                if market_mode == "intraday":
                    payload = self._aggregate_index_context(trade_date)
                    payload["schema_version"] = 3
                    return payload
                payload = client.market_indices(trade_date)
                payload["schema_version"] = 3
                if market_mode == "closed":
                    payload["finalized"] = True
                self.database.save_data_snapshot(
                    "heaven_indices",
                    trade_date,
                    str(payload.get("source") or "tushare"),
                    payload,
                )
                return payload
            except Exception as exc:
                error = str(exc)
        overview = dashboard.get("overview") or {}
        up_count = float(overview.get("up_count") or 0)
        down_count = float(overview.get("down_count") or 0)
        breadth = (up_count - down_count) / max(up_count + down_count, 1)
        return {
            "source": "market_breadth_proxy",
            "trade_date": trade_date,
            "realtime": False,
            "precise": False,
            "schema_version": 3,
            "notice": f"指数数据不可用，当前以市场宽度代理：{error}",
            "indices": [],
            "aggregate": {
                "average_pct_chg": round(breadth * 2.5, 3),
                "average_return_5d": 0,
                "average_return_20d": 0,
            },
        }

    def _aggregate_index_context(
        self,
        trade_date: str,
        tushare_error: str = "",
    ) -> dict[str, Any]:
        quotes = self.realtime_aggregator.tencent_indices()
        epochs = [int(item.get("quote_time_epoch") or 0) for item in quotes]
        quote_dates = {
            datetime.fromtimestamp(epoch).astimezone().strftime("%Y%m%d")
            for epoch in epochs if epoch
        }
        if len(quotes) != 3 or quote_dates != {trade_date}:
            raise ValueError("腾讯三大指数日期与目标交易日不一致")
        now = datetime.now().astimezone()
        max_skew = 120 if now.hour >= 15 else 15
        if max(epochs) - min(epochs) > max_skew:
            raise ValueError(f"腾讯三大指数时间差超过{max_skew}秒")

        code_map = {
            "000001": "000001.SH",
            "399001": "399001.SZ",
            "399006": "399006.SZ",
        }
        client = self._tushare_client()
        indices = []
        start_date = (
            datetime.strptime(trade_date, "%Y%m%d") - timedelta(days=20)
        ).strftime("%Y%m%d")
        for quote in quotes:
            ts_code = code_map[str(quote.get("code") or "")]
            history = client.query(
                "index_daily",
                {"ts_code": ts_code, "start_date": start_date, "end_date": trade_date},
                "ts_code,trade_date,close,pct_chg",
            )
            history.sort(key=lambda item: str(item.get("trade_date") or ""))
            completed_closes = [
                float(item.get("close") or 0)
                for item in history
                if str(item.get("trade_date") or "") < trade_date
                and float(item.get("close") or 0) > 0
            ]
            close_5d = (
                completed_closes[-5]
                if len(completed_closes) >= 5
                else completed_closes[0] if completed_closes else 0
            )
            close = float(quote.get("price") or 0)
            indices.append(
                {
                    "ts_code": ts_code,
                    "name": quote.get("name") or ts_code,
                    "trade_date": trade_date,
                    "close": close,
                    "pct_chg": round(float(quote.get("change") or 0), 3),
                    "return_5d": round((close / close_5d - 1) * 100, 3) if close_5d else 0,
                    "return_20d": 0,
                    "amount_billion": float(quote.get("amount_billion") or 0),
                    "quote_time": quote.get("quote_time") or "",
                }
            )
        return {
            "trade_date": trade_date,
            "source": "+".join(
                sorted({str(item.get("source") or "web_quote") for item in quotes})
                + ["tushare_index_daily"]
            ),
            "realtime": True,
            "precise": True,
            "indices": indices,
            "aggregate": {
                "average_pct_chg": round(
                    sum(item["pct_chg"] for item in indices) / len(indices), 3
                ),
                "average_return_5d": round(
                    sum(item["return_5d"] for item in indices) / len(indices), 3
                ),
                "average_return_20d": 0,
            },
            "quote_time_skew_seconds": max(epochs) - min(epochs),
            "notice": (
                "指数实时行情来自腾讯行情，5日趋势来自Tushare历史指数。"
                + (f" Tushare实时指数未使用：{tushare_error}" if tushare_error else "")
            ),
        }

    def _heaven_sector_context(
        self,
        identifier: str,
        trade_date: str,
        market_mode: str = "historical",
    ) -> dict[str, Any] | None:
        """Return the Shenwan L2 sector context for heaven trend.

        观势行业层只使用申万二级行业。外显盘中使用 rt_sw_k、历史使用
        sw_daily；内核独立使用目标日期成分股行情聚合。收盘过渡期在
        sw_daily 入库前接受同日15:00后的 rt_sw_k 收盘快照。
        """
        cache_key = f"{trade_date}:{identifier.strip().lower()}"
        cached = self.database.get_data_snapshot("heaven_sector", cache_key)
        cached_date = str((cached or {}).get("trade_date") or "").replace("-", "")
        cached_valid = bool(
            cached
            and cached_date == trade_date
            and cached.get("taxonomy") == "sw_l2"
            and cached.get("inner_precise", cached.get("precise"))
            and cached.get("outer_precise", cached.get("precise"))
            and not cached.get("realtime")
            and int(cached.get("schema_version") or 0) >= 6
        )
        if market_mode != "intraday" and cached_valid:
            return cached
        if not self.configured:
            return None
        try:
            payload = self._tushare_client().sw_sector_snapshot(
                tushare_code(identifier),
                trade_date,
                realtime_expected=market_mode == "intraday",
                allow_realtime_close=market_mode == "closed",
            )
        except TushareError as exc:
            if cached_valid:
                return cached
            return {
                "name": "",
                "code": "",
                "taxonomy": "sw_l2",
                "source": "tushare",
                "trade_date": trade_date,
                "realtime": market_mode == "intraday",
                "precise": False,
                "inner_precise": False,
                "outer_precise": False,
                "coverage": 0,
                "member_count": 0,
                "quote_count": 0,
                "error": f"申万二级行业数据获取失败：{exc}",
            }
        if not payload.get("realtime") and payload.get("precise"):
            self.database.save_data_snapshot(
                "heaven_sector",
                cache_key,
                str(payload.get("source") or "tushare"),
                payload,
            )
        return payload
