"""
阴阳师抽卡插件 - xapi 数据管理模块。

本模块只负责调用 xapi /bot/gacha 运行时 API。抽卡概率、奖励发放和 QQ 消息编排
仍由 nonebot 插件本地负责。
"""

from __future__ import annotations

import asyncio
import datetime
import logging
from typing import Any, Dict, List, Optional

import aiohttp

from .config import Config

logger = logging.getLogger(__name__)
config = Config()


class DataManager:
    """抽卡数据管理器，封装 /bot/gacha HTTP 调用。"""

    def __init__(self):
        self.shikigami_data: Dict[str, List[Dict[str, Any]]] = {"R": [], "SR": [], "SSR": [], "SP": []}

    def _url(self, path: str) -> str:
        """拼接 /bot/gacha 端点地址。"""

        return f"{config.GACHA_API_HOST}/{path.lstrip('/')}"

    def _auth(self) -> Dict[str, str]:
        """生成 xapi Bot 鉴权参数。"""

        return {
            "user": config.BOT_USER_ID,
            "token": config.BOT_TOKEN,
        }

    async def _request(
        self,
        method: str,
        path: str,
        *,
        payload: Optional[Dict[str, Any]] = None,
        params: Optional[Dict[str, Any]] = None,
    ) -> Optional[Dict[str, Any]]:
        """调用 xapi /bot/gacha，并只向上层暴露 data。"""

        request_url = self._url(path)
        timeout = aiohttp.ClientTimeout(total=10)
        try:
            async with aiohttp.ClientSession() as session:
                if method == "GET":
                    request_params = {**self._auth(), **(params or {})}
                    async with session.get(request_url, params=request_params, timeout=timeout) as resp:
                        return await self._parse_response(resp, path)
                request_payload = {**self._auth(), **(payload or {})}
                async with session.post(request_url, json=request_payload, timeout=timeout) as resp:
                    return await self._parse_response(resp, path)
        except aiohttp.ClientError as exc:
            logger.error("gacha api request failed path=%s error=%s", path, exc)
            return None
        except asyncio.TimeoutError as exc:
            logger.error("gacha api request timeout path=%s error=%s", path, exc)
            return None

    async def _parse_response(self, resp: aiohttp.ClientResponse, path: str) -> Optional[Dict[str, Any]]:
        """解析 xapi 统一响应，失败时返回 None 维持旧调用方失败语义。"""

        if resp.status != 200:
            logger.error("gacha api bad status path=%s status=%s", path, resp.status)
            return None
        body = await resp.json()
        if body.get("code") != 200:
            logger.error("gacha api fail path=%s code=%s message=%s", path, body.get("code"), body.get("message"))
            return None
        data = body.get("data")
        return data if isinstance(data, dict) else None

    async def refresh_shikigami_data(self) -> Dict[str, List[Dict[str, Any]]]:
        """从 xapi 拉取式神基础数据并按稀有度缓存。"""

        data = await self._request("GET", "shikigami")
        items = data.get("items", []) if data else []
        grouped: Dict[str, List[Dict[str, Any]]] = {"R": [], "SR": [], "SSR": [], "SP": []}
        for item in items:
            rarity = item.get("rarity")
            if rarity not in grouped:
                continue
            image_path = item.get("image_path") or item.get("image_url") or ""
            grouped[rarity].append(
                {
                    "id": item.get("id"),
                    "name": item.get("name"),
                    "rarity": rarity,
                    "image_path": image_path,
                    "image_url": image_path,
                }
            )
        self.shikigami_data = grouped
        return self.shikigami_data

    async def ensure_shikigami_data(self) -> Dict[str, List[Dict[str, Any]]]:
        """确保式神缓存已加载。"""

        if not any(self.shikigami_data.values()):
            await self.refresh_shikigami_data()
        return self.shikigami_data

    def get_today_date(self) -> str:
        """获取当前日期字符串。"""

        return datetime.datetime.now().strftime("%Y-%m-%d")

    def get_current_time(self) -> str:
        """获取当前时间字符串。"""

        return datetime.datetime.now().strftime("%H:%M:%S")

    def _find_shikigami(self, rarity: str, shikigami_name: str) -> Optional[Dict[str, Any]]:
        """从本地缓存查找 xapi 托管式神。"""

        for item in self.shikigami_data.get(rarity, []):
            if item.get("name") == shikigami_name:
                return item
        return None

    async def get_draws_left(self, user_id: str) -> int:
        """获取用户今日剩余抽卡次数。"""

        data = await self._request("GET", "draws-left", params={"user_id": user_id})
        if data is None:
            return 0
        return int(data.get("draws_left", 0) or 0)

    async def check_daily_limit(self, user_id: str) -> bool:
        """检查用户是否还有抽卡次数。"""

        return await self.get_draws_left(user_id) > 0

    async def record_draw_result(self, user_id: str, rarity: str, shikigami: Dict[str, Any]) -> Dict[str, Any]:
        """写入一次抽卡并返回 xapi 原始业务结果。"""

        data = await self._request(
            "POST",
            "draw",
            payload={
                "user_id": user_id,
                "shikigami_id": int(shikigami["id"]),
                "rarity": rarity,
                "name": shikigami["name"],
            },
        )
        if data is None:
            return {"success": False, "message": "抽卡记录写入失败"}
        return data

    async def record_triple_draw_result(self, user_id: str, draws: List[Dict[str, Any]]) -> Dict[str, Any]:
        """写入三连抽并返回 xapi 原始业务结果。"""

        payload_draws = [
            {
                "shikigami_id": int(item["id"]),
                "rarity": item["rarity"],
                "name": item["name"],
            }
            for item in draws
        ]
        data = await self._request("POST", "draw/triple", payload={"user_id": user_id, "draws": payload_draws})
        if data is None:
            return {"success": False, "message": "三连抽记录写入失败"}
        return data

    async def record_draw(self, user_id: str, rarity: str, shikigami_name: str) -> List[str]:
        """记录一次抽卡，返回新解锁的成就列表。"""

        await self.ensure_shikigami_data()
        shikigami = self._find_shikigami(rarity, shikigami_name)
        if not shikigami:
            logger.error("找不到式神: %s (%s)", shikigami_name, rarity)
            return []
        result = await self.record_draw_result(user_id, rarity, shikigami)
        if not result.get("success"):
            logger.error("抽卡记录写入失败 user_id=%s message=%s", user_id, result.get("message"))
            return []
        return result.get("unlocked_achievements", [])

    async def record_sign_in(self, user_id: str, points_awarded: int) -> bool:
        """记录每日签到，重复签到返回 False。"""

        data = await self._request(
            "POST",
            "sign-in",
            payload={"user_id": user_id, "points_awarded": points_awarded},
        )
        if data is None:
            return False
        return bool(data.get("success")) and not bool(data.get("signed_already"))

    async def get_user_stats(self, user_id: str) -> Dict[str, Any]:
        """获取用户抽卡统计。"""

        data = await self._request("GET", "user-stats", params={"user_id": user_id})
        return data or {"success": False, "message": "您还没有抽卡记录哦！"}

    async def get_daily_stats(self, date: Optional[str] = None) -> Dict[str, Any]:
        """获取指定日期抽卡统计。"""

        params = {"date": date} if date else {}
        data = await self._request("GET", "daily-stats", params=params)
        return data or {"success": False, "message": "今日还没有人抽卡哦！"}

    async def get_rank(self, limit: int = 10) -> List[Dict[str, Any]]:
        """获取抽卡排行榜。"""

        data = await self._request("GET", "rank", params={"limit": max(1, min(100, limit))})
        if data is None:
            return []
        items = data.get("items", [])
        return items if isinstance(items, list) else []

    async def get_user_achievements(self, user_id: str) -> Dict[str, Any]:
        """获取用户成就信息。"""

        data = await self._request("GET", f"achievements/{user_id}")
        if data is None:
            return {
                "unlocked": {},
                "progress": {
                    "consecutive_days": 0,
                    "no_ssr_streak": 0,
                    "total_consecutive_days": 0,
                },
            }
        return {
            "unlocked": data.get("achievements", {}),
            "progress": data.get("progress", {}),
        }

    async def claim_achievement_reward(self, user_id: str, achievement_id: str) -> bool:
        """标记成就奖励已领取。"""

        data = await self._request("POST", f"achievements/{user_id}/claim", payload={"achievement_id": achievement_id})
        return bool(data and data.get("success"))

    async def get_daily_records(self, date: Optional[str] = None) -> Dict[str, Any]:
        """获取每日详细抽卡记录。"""

        params = {"date": date} if date else {}
        data = await self._request("GET", "records/daily", params=params)
        return data or {"success": False, "date": date or self.get_today_date(), "records": [], "total_count": 0}

    async def get_daily_draws(self, date: Optional[str] = None) -> Dict[str, Dict[str, List[Dict[str, str]]]]:
        """按旧结构返回每日抽卡记录。"""

        data = await self.get_daily_records(date)
        result: Dict[str, Dict[str, List[Dict[str, str]]]] = {}
        if not data.get("success"):
            return result
        target_date = data.get("date") or date or self.get_today_date()
        result[target_date] = {}
        for record in data.get("records", []):
            user_id = record.get("user_id")
            if not user_id:
                continue
            result[target_date].setdefault(user_id, []).append(
                {
                    "rarity": record.get("rarity", ""),
                    "name": record.get("shikigami_name", ""),
                    "timestamp": record.get("draw_time", ""),
                }
            )
        return result

    async def has_signed_in_today(self, user_id: str) -> bool:
        """保留旧方法名；当前无独立查询端点，签到去重由 xapi sign-in 写接口处理。"""

        return False

    def save_daily_draws(self, data: Dict[str, Dict[str, List[Dict[str, str]]]]) -> None:
        """兼容旧空方法，运行时不写本地文件。"""

        return None

    def save_user_stats(self, data: Dict[str, Dict[str, Any]]) -> None:
        """兼容旧空方法，运行时不写本地文件。"""

        return None
