from __future__ import annotations

import json
import secrets
from datetime import date
from typing import Any

from backend.bootstrap.config import normalize_date
from backend.features.heaven.agent import (
    HEAVEN_PROMPT_VERSIONS,
    HeavenAgentError,
    interpret_heaven,
)
from backend.features.heaven.engine import (
    build_five_phase_field,
    hexagram_from_lines,
)
from backend.features.heaven.knowledge import prepare_heaven_context
from backend.features.heaven.six_yao import build_six_yao_chart
from backend.features.market import MarketServiceMixin


class HeavenReadingMixin:
    def heaven_personal(self, payload: dict[str, Any]) -> dict[str, Any]:
        trade_date = normalize_date(str(payload.get("trade_date") or date.today().isoformat()))
        field = build_five_phase_field(
            trade_date,
            self.database.list_sector_phase_overrides(),
        )
        personal = self.account_personal_field(trade_date, field, public=True)
        if not personal:
            raise ValueError("请先在账号设置中保存个人命理资料。")
        return personal

    def heaven_hexagram(self, raw_lines: Any) -> dict[str, Any]:
        if not isinstance(raw_lines, list):
            raise ValueError("六爻起卦结果格式不正确。")
        try:
            lines = [int(value) for value in raw_lines]
        except (TypeError, ValueError) as exc:
            raise ValueError("六爻必须由六、七、八、九组成。") from exc
        return hexagram_from_lines(lines)

    def heaven_readings(
        self, mode: str, context_date: str = "", limit: int = 100
    ) -> dict[str, Any]:
        mode = str(mode or "").strip()
        if mode not in {"trend", "fortune", "heart"}:
            raise ValueError("解读记录类型不正确。")
        normalized_date = normalize_date(context_date) if context_date else ""
        return {
            "mode": mode,
            "items": self.database.list_heaven_readings(
                self.current_user_id, mode, normalized_date, limit
            ),
        }

    @staticmethod
    def _heaven_reading_identity(
        mode: str, context_date: str, context: dict[str, Any]
    ) -> tuple[str, str]:
        display_date = MarketServiceMixin._display_compact_date(context_date)
        if mode == "trend":
            stock = (context.get("selected_focus") or {}).get("stock") or {}
            code = str(stock.get("code") or "").strip()
            name = str(stock.get("name") or "").strip()
            hexagram = context.get("hexagram") or {}
            transformed = hexagram.get("transformed") or {}
            subject = " ".join(item for item in (code, name) if item) or "观势"
            detail = f"{display_date} · {hexagram.get('name') or '--'} → {transformed.get('name') or '--'}"
            return subject, detail
        if mode == "fortune":
            field = context.get("five_phase_field") or {}
            pillars = field.get("pillars") or {}
            dominant = (field.get("balance") or [{}])[0]
            subject = f"{display_date} 观气"
            detail = (
                f"{pillars.get('year') or '--'}年 · {pillars.get('month') or '--'}月 · "
                f"{pillars.get('day') or '--'}日 · {dominant.get('element') or '--'}气偏显"
            )
            return subject, detail
        hexagram = context.get("hexagram") or {}
        transformed = hexagram.get("transformed") or {}
        question = str(context.get("question") or "").strip()
        question_detail = f" · {question[:48]}" if question else ""
        return (
            f"{display_date} 观心",
            f"{hexagram.get('name') or '--'} → {transformed.get('name') or '--'}{question_detail}",
        )

    def heaven_interpret(self, payload: dict[str, Any]) -> dict[str, Any]:
        mode = str(payload.get("mode") or "").strip()
        if mode not in {"trend", "fortune", "heart"}:
            raise ValueError("问天解读模式不正确。")
        trade_date = normalize_date(str(payload.get("trade_date") or date.today().isoformat()))
        prompt_version = HEAVEN_PROMPT_VERSIONS[mode]
        stale_fortune: dict[str, Any] | None = None
        if mode == "fortune":
            existing = self.database.latest_heaven_reading(
                self.current_user_id, "fortune", trade_date
            )
            if self._legacy_truncated_heaven_reading(existing):
                self.database.delete_heaven_reading(
                    self.current_user_id, int(existing["id"])
                )
                existing = None
            if existing and self.database.heaven_reading_interpretation_version(
                self.current_user_id, int(existing["id"])
            ) == prompt_version:
                return {
                    "answer": existing["answer"],
                    "mode": mode,
                    "compiler": "stored",
                    "notice": "",
                    "reading": existing,
                    "reused": True,
                }
            stale_fortune = existing
        if mode in {"trend", "fortune"}:
            setup = self.heaven_setup(
                trade_date,
                str(payload.get("sector") or ""),
                str(payload.get("stock_code") or ""),
                payload.get("manual_data"),
            )
            if mode == "trend":
                chart = setup["chart"]
                if not chart.get("available"):
                    issues = "；".join((chart.get("quality") or {}).get("issues") or [])
                    raise ValueError(f"观势数据未通过六爻校验，暂不解势：{issues}")
                hexagram_context = json.loads(json.dumps(chart["hexagram"], ensure_ascii=False))
                for line in hexagram_context.get("lines", []):
                    line.pop("evidence", None)
                    line.pop("score", None)
                    line.pop("talent", None)
                    line.pop("layer", None)
                    line.pop("role", None)
                    if not line.get("moving"):
                        line.pop("text", None)
                        line.pop("image", None)
                        line.pop("line_name", None)
                context = {
                    "data_trade_date": setup["trade_date"],
                    "selected_focus": {
                        "sector": chart.get("sector") or "",
                        "stock": chart.get("stock") or {},
                    },
                    "hexagram": hexagram_context,
                    "movement": chart.get("movement") or {},
                }
            else:
                personal_profile = self.account_personal_field(
                    setup["calendar_date"],
                    setup["field"],
                    public=False,
                )
                context = {
                    "calendar_date": setup["calendar_date"],
                    "five_phase_field": setup["field"],
                    "personal_profile": personal_profile,
                }
                context_date = setup["calendar_date"]
            if mode == "trend":
                context_date = setup["trade_date"]
        else:
            question = str(payload.get("question") or "").strip()
            if len(question) > 300:
                raise ValueError("观心问题不能超过300个字符。")
            question_preset = str(payload.get("question_preset") or "unthemed").strip()
            if question_preset not in {"trade", "mind", "unthemed", "custom"}:
                question_preset = "custom"
            if not question:
                question = "不设具体问题，只观此刻一念。"
                question_preset = "unthemed"
            raw_lines = payload.get("lines")
            hexagram = self.heaven_hexagram(raw_lines)
            context = {
                "question": question,
                "question_preset": question_preset,
                "hexagram": hexagram,
                "six_yao": build_six_yao_chart(
                    [int(value) for value in raw_lines],
                    str(payload.get("cast_at") or ""),
                ),
                "ritual": {
                    "breathing": "用户已完成1秒准备与五轮吸3秒、顿2秒、呼4秒的静心呼吸。",
                    "casting": "用户以三枚铜钱自初爻至上爻投掷六次。",
                    "reflection": "用户已在看见卦象后察看第一念。",
                },
            }
            context_date = trade_date
        agent_context = prepare_heaven_context(mode, context)
        agent_context["interpretation_version"] = prompt_version
        result, compiler = self._call_heaven_agent(mode, agent_context)
        subject, subject_detail = self._heaven_reading_identity(
            mode, context_date, context
        )
        dedupe_key = (
            f"fortune:{context_date}"
            if mode == "fortune"
            else f"{mode}:{context_date}:{secrets.token_urlsafe(12)}"
        )
        if stale_fortune:
            self.database.delete_heaven_reading(
                self.current_user_id, int(stale_fortune["id"])
            )
        reading = self.database.save_heaven_reading(
            self.current_user_id,
            mode,
            context_date,
            subject,
            subject_detail,
            str(result.get("answer") or ""),
            agent_context,
            dedupe_key,
        )
        return {
            **result,
            "mode": mode,
            "compiler": compiler,
            "notice": "当前智能服务繁忙，已自动切换备用服务。" if compiler == "fallback" else "",
            "reading": reading,
            "reused": False,
        }

    @staticmethod
    def _legacy_truncated_heaven_reading(reading: dict[str, Any] | None) -> bool:
        return bool(reading and str(reading.get("answer") or "").rstrip().endswith("……"))

    def _call_heaven_agent(self, mode: str, context: dict[str, Any]) -> tuple[dict[str, Any], str]:
        prompt_version = HEAVEN_PROMPT_VERSIONS[mode]
        result = self.llm_gateway.call(
            f"heaven_{mode}",
            prompt_version,
            lambda profile: interpret_heaven(
                mode,
                context,
                profile.api_key,
                profile.base_url,
                profile.model,
            ),
            (HeavenAgentError,),
        )
        return result.value, result.role
