"""子智能体状态管理 Mixin。"""

from __future__ import annotations

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

from utils.logger import setup_logger
from modules.multi_agent.debug_logger import ma_debug

logger = setup_logger(__name__)
TERMINAL_STATUSES = {"completed", "failed", "timeout"}


class SubAgentStateMixin:
    """提供子智能体任务状态加载、保存、刷新与结果落盘能力。"""

    state_file: Path
    tasks: Dict[str, Dict[str, Any]]
    conversation_agents: Dict[str, List[int]]
    _running_tasks: Dict[str, Any]
    _state_lock: Any

    def _load_state(self):
        with self._state_lock:
            if not self.state_file.exists():
                self.tasks = {}
                self.conversation_agents = {}
                return
            try:
                data = json.loads(self.state_file.read_text(encoding="utf-8"))
                loaded_tasks = data.get("tasks", {})
                loaded_agents = data.get("conversation_agents", {})
            except json.JSONDecodeError:
                logger.warning("子智能体状态文件损坏，已忽略。")
                self.tasks = {}
                self.conversation_agents = {}
                return

            runtime_only_keys = {"_stdout_lines"}
            merged_tasks: Dict[str, Dict] = {}
            for task_id, task in loaded_tasks.items():
                existing = self.tasks.get(task_id)
                if existing:
                    for key in runtime_only_keys:
                        if key in existing:
                            task[key] = existing[key]
                merged_tasks[task_id] = task
            self.tasks = merged_tasks
            self.conversation_agents = loaded_agents

            # 恢复多智能体运行态（如果状态文件包含）
            try:
                from modules.multi_agent.state import MultiAgentState

                manager = self
                multi_agent_states = getattr(manager, "multi_agent_states", None)
                if multi_agent_states is not None and isinstance(multi_agent_states, dict):
                    loaded_ma_states = data.get("multi_agent_states", {})
                    for conv_id, snapshot in loaded_ma_states.items():
                        try:
                            if isinstance(snapshot, dict):
                                # 关键：不要覆盖内存中已存在的 MultiAgentState，
                                # 否则 SubAgentTask 持有的旧引用上的 pending_master_messages
                                # 会被新的空 state 覆盖，导致子智能体输出丢失。
                                if conv_id in multi_agent_states:
                                    ma_debug(
                                        "load_state_skip_existing_ma_state",
                                        conversation_id=conv_id,
                                        existing_state_id=id(multi_agent_states[conv_id]),
                                    )
                                    continue
                                multi_agent_states[conv_id] = MultiAgentState.from_snapshot(snapshot)
                                ma_debug(
                                    "load_state_restore_ma_state",
                                    conversation_id=conv_id,
                                    state_id=id(multi_agent_states[conv_id]),
                                )
                        except Exception as exc:
                            logger.warning(f"恢复多智能体状态失败 {conv_id}: {exc}")
            except Exception as exc:
                logger.warning(f"加载多智能体状态失败: {exc}")

            if self.tasks:
                migrated = False
                for task in self.tasks.values():
                    if task.get("parent_conversation_id"):
                        continue
                    candidate = task.get("conversation_id") or (task.get("service_payload") or {}).get("parent_conversation_id")
                    if candidate:
                        task["parent_conversation_id"] = candidate
                        migrated = True
                if migrated:
                    self._save_state_unsafe()

    def _save_state(self):
        with self._state_lock:
            self._save_state_unsafe()

    def _save_state_unsafe(self):
        payload = {
            "tasks": self.tasks,
            "conversation_agents": self.conversation_agents,
        }
        # 多智能体运行态持久化
        try:
            manager = self
            multi_agent_states = getattr(manager, "multi_agent_states", None)
            if multi_agent_states:
                payload["multi_agent_states"] = {
                    conv_id: state.to_snapshot()
                    for conv_id, state in multi_agent_states.items()
                    if isinstance(state, object) and hasattr(state, "to_snapshot")
                }
        except Exception as exc:
            logger.warning(f"保存多智能体状态失败: {exc}")
        try:
            self.state_file.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
        except Exception as exc:
            logger.warning(f"保存子智能体状态失败: {exc}")

    def _check_task_status(self, task: Dict) -> Dict:
        """检查任务状态，读取输出文件。"""
        task_id = task["task_id"]
        output_file = Path(task.get("output_file", ""))
        if not output_file.exists():
            running_task = self._running_tasks.get(task_id)
            if running_task and running_task.done():
                try:
                    running_task.result(timeout=0)
                except Exception:
                    pass
                task["status"] = "failed"
                task["updated_at"] = time.time()
            return {"status": task.get("status", "running"), "task_id": task_id}

        try:
            output = json.loads(output_file.read_text(encoding="utf-8"))
        except Exception as exc:
            logger.warning(f"[check_task_status] output 文件解析失败: {exc}")
            task["status"] = "failed"
            task["updated_at"] = time.time()
            return {"success": False, "status": "failed", "task_id": task_id, "message": f"输出文件解析失败: {exc}"}

        success = output.get("success", False)
        summary = output.get("summary", "")
        stats = output.get("stats", {})
        elapsed_seconds = self._compute_elapsed_seconds(task)

        ma_debug(
            "check_task_status",
            task_id=task_id,
            agent_id=task.get("agent_id"),
            multi_agent_mode=task.get("multi_agent_mode"),
            output_status=output.get("status"),
            output_success=success,
        )

        # 多智能体模式：output 中 status 为 running/idle 表示子智能体仍在运行或
        # 本轮结束但上下文保留、可继续接收消息，都不是失败/完成。
        # 此时不生成 final_result，只保持原状态，避免 reconcile 把运行中误判为失败。
        if task.get("multi_agent_mode") and output.get("status") in {"running", "idle"}:
            task["status"] = output["status"]
            task["updated_at"] = time.time()
            ma_debug("check_task_status_keep_alive", task_id=task_id, status=output["status"])
            return {"status": output["status"], "task_id": task_id}

        if output.get("timeout"):
            status = "timeout"
        elif output.get("max_turns_exceeded"):
            status = "failed"
            summary = f"任务执行超过最大轮次限制。{summary}"
        elif success:
            status = "completed"
        else:
            status = "failed"

        ma_debug("check_task_status_result", task_id=task_id, agent_id=task.get("agent_id"), status=status)
        task["status"] = status
        task["updated_at"] = time.time()
        if status == "completed" and elapsed_seconds is not None:
            task["elapsed_seconds"] = elapsed_seconds
            task["runtime_seconds"] = elapsed_seconds

        agent_id = task.get("agent_id")
        task_summary = task.get("summary")
        deliverables_dir = task.get("deliverables_dir")
        stats_summary = self._build_stats_summary(stats)

        if status == "completed":
            system_message = self._compose_sub_agent_message(
                prefix=f"✅ 子智能体{agent_id} 任务摘要：{task_summary} 已完成。",
                stats_summary=stats_summary,
                summary=summary,
                deliverables_dir=deliverables_dir,
                duration_seconds=elapsed_seconds,
            )
        elif status == "timeout":
            system_message = self._compose_sub_agent_message(
                prefix=f"⏱️ 子智能体{agent_id} 任务摘要：{task_summary} 超时未完成。",
                stats_summary=stats_summary,
                summary=summary,
            )
        else:
            system_message = self._compose_sub_agent_message(
                prefix=f"❌ 子智能体{agent_id} 任务摘要：{task_summary} 执行失败。",
                stats_summary=stats_summary,
                summary=summary,
            )

        result = {
            "success": success,
            "status": status,
            "task_id": task_id,
            "agent_id": agent_id,
            "message": summary,
            "deliverables_dir": deliverables_dir,
            "stats": stats,
            "stats_summary": stats_summary,
            "system_message": system_message,
        }
        if status == "completed" and elapsed_seconds is not None:
            result["elapsed_seconds"] = elapsed_seconds
            result["runtime_seconds"] = elapsed_seconds
        task["final_result"] = result
        return result

    def _handle_timeout(self, task: Dict) -> Dict:
        """处理任务超时。"""
        task_id = task["task_id"]
        running_task = self._running_tasks.pop(task_id, None)
        if running_task and not running_task.done():
            running_task.cancel()
            deadline = time.time() + 5
            while not running_task.done() and time.time() < deadline:
                time.sleep(0.05)

        task["status"] = "timeout"
        task["updated_at"] = time.time()

        stats = {}
        stats_file = Path(task.get("stats_file", ""))
        if stats_file.exists():
            try:
                stats = json.loads(stats_file.read_text(encoding="utf-8"))
            except Exception:
                stats = {}
        stats_summary = self._build_stats_summary(stats)
        system_message = self._compose_sub_agent_message(
            prefix=f"⏱️ 子智能体{task.get('agent_id')} 任务摘要：{task.get('summary')} 超时未完成。",
            stats_summary=stats_summary,
            summary="等待超时，子智能体已被终止。",
        )

        result = {
            "success": False,
            "status": "timeout",
            "task_id": task_id,
            "agent_id": task.get("agent_id"),
            "message": "等待超时，子智能体已被终止。",
            "stats": stats,
            "stats_summary": stats_summary,
            "system_message": system_message,
        }
        task["final_result"] = result
        self._save_state()
        return result

    def _mark_task_terminated(
        self,
        task: Dict[str, Any],
        *,
        message: str,
        system_message: Optional[str] = None,
        notified: bool = False,
    ) -> Dict[str, Any]:
        task["status"] = "terminated"
        task["updated_at"] = time.time()
        if notified:
            task["notified"] = True
        result = {
            "success": False,
            "status": "terminated",
            "task_id": task.get("task_id"),
            "agent_id": task.get("agent_id"),
            "message": message,
            "system_message": system_message or message,
        }
        task["final_result"] = result
        return result

    def _mark_task_done(
        self,
        task_id: str,
        success: bool,
        summary: str,
        runtime_seconds: int,
    ) -> None:
        task = self.tasks.get(task_id)
        if not task:
            return
        # 如果 terminate_sub_agent 已把任务标为 terminated，下渗异步任务自然结束时
        # 不要被“已完成/fulfilled覆盖为failed”值覆盖，否则 task 被碰成 failed 但实际上
        # 用户手动终止已发生，状态语义错误且“successful=false”。
        existing_status = task.get("status")
        if existing_status in TERMINAL_STATUSES.union({"terminated"}):
            ma_debug(
                "mark_task_done_skip_already_terminal",
                task_id=task_id,
                existing_status=existing_status,
            )
            return
        status = "completed" if success else "failed"
        ma_debug(
            "mark_task_done",
            task_id=task_id,
            agent_id=task.get("agent_id"),
            success=success,
            new_status=status,
        )
        task["status"] = status
        task["updated_at"] = time.time()
        task["runtime_seconds"] = runtime_seconds
        self._check_task_status(task)
        self._save_state()

    def _refresh_task_runtime_state(self, task: Dict[str, Any]) -> Dict[str, Any]:
        """刷新单个任务运行态。"""
        status = task.get("status")
        task_id = task.get("task_id")
        agent_id = task.get("agent_id")
        multi_agent_flag = task.get("multi_agent_mode")
        ma_debug(
            "refresh_task_runtime_state_start",
            task_id=task_id,
            agent_id=agent_id,
            before_status=status,
            multi_agent_mode=multi_agent_flag,
        )
        if status in TERMINAL_STATUSES.union({"terminated"}):
            return {"status": status, "task_id": task_id}

        running_task = self._running_tasks.get(task_id) if task_id else None
        if running_task:
            if running_task.done():
                try:
                    running_task.result(timeout=0)
                except Exception:
                    pass
                # 多智能体模式：任务自然进入 idle 时不写输出文件，不应标记为失败
                if task.get("multi_agent_mode") and not Path(task.get("output_file", "")).exists():
                    task["status"] = "idle"
                    task["updated_at"] = time.time()
                    ma_debug("refresh_task_runtime_state_idle_no_output", task_id=task_id)
                    return {"status": "idle", "task_id": task_id}
                result = self._check_task_status(task)
                ma_debug("refresh_task_runtime_state_done_result", task_id=task_id, result=result)
                return result
            ma_debug("refresh_task_runtime_state_still_running", task_id=task_id)
            return {"status": "running", "task_id": task_id}

        output_file = Path(task.get("output_file", ""))
        if output_file.exists():
            result = self._check_task_status(task)
            ma_debug("refresh_task_runtime_state_output_result", task_id=task_id, result=result)
            return result

        # 多智能体模式：没有输出文件表示 idle，不强制清理
        if task.get("multi_agent_mode"):
            task["status"] = "idle"
            task["updated_at"] = time.time()
            ma_debug("refresh_task_runtime_state_idle_no_output_file", task_id=task_id)
            return {"status": "idle", "task_id": task_id}

        if self._should_force_cleanup_stale_task(task):
            return self._mark_task_terminated(
                task,
                message="子智能体疑似僵尸任务，已超时自动清理运行状态。",
                system_message="⚠️ 子智能体长时间未结束，系统已自动清理运行状态。",
                notified=True,
            )

        return self._mark_task_terminated(
            task,
            message="检测到子智能体任务已退出，已自动清理运行状态。",
            system_message="⚠️ 子智能体任务异常退出，系统已自动清理运行状态。",
            notified=True,
        )

    def reconcile_task_states(self, conversation_id: Optional[str] = None) -> int:
        """修正运行态任务状态，返回修正条目数。"""
        changed = 0
        ma_debug("reconcile_task_states_start", conversation_id=conversation_id, task_count=len(self.tasks))
        for task in self.tasks.values():
            if not isinstance(task, dict):
                continue
            if conversation_id and task.get("conversation_id") != conversation_id:
                continue
            before_status = task.get("status")
            before_notified = task.get("notified")
            self._refresh_task_runtime_state(task)
            after_status = task.get("status")
            if after_status != before_status or task.get("notified") != before_notified:
                ma_debug(
                    "reconcile_task_states_changed",
                    task_id=task.get("task_id"),
                    agent_id=task.get("agent_id"),
                    before_status=before_status,
                    after_status=after_status,
                )
                changed += 1
        if changed:
            self._save_state()
        ma_debug("reconcile_task_states_end", conversation_id=conversation_id, changed=changed)
        return changed

    def _should_force_cleanup_stale_task(self, task: Dict[str, Any]) -> bool:
        try:
            created_at = float(task.get("created_at") or 0)
        except (TypeError, ValueError):
            created_at = 0
        if created_at <= 0:
            return False
        # timeout_seconds 为 None 表示永久子智能体，不强制清理
        raw_timeout = task.get("timeout_seconds")
        if raw_timeout is None:
            return False
        from config import SUB_AGENT_DEFAULT_TIMEOUT
        timeout_seconds = int(raw_timeout or SUB_AGENT_DEFAULT_TIMEOUT or 0)
        timeout_seconds = max(timeout_seconds, 1)
        grace_seconds = 120
        elapsed = time.time() - created_at
        if elapsed <= (timeout_seconds + grace_seconds):
            return False
        output_file = Path(task.get("output_file", ""))
        if output_file.exists():
            return False
        progress_file = Path(task.get("progress_file", ""))
        if progress_file.exists():
            try:
                stale_span = time.time() - progress_file.stat().st_mtime
                if stale_span <= grace_seconds:
                    return False
            except Exception:
                pass
        return True
