from __future__ import annotations

import json
import re
from datetime import datetime
from typing import Any

from backend.bootstrap.config import display_compact_date as _display_date
from backend.data.numbers import finite_number as _number
from backend.data.providers.tushare_helpers import _text
from backend.data.providers.tushare_transport import TushareError


class DragonTigerMixin:
    def hot_money_profiles(self) -> dict[str, Any]:
        rows = self.query("hm_list", {}, "name,desc,orgs")
        profiles: list[dict[str, Any]] = []
        seen_names: set[str] = set()
        for row in rows:
            name = str(row.get("name") or "").strip()
            if not name or name in seen_names:
                continue
            seen_names.add(name)
            description = _text(row.get("desc"))
            organization_text = _text(row.get("orgs"))
            parsed_organizations: Any = None
            if organization_text.startswith("["):
                try:
                    parsed_organizations = json.loads(organization_text)
                except json.JSONDecodeError:
                    parsed_organizations = None
            organization_parts = (
                parsed_organizations
                if isinstance(parsed_organizations, list)
                else re.split(r"[,，;；\n]+", organization_text)
            )
            organizations = list(dict.fromkeys(
                _text(part)
                for part in organization_parts
                if _text(part)
            ))
            profiles.append(
                {
                    "id": f"hot-money-profile-{len(profiles) + 1}",
                    "name": name,
                    "description": description,
                    "organizations": organizations,
                    "organization_count": len(organizations),
                }
            )
        return {
            "meta": {
                "source": "tushare",
                "status": "success" if profiles else "empty",
                "schema_version": 1,
                "updated_at": datetime.now().astimezone().isoformat(timespec="seconds"),
                "notice": "",
            },
            "summary": {
                "profile_count": len(profiles),
                "described_count": sum(bool(item["description"]) for item in profiles),
                "organization_count": sum(item["organization_count"] for item in profiles),
            },
            "profiles": profiles,
        }

    def dragon_tiger(self, requested_date: str) -> dict[str, Any]:
        trade_date, _ = self.resolve_trade_context(requested_date)
        detail_rows = self.query(
            "hm_detail",
            {"trade_date": trade_date},
            "trade_date,ts_code,ts_name,buy_amount,sell_amount,net_amount,"
            "hm_name,hm_orgs,tag",
        )

        notices: list[str] = []
        try:
            directory_rows = self.query("hm_list", {}, "name,desc,orgs")
        except TushareError as exc:
            directory_rows = []
            notices.append(f"游资名录暂不可用：{exc}")
        directory = {
            str(row.get("name") or "").strip(): {
                "description": _text(row.get("desc")),
                "orgs": _text(row.get("orgs")),
            }
            for row in directory_rows
            if str(row.get("name") or "").strip()
        }

        # 个股龙虎榜仅用于补充涨幅和上榜原因，不参与游资身份识别。
        try:
            top_rows = self.query(
                "top_list",
                {"trade_date": trade_date},
                "trade_date,ts_code,name,pct_change,reason",
            )
        except TushareError as exc:
            top_rows = []
            notices.append(f"个股龙虎榜辅助信息暂不可用：{exc}")
        stock_context: dict[str, dict[str, Any]] = {}
        for row in top_rows:
            ts_code = str(row.get("ts_code") or "")
            if ts_code and ts_code not in stock_context:
                stock_context[ts_code] = row

        groups: dict[str, dict[str, Any]] = {}
        for row in detail_rows:
            trader_name = str(row.get("hm_name") or "未命名游资").strip()
            ts_code = str(row.get("ts_code") or "").strip()
            stock = stock_context.get(ts_code, {})
            directory_item = directory.get(trader_name, {})
            seat_name = _text(row.get("hm_orgs")) or directory_item.get("orgs") or "--"
            buy = round(_number(row.get("buy_amount")) / 1000000, 2)
            sell = round(_number(row.get("sell_amount")) / 1000000, 2)
            net_buy = round(_number(row.get("net_amount")) / 1000000, 2)
            group = groups.setdefault(
                trader_name,
                {
                    "name": trader_name,
                    "description": directory_item.get("description") or "",
                    "directory_orgs": directory_item.get("orgs") or "",
                    "identity_type": "trader",
                    "identity_source": "tushare_hm",
                    "recognized": True,
                    "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
            if seat_name != "--":
                group["seat_names"].add(seat_name)
            code = ts_code.split(".")[0]
            if code:
                group["stock_codes"].add(code)
            group["operations"].append(
                {
                    "code": code,
                    "ts_code": ts_code,
                    "name": row.get("ts_name") or stock.get("name") or "--",
                    "change": (
                        _number(stock.get("pct_change"))
                        if stock.get("pct_change") is not None
                        else None
                    ),
                    "direction": "买入" if net_buy > 0 else "卖出" if net_buy < 0 else "持平",
                    "buy_million": buy,
                    "sell_million": sell,
                    "net_buy_million": net_buy,
                    "seat_name": seat_name,
                    "seat_alias": trader_name,
                    "tag": _text(row.get("tag")) or "--",
                    "reason": _text(stock.get("reason")) or "--",
                }
            )

        traders = list(groups.values())
        traders.sort(key=lambda item: abs(item["net_buy_million"]), reverse=True)
        for index, group in enumerate(traders, start=1):
            group["id"] = f"hot-money-{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
            )

        operation_count = sum(item["operation_count"] for item in traders)
        active_stocks = {
            operation["code"] for item in traders for operation in item["operations"]
            if operation["code"]
        }
        net_buy_total = round(sum(item["net_buy_million"] for item in traders), 2)
        status = "success" if detail_rows else "partial" if top_rows else "empty"
        if not detail_rows:
            notices.insert(
                0,
                f"当日有 {len(stock_context)} 只股票上榜，但未返回可识别的游资每日明细。"
                if top_rows
                else "该交易日未返回龙虎榜或游资每日明细。",
            )
        return {
            "meta": {
                "requested_date": _display_date(requested_date),
                "trade_date": _display_date(trade_date),
                "source": "tushare",
                "status": status,
                "schema_version": 3,
                "updated_at": datetime.now().astimezone().isoformat(timespec="seconds"),
                "notice": "；".join(notices),
            },
            "summary": {
                "trader_count": len(traders),
                "identity_count": len(traders),
                "operation_count": operation_count,
                "active_stock_count": len(active_stocks),
                "seat_net_buy_million": net_buy_total,
                "unclassified_count": 0,
                "directory_count": len(directory),
                "official_stock_count": len(stock_context),
            },
            "traders": traders,
            "unclassified_seats": [],
            "rows": [],
        }
