from __future__ import annotations

from datetime import datetime, timedelta
from typing import Any

from backend.bootstrap.config import display_compact_date as _display_date
from backend.data.numbers import finite_number as _number


class StockMixin:
    def stock_detail(self, ts_code: str, requested_date: str) -> dict[str, Any]:
        trade_date, _ = self.resolve_trade_context(requested_date)
        end = datetime.strptime(trade_date, "%Y%m%d")
        start_date = (end - timedelta(days=190)).strftime("%Y%m%d")
        daily = self.query(
            "daily",
            {"ts_code": ts_code, "start_date": start_date, "end_date": trade_date},
            "ts_code,trade_date,open,high,low,close,pct_chg,vol,amount",
        )
        factors = self.query(
            "adj_factor",
            {"ts_code": ts_code, "start_date": start_date, "end_date": trade_date},
            "ts_code,trade_date,adj_factor",
        )
        basics = self.query(
            "stock_basic",
            {"ts_code": ts_code},
            "ts_code,symbol,name,area,industry,market,list_date",
        )
        daily_basics = self.query(
            "daily_basic",
            {"ts_code": ts_code, "trade_date": trade_date},
            "ts_code,trade_date,turnover_rate,volume_ratio,total_mv,circ_mv",
        )
        moneyflow = self.query(
            "moneyflow",
            {"ts_code": ts_code, "trade_date": trade_date},
            "ts_code,trade_date,buy_sm_amount,sell_sm_amount,buy_md_amount,sell_md_amount,"
            "buy_lg_amount,sell_lg_amount,buy_elg_amount,sell_elg_amount,net_mf_amount",
        )
        factor_map = {row["trade_date"]: _number(row.get("adj_factor"), 1) for row in factors}
        latest_factor = max(factor_map.values(), default=1) or 1
        prices = []
        for row in sorted(daily, key=lambda item: item.get("trade_date", ""))[-90:]:
            factor = factor_map.get(row.get("trade_date"), latest_factor)
            ratio = factor / latest_factor
            prices.append(
                {
                    "trade_date": _display_date(str(row.get("trade_date", ""))),
                    "open": round(_number(row.get("open")) * ratio, 3),
                    "high": round(_number(row.get("high")) * ratio, 3),
                    "low": round(_number(row.get("low")) * ratio, 3),
                    "close": round(_number(row.get("close")) * ratio, 3),
                    "change": _number(row.get("pct_chg")),
                    "volume": _number(row.get("vol")),
                    "amount_billion": round(_number(row.get("amount")) / 100000, 2),
                }
            )
        flow = moneyflow[0] if moneyflow else {}
        basic = basics[0] if basics else {}
        daily_basic = daily_basics[0] if daily_basics else {}
        latest = prices[-1] if prices else {}
        actual_trade_date = max(
            (str(row.get("trade_date") or "") for row in daily),
            default=trade_date,
        ) or trade_date
        return {
            "meta": {
                "requested_date": _display_date(requested_date),
                "trade_date": _display_date(actual_trade_date),
                "source": "tushare",
                "updated_at": datetime.now().astimezone().isoformat(timespec="seconds"),
                "notice": "",
            },
            "stock": {
                "code": ts_code.split(".")[0],
                "ts_code": ts_code,
                "name": basic.get("name") or "--",
                "industry": basic.get("industry") or "其他",
                "area": basic.get("area") or "--",
                "market": basic.get("market") or "--",
                "list_date": _display_date(str(basic.get("list_date") or "")),
                "price": latest.get("close", 0),
                "change": latest.get("change", 0),
                "turnover_rate": _number(daily_basic.get("turnover_rate")),
                "volume_ratio": _number(daily_basic.get("volume_ratio")),
                "amount_billion": latest.get("amount_billion", 0),
            },
            "prices": prices,
            "moneyflow": {
                "net_million": round(_number(flow.get("net_mf_amount")) / 100, 2),
                "large_million": round(
                    (_number(flow.get("buy_lg_amount")) + _number(flow.get("buy_elg_amount"))
                     - _number(flow.get("sell_lg_amount")) - _number(flow.get("sell_elg_amount"))) / 100,
                    2,
                ),
                "medium_million": round(
                    (_number(flow.get("buy_md_amount")) - _number(flow.get("sell_md_amount"))) / 100,
                    2,
                ),
                "small_million": round(
                    (_number(flow.get("buy_sm_amount")) - _number(flow.get("sell_sm_amount"))) / 100,
                    2,
                ),
            },
        }

    def stock_intraday(self, ts_code: str, requested_date: str) -> dict[str, Any]:
        trade_date, _ = self.resolve_trade_context(requested_date)
        display_date = _display_date(trade_date)
        rows = self.query(
            "stk_mins",
            {
                "ts_code": ts_code,
                "freq": "1min",
                "start_date": f"{display_date} 09:00:00",
                "end_date": f"{display_date} 15:30:00",
            },
            "ts_code,trade_time,open,close,high,low,vol,amount",
        )
        points = []
        for row in sorted(rows, key=lambda item: str(item.get("trade_time") or "")):
            trade_time = str(row.get("trade_time") or "")
            if not trade_time:
                continue
            points.append(
                {
                    "time": trade_time[-8:-3] if len(trade_time) >= 8 else trade_time,
                    "open": round(_number(row.get("open")), 3),
                    "high": round(_number(row.get("high")), 3),
                    "low": round(_number(row.get("low")), 3),
                    "close": round(_number(row.get("close")), 3),
                    "volume": _number(row.get("vol")),
                    "amount": _number(row.get("amount")),
                }
            )
        return {"trade_date": display_date, "points": points}
