from __future__ import annotations

import copy
from datetime import datetime, timedelta
from typing import Any

from backend.data.numbers import non_nan_number as _number
from backend.data.providers.tushare_client import TushareError
from backend.features.market.insights_context import _display_date


class MarketThemeInsightsMixin:
    def _theme_directory(self) -> list[dict[str, Any]]:
        cached = self.database.get_data_snapshot("theme_directory_v1", "ths") or {}
        if cached.get("items"):
            return list(cached["items"])
        rows = self.client.query(
            "ths_index", {}, "ts_code,name,count,exchange,list_date,type"
        )
        items = [
            {
                "code": str(row.get("ts_code") or ""),
                "name": str(row.get("name") or ""),
                "member_count": int(_number(row.get("count"))),
                "list_date": str(row.get("list_date") or ""),
            }
            for row in rows
            if str(row.get("type") or "").upper() == "N"
            and str(row.get("exchange") or "").upper() == "A"
            and row.get("ts_code")
            and row.get("name")
        ]
        self.database.save_data_snapshot(
            "theme_directory_v1", "ths", "market", {"items": items}
        )
        return items

    def theme_library(self, requested_date: str, force: bool = False) -> dict[str, Any]:
        trade_date, previous_date = self._trade_context(requested_date)
        if not force:
            cached = self.database.get_data_snapshot("theme_library_v1", trade_date)
            if cached:
                result = copy.deepcopy(cached)
                result["meta"] = {**result.get("meta", {}), "cached": True}
                return result

        try:
            daily = self.client.query(
                "ths_daily",
                {"trade_date": trade_date},
                "ts_code,trade_date,open,high,low,close,pre_close,pct_change,vol,turnover_rate",
            )
        except TushareError:
            fallback = self._latest_feature_snapshot("theme_library_v1", trade_date)
            if fallback:
                result = copy.deepcopy(fallback)
                result["meta"] = {
                    **result.get("meta", {}),
                    "requested_date": _display_date(requested_date),
                    "carried_forward": True,
                    "cached": True,
                    "notice": "当前题材行情暂不可用，展示最近有效快照",
                }
                return result
            daily = []
        actual_date = trade_date
        carried_forward = False
        if not daily and previous_date:
            try:
                daily = self.client.query(
                    "ths_daily",
                    {"trade_date": previous_date},
                    "ts_code,trade_date,open,high,low,close,pre_close,pct_change,vol,turnover_rate",
                )
            except TushareError:
                daily = []
            actual_date = previous_date
            carried_forward = bool(daily)
        daily_map = {str(row.get("ts_code") or ""): row for row in daily}
        try:
            hot_rows = self.client.query("ths_hot", {"trade_date": actual_date})
        except TushareError:
            hot_rows = []
        hot_map = {
            str(row.get("ts_code") or ""): int(_number(row.get("rank")))
            for row in hot_rows
            if str(row.get("data_type") or "") == "概念板块"
        }
        items = []
        for item in self._theme_directory():
            quote = daily_map.get(item["code"], {})
            items.append(
                {
                    **item,
                    "change": round(_number(quote.get("pct_change")), 2),
                    "close": round(_number(quote.get("close")), 3),
                    "turnover_rate": round(_number(quote.get("turnover_rate")), 2),
                    "volume": round(_number(quote.get("vol")), 2),
                    "hot_rank": hot_map.get(item["code"]),
                    "has_quote": bool(quote),
                }
            )
        items.sort(
            key=lambda item: (
                item["has_quote"],
                item["hot_rank"] is not None,
                -(item["hot_rank"] or 9999),
                item["change"],
            ),
            reverse=True,
        )
        quoted = [item for item in items if item["has_quote"]]
        result = {
            "meta": {
                "requested_date": _display_date(requested_date),
                "trade_date": _display_date(actual_date),
                "carried_forward": carried_forward,
                "cached": False,
                "notice": "" if quoted else "该交易日暂无题材行情，已保留题材目录",
                "updated_at": datetime.now().astimezone().isoformat(timespec="seconds"),
            },
            "summary": {
                "theme_count": len(items),
                "quoted_count": len(quoted),
                "up_count": sum(item["change"] > 0 for item in quoted),
                "down_count": sum(item["change"] < 0 for item in quoted),
                "hot_count": len(hot_map),
            },
            "items": items,
        }
        self.database.save_data_snapshot("theme_library_v1", trade_date, "market", result)
        return result

    def theme_detail(self, code: str, requested_date: str) -> dict[str, Any]:
        code = str(code or "").strip().upper()
        library = self.theme_library(requested_date)
        theme = next((item for item in library["items"] if item["code"] == code), None)
        if not theme:
            raise ValueError("未找到对应题材。")
        actual_date = str(library["meta"]["trade_date"]).replace("-", "")
        detail_key = f"{actual_date}:{code}"
        cached_detail = self.database.get_data_snapshot("theme_detail_v1", detail_key)
        if cached_detail:
            return cached_detail
        try:
            members = self.client.query(
                "ths_member", {"ts_code": code, "is_new": "Y"}, "ts_code,con_code,con_name"
            )
        except TushareError:
            members = []
        bars = self.database.daily_bars_for_date(actual_date)
        if not bars:
            bars = self.client.query(
                "daily",
                {"trade_date": actual_date},
                "ts_code,trade_date,open,high,low,close,pct_chg,vol,amount",
            )
            self.database.upsert_daily_bars(bars)
        bar_map = {str(row.get("ts_code") or ""): row for row in bars}
        normalized_members = []
        for member in members:
            ts_code = str(member.get("con_code") or "")
            quote = bar_map.get(ts_code, {})
            normalized_members.append(
                {
                    "code": ts_code.split(".")[0],
                    "ts_code": ts_code,
                    "name": str(member.get("con_name") or "--"),
                    "price": round(_number(quote.get("close")), 2),
                    "change": round(_number(quote.get("pct_chg")), 2),
                    "amount_billion": round(_number(quote.get("amount")) / 100_000, 2),
                    "has_quote": bool(quote),
                }
            )
        normalized_members.sort(
            key=lambda item: (item["has_quote"], item["change"], item["amount_billion"]),
            reverse=True,
        )
        end = datetime.strptime(actual_date, "%Y%m%d")
        try:
            history = self.client.query(
                "ths_daily",
                {
                    "ts_code": code,
                    "start_date": (end - timedelta(days=190)).strftime("%Y%m%d"),
                    "end_date": actual_date,
                },
                "ts_code,trade_date,open,high,low,close,pct_change,vol,turnover_rate",
            )
        except TushareError:
            history = []
        history.sort(key=lambda row: str(row.get("trade_date") or ""))
        series = [
            {
                "trade_date": _display_date(str(row.get("trade_date") or "")),
                "open": _number(row.get("open")),
                "high": _number(row.get("high")),
                "low": _number(row.get("low")),
                "close": _number(row.get("close")),
                "change": _number(row.get("pct_change")),
                "volume": _number(row.get("vol")),
            }
            for row in history[-90:]
        ]
        result = {
            "meta": {
                "trade_date": _display_date(actual_date),
                "notice": "" if members or history else "题材成分与走势暂不可用",
            },
            "theme": theme,
            "series": series,
            "members": normalized_members,
            "summary": {
                "member_count": len(normalized_members),
                "up_count": sum(item["change"] > 0 for item in normalized_members if item["has_quote"]),
                "down_count": sum(item["change"] < 0 for item in normalized_members if item["has_quote"]),
                "quoted_count": sum(item["has_quote"] for item in normalized_members),
            },
        }
        if members or history:
            self.database.save_data_snapshot("theme_detail_v1", detail_key, "market", result)
        return result
