from __future__ import annotations

from datetime import datetime
from typing import Any

from backend.bootstrap.config import normalize_date
from backend.data.providers.tushare_client import TushareError


class DragonTigerServiceMixin:
    def get_hot_money_profiles(self, force: bool = False) -> dict[str, Any]:
        cache_kind = "hot_money_profiles_v1"
        cache_key = "directory"
        cached = self.database.get_data_snapshot(cache_kind, cache_key)
        if cached and not force:
            cached["meta"] = {**cached.get("meta", {}), "cached": True}
            return cached
        if self.configured:
            try:
                payload = self._tushare_client().hot_money_profiles()
            except TushareError:
                if cached:
                    cached["meta"] = {
                        **cached.get("meta", {}),
                        "cached": True,
                        "stale": True,
                        "notice": "名录暂未完成更新，当前展示最近一次收录结果。",
                    }
                    return cached
                return {
                    "meta": {
                        "source": "unavailable",
                        "status": "unavailable",
                        "schema_version": 1,
                        "cached": False,
                        "updated_at": datetime.now().astimezone().isoformat(timespec="seconds"),
                        "notice": "游资名录暂不可用，请稍后重试。",
                    },
                    "summary": {
                        "profile_count": 0,
                        "described_count": 0,
                        "organization_count": 0,
                    },
                    "profiles": [],
                }
            payload["meta"]["cached"] = False
            if payload.get("meta", {}).get("status") == "success":
                self.database.save_data_snapshot(cache_kind, cache_key, "tushare", payload)
            return payload
        if cached:
            cached["meta"] = {**cached.get("meta", {}), "cached": True}
            return cached
        return {
            "meta": {
                "source": "unavailable",
                "status": "unavailable",
                "schema_version": 1,
                "cached": False,
                "updated_at": datetime.now().astimezone().isoformat(timespec="seconds"),
                "notice": "游资名录暂不可用，请联系管理员检查行情配置。",
            },
            "summary": {
                "profile_count": 0,
                "described_count": 0,
                "organization_count": 0,
            },
            "profiles": [],
        }

    def get_dragon_tiger(self, trade_date: str, force: bool = False) -> dict[str, Any]:
        normalized_date = normalize_date(trade_date)
        cache_kind = "hot_money_detail_v3"
        if not force:
            cached = self.database.get_data_snapshot(cache_kind, normalized_date)
            if (
                cached
                and cached.get("meta", {}).get("source") == "tushare"
                and cached.get("meta", {}).get("status") == "success"
                and int(cached.get("meta", {}).get("schema_version") or 0) == 3
            ):
                cached["meta"] = {**cached.get("meta", {}), "cached": True}
                return cached
        if self.configured:
            try:
                payload = self._tushare_client().dragon_tiger(normalized_date)
            except TushareError as exc:
                return {
                    "meta": {
                        "requested_date": f"{normalized_date[:4]}-{normalized_date[4:6]}-{normalized_date[6:8]}",
                        "trade_date": f"{normalized_date[:4]}-{normalized_date[4:6]}-{normalized_date[6:8]}",
                        "source": "tushare_error",
                        "status": "error",
                        "schema_version": 3,
                        "cached": False,
                        "updated_at": datetime.now().astimezone().isoformat(timespec="seconds"),
                        "notice": "龙虎榜数据暂不可用，请稍后重试。",
                    },
                    "summary": {
                        "trader_count": 0,
                        "identity_count": 0,
                        "operation_count": 0,
                        "active_stock_count": 0,
                        "seat_net_buy_million": 0,
                        "unclassified_count": 0,
                        "directory_count": 0,
                    },
                    "traders": [],
                    "unclassified_seats": [],
                    "rows": [],
                }
            payload["meta"]["cached"] = False
            if payload.get("meta", {}).get("status") == "success":
                self.database.save_data_snapshot(cache_kind, normalized_date, "tushare", payload)
            return payload

        return {
            "meta": {
            "requested_date": f"{normalized_date[:4]}-{normalized_date[4:6]}-{normalized_date[6:8]}",
            "trade_date": f"{normalized_date[:4]}-{normalized_date[4:6]}-{normalized_date[6:8]}",
            "source": "unavailable",
            "status": "unavailable",
            "schema_version": 3,
            "cached": False,
            "notice": "龙虎榜数据暂不可用，请联系管理员检查行情配置。",
            },
            "summary": {
                "trader_count": 0,
                "identity_count": 0,
                "operation_count": 0,
                "active_stock_count": 0,
                "seat_net_buy_million": 0,
                "unclassified_count": 0,
                "directory_count": 0,
            },
            "traders": [],
            "unclassified_seats": [],
            "rows": [],
        }

    def _apply_seat_aliases(self, payload: dict[str, Any]) -> dict[str, Any]:
        aliases = self.database.list_seat_aliases()
        result = dict(payload)
        rows = payload.get("rows") or []
        for row in rows:
            for institution in row.get("institutions") or []:
                institution["alias"] = aliases.get(institution.get("seat_name", ""), "")
        traders: dict[tuple[str, str], dict[str, Any]] = {}
        unclassified: dict[str, dict[str, Any]] = {}
        seen_operations: set[tuple[Any, ...]] = set()
        builtin_aliases = {
            "国泰海通证券股份有限公司南京太平南路证券营业部": "作手新一",
        }

        for row in rows:
            for institution in row.get("institutions") or []:
                seat_name = str(institution.get("seat_name") or "未知席位").strip()
                saved_alias = str(institution.get("alias") or "").strip()
                builtin_alias = builtin_aliases.get(seat_name, "")
                if saved_alias or builtin_alias:
                    identity_name = saved_alias or builtin_alias
                    identity_type = "trader"
                    recognized = True
                    identity_source = "manual" if saved_alias else "builtin"
                elif "机构专用" in seat_name:
                    identity_name = "机构专用"
                    identity_type = "institution"
                    recognized = True
                    identity_source = "system"
                elif "沪股通专用" in seat_name or "深股通专用" in seat_name:
                    identity_name = "北向资金"
                    identity_type = "channel"
                    recognized = True
                    identity_source = "system"
                else:
                    identity_name = seat_name
                    identity_type = "unclassified"
                    recognized = False
                    identity_source = "raw"

                buy = round(float(institution.get("buy_million") or 0), 2)
                sell = round(float(institution.get("sell_million") or 0), 2)
                net_buy = round(float(institution.get("net_buy_million") or 0), 2)
                operation_key = (row.get("code"), seat_name, buy, sell, net_buy)
                if operation_key in seen_operations:
                    continue
                seen_operations.add(operation_key)

                group_key = (identity_type, identity_name)
                group = traders.setdefault(
                    group_key,
                    {
                        "name": identity_name,
                        "identity_type": identity_type,
                        "identity_source": identity_source,
                        "recognized": recognized,
                        "buy_million": 0.0,
                        "sell_million": 0.0,
                        "net_buy_million": 0.0,
                        "seat_names": set(),
                        "stock_codes": set(),
                        "operations": [],
                    },
                )
                group["buy_million"] += buy
                group["sell_million"] += sell
                group["net_buy_million"] += net_buy
                group["seat_names"].add(seat_name)
                group["stock_codes"].add(str(row.get("code") or ""))
                group["operations"].append(
                    {
                        "code": row.get("code") or "",
                        "name": row.get("name") or "--",
                        "change": row.get("change") or 0,
                        "direction": "买入" if net_buy > 0 else "卖出" if net_buy < 0 else "持平",
                        "buy_million": buy,
                        "sell_million": sell,
                        "net_buy_million": net_buy,
                        "reason": row.get("reason") or "--",
                        "seat_name": seat_name,
                        "seat_alias": identity_name if recognized else "",
                    }
                )

                if not recognized:
                    pending = unclassified.setdefault(
                        seat_name,
                        {
                            "seat_name": seat_name,
                            "stock_codes": set(),
                            "operation_count": 0,
                            "buy_million": 0.0,
                            "sell_million": 0.0,
                            "net_buy_million": 0.0,
                        },
                    )
                    pending["stock_codes"].add(str(row.get("code") or ""))
                    pending["operation_count"] += 1
                    pending["buy_million"] += buy
                    pending["sell_million"] += sell
                    pending["net_buy_million"] += net_buy

        type_order = {"trader": 0, "institution": 1, "channel": 2, "unclassified": 3}
        aggregated = list(traders.values())
        aggregated.sort(
            key=lambda item: (
                type_order.get(item["identity_type"], 9),
                -abs(item["net_buy_million"]),
                item["name"],
            )
        )
        for index, group in enumerate(aggregated, start=1):
            group["id"] = f"identity-{index}"
            group["buy_million"] = round(group["buy_million"], 2)
            group["sell_million"] = round(group["sell_million"], 2)
            group["net_buy_million"] = round(group["net_buy_million"], 2)
            group["seat_count"] = len(group.pop("seat_names"))
            group["stock_count"] = len(group.pop("stock_codes"))
            group["operation_count"] = len(group["operations"])
            group["operations"].sort(
                key=lambda item: abs(float(item.get("net_buy_million") or 0)), reverse=True
            )

        pending_seats = list(unclassified.values())
        for pending in pending_seats:
            pending["stock_count"] = len(pending.pop("stock_codes"))
            pending["buy_million"] = round(pending["buy_million"], 2)
            pending["sell_million"] = round(pending["sell_million"], 2)
            pending["net_buy_million"] = round(pending["net_buy_million"], 2)
        pending_seats.sort(key=lambda item: abs(item["net_buy_million"]), reverse=True)

        operation_count = sum(item["operation_count"] for item in aggregated)
        active_stocks = {
            operation["code"] for item in aggregated for operation in item["operations"]
        }
        seat_net_buy = round(sum(item["net_buy_million"] for item in aggregated), 2)
        result["rows"] = rows
        result["traders"] = aggregated
        result["unclassified_seats"] = pending_seats
        result["summary"] = {
            **(payload.get("summary") or {}),
            "trader_count": sum(item["identity_type"] == "trader" for item in aggregated),
            "identity_count": len(aggregated),
            "operation_count": operation_count,
            "active_stock_count": len(active_stocks),
            "seat_net_buy_million": seat_net_buy,
            "unclassified_count": len(pending_seats),
        }
        return result
