import json
import time
from pathlib import Path
from typing import Any, Dict, List, Optional
from uuid import uuid4

from src.config.config import global_config


class PlanReplyLogger:
    """独立的Plan/Reply日志记录器，负责落盘和容量控制。"""

    _BASE_DIR = Path("logs")
    _PLAN_DIR = _BASE_DIR / "plan"
    _REPLY_DIR = _BASE_DIR / "reply"
    _TRIM_COUNT = 100

    @classmethod
    def _get_max_per_chat(cls) -> int:
        """从配置中获取每个聊天流最大保存的日志数量"""
        return getattr(global_config.chat, "plan_reply_log_max_per_chat", 1000)

    @classmethod
    def log_plan(
        cls,
        chat_id: str,
        prompt: str,
        reasoning: str,
        raw_output: Optional[str],
        raw_reasoning: Optional[str],
        actions: List[Any],
        timing: Optional[Dict[str, Any]] = None,
        extra: Optional[Dict[str, Any]] = None,
    ) -> None:
        payload = {
            "type": "plan",
            "chat_id": chat_id,
            "timestamp": time.time(),
            "prompt": prompt,
            "reasoning": reasoning,
            "raw_output": raw_output,
            "raw_reasoning": raw_reasoning,
            "actions": [cls._serialize_action(action) for action in actions],
            "timing": timing or {},
            "extra": cls._safe_data(extra),
        }
        cls._write_json(cls._PLAN_DIR, chat_id, payload)

    @classmethod
    def log_reply(
        cls,
        chat_id: str,
        prompt: str,
        output: Optional[str],
        processed_output: Optional[List[Any]],
        model: Optional[str],
        timing: Optional[Dict[str, Any]] = None,
        reasoning: Optional[str] = None,
        think_level: Optional[int] = None,
        error: Optional[str] = None,
        success: bool = True,
    ) -> None:
        payload = {
            "type": "reply",
            "chat_id": chat_id,
            "timestamp": time.time(),
            "prompt": prompt,
            "output": output,
            "processed_output": cls._safe_data(processed_output),
            "model": model,
            "reasoning": reasoning,
            "think_level": think_level,
            "timing": timing or {},
            "error": error if not success else None,
            "success": success,
        }
        cls._write_json(cls._REPLY_DIR, chat_id, payload)

    @classmethod
    def _write_json(cls, base_dir: Path, chat_id: str, payload: Dict[str, Any]) -> None:
        chat_dir = base_dir / chat_id
        chat_dir.mkdir(parents=True, exist_ok=True)
        file_path = chat_dir / f"{int(time.time() * 1000)}_{uuid4().hex[:8]}.json"
        try:
            with file_path.open("w", encoding="utf-8") as f:
                json.dump(cls._safe_data(payload), f, ensure_ascii=False, indent=2)
        finally:
            cls._trim_overflow(chat_dir)

    @classmethod
    def _trim_overflow(cls, chat_dir: Path) -> None:
        """超过阈值时删除最老的若干文件，避免目录无限增长。"""
        files = sorted(chat_dir.glob("*.json"), key=lambda p: p.stat().st_mtime)
        max_per_chat = cls._get_max_per_chat()
        if len(files) <= max_per_chat:
            return
        # 删除最老的 TRIM_COUNT 条
        for old_file in files[: cls._TRIM_COUNT]:
            try:
                old_file.unlink()
            except FileNotFoundError:
                continue

    @classmethod
    def _serialize_action(cls, action: Any) -> Dict[str, Any]:
        # ActionPlannerInfo 结构的轻量序列化，避免引用复杂对象
        message_info = None
        action_message = getattr(action, "action_message", None)
        if action_message:
            user_info = getattr(action_message, "user_info", None)
            message_info = {
                "message_id": getattr(action_message, "message_id", None),
                "user_id": getattr(user_info, "user_id", None) if user_info else None,
                "platform": getattr(user_info, "platform", None) if user_info else None,
                "text": getattr(action_message, "processed_plain_text", None),
            }

        return {
            "action_type": getattr(action, "action_type", None),
            "reasoning": getattr(action, "reasoning", None),
            "action_data": cls._safe_data(getattr(action, "action_data", None)),
            "action_message": message_info,
            "available_actions": cls._safe_data(getattr(action, "available_actions", None)),
            "action_reasoning": getattr(action, "action_reasoning", None),
        }

    @classmethod
    def _safe_data(cls, value: Any) -> Any:
        if isinstance(value, (str, int, float, bool)) or value is None:
            return value
        if isinstance(value, dict):
            return {str(k): cls._safe_data(v) for k, v in value.items()}
        if isinstance(value, (list, tuple, set)):
            return [cls._safe_data(v) for v in value]
        if isinstance(value, Path):
            return str(value)
        # Fallback to string for other complex types
        return str(value)

