from __future__ import annotations

import json
from datetime import date
from typing import Any

from app_config import normalize_date, validate_stock_code, validate_text
from backend.database.repositories import TradeJournalRepository


TRADE_ACTIONS = {"buy": "买入", "sell": "卖出", "trim": "减仓", "add": "加仓", "watch": "观察"}
EMOTIONS = {"calm": "平静", "confident": "笃定", "hesitant": "犹豫", "anxious": "焦虑", "impulsive": "冲动"}


class TradeJournalService:
    def __init__(self, repository: TradeJournalRepository) -> None:
        self.repository = repository

    def save(self, user_id: int, payload: dict[str, Any]) -> int:
        trade_id = int(payload.get("id") or 0)
        trade_date = normalize_date(str(payload.get("trade_date") or date.today().isoformat()))
        code = validate_stock_code(str(payload.get("code") or ""))
        name = validate_text(payload.get("name"), "股票名称", 40, required=True)
        action = str(payload.get("action") or "")
        if action not in TRADE_ACTIONS:
            raise ValueError("交易动作不支持。")
        emotion = str(payload.get("emotion") or "calm")
        if emotion not in EMOTIONS:
            raise ValueError("交易情绪不支持。")
        price = self._number(payload.get("price"), "成交价格", 0, 1000000, required=True)
        quantity = int(self._number(payload.get("quantity"), "成交数量", 0, 100000000))
        position_pct = self._number(payload.get("position_pct"), "仓位", 0, 100)
        pnl_amount = self._optional_number(payload.get("pnl_amount"), "盈亏金额", -1e12, 1e12)
        pnl_pct = self._optional_number(payload.get("pnl_pct"), "盈亏比例", -1000, 10000)
        thesis = validate_text(payload.get("thesis"), "交易逻辑", 2000)
        execution = validate_text(payload.get("execution"), "执行复核", 2000)
        raw_tags = payload.get("tags") or []
        if isinstance(raw_tags, str):
            raw_tags = [item.strip() for item in raw_tags.replace("，", ",").split(",")]
        if not isinstance(raw_tags, list):
            raise ValueError("交易标签格式不正确。")
        tags = [validate_text(item, "交易标签", 20) for item in raw_tags if str(item).strip()][:8]
        return self.repository.save_trade_entry(
            user_id, trade_date, code, name, action, price, quantity, position_pct,
            pnl_amount, pnl_pct, thesis, execution, emotion, tags, trade_id or None,
        )

    def list_entries(
        self, user_id: int, start_date: str = "", end_date: str = "", code: str = ""
    ) -> dict[str, Any]:
        start = normalize_date(start_date) if start_date else ""
        end = normalize_date(end_date) if end_date else date.today().strftime("%Y%m%d")
        if start and start > end:
            raise ValueError("开始日期不能晚于结束日期。")
        code = validate_stock_code(code) if code else ""
        items = self.repository.list_trade_entries(user_id, start, end, code)
        for item in items:
            item["tags"] = json.loads(item.get("tags") or "[]")
            item["action_label"] = TRADE_ACTIONS.get(item["action"], item["action"])
            item["emotion_label"] = EMOTIONS.get(item["emotion"], item["emotion"])
        realized = [item for item in items if item.get("pnl_pct") is not None]
        return {"items": items, "summary": self._summary(items, realized)}

    def delete(self, user_id: int, trade_id: int) -> bool:
        return self.repository.delete_trade_entry(user_id, trade_id)

    @staticmethod
    def _summary(items: list[dict[str, Any]], realized: list[dict[str, Any]]) -> dict[str, Any]:
        pnl_amounts = [float(item["pnl_amount"]) for item in realized if item.get("pnl_amount") is not None]
        positions = [float(item["position_pct"]) for item in items if float(item.get("position_pct") or 0) > 0]
        wins = sum(float(item.get("pnl_pct") or 0) > 0 for item in realized)
        return {
            "total": len(items),
            "realized": len(realized),
            "win_rate": round(wins / len(realized) * 100, 1) if realized else None,
            "pnl_amount": round(sum(pnl_amounts), 2) if pnl_amounts else None,
            "average_position": round(sum(positions) / len(positions), 1) if positions else None,
        }

    @staticmethod
    def _number(value: Any, label: str, minimum: float, maximum: float, required: bool = False) -> float:
        if value in (None, ""):
            if required:
                raise ValueError(f"{label}不能为空。")
            return 0.0
        try:
            parsed = float(value)
        except (TypeError, ValueError) as exc:
            raise ValueError(f"{label}格式不正确。") from exc
        if parsed < minimum or parsed > maximum:
            raise ValueError(f"{label}超出允许范围。")
        return parsed

    @classmethod
    def _optional_number(
        cls, value: Any, label: str, minimum: float, maximum: float
    ) -> float | None:
        if value in (None, ""):
            return None
        return cls._number(value, label, minimum, maximum, required=True)
