"""DebateRunnerMixin — 辩论 5 阶段执行（开场/论点/小结/裁决）。

# TYPE_CHECKING: 由 TeamOrchestrator 组合，访问 self 共享状态
"""

from __future__ import annotations

import asyncio
import json
import logging
import re
from typing import TYPE_CHECKING

from .expert import Expert
from .plan import PhaseStatus, PlanPhase, TeamPlan

if TYPE_CHECKING:
    from .team import ExpertTeam

logger = logging.getLogger(__name__)


class DebateRunnerMixin:
    """Mixin: Lead-facilitated structured debate (5 stages). 由 TeamOrchestrator 组合。"""

    # Shared state provided by TeamOrchestrator (annotations only)
    _team: ExpertTeam
    _phase_semaphore: asyncio.Semaphore
    MAX_DEBATE_ROUNDS: int

    async def _execute_debate_phase(self, phase: PlanPhase, plan: TeamPlan) -> dict[str, object]:
        """Execute a DEBATE phase: Lead-facilitated structured debate (5 stages).
        Parse config → Lead opens → experts argue in parallel rounds → Lead
        summarizes → Lead adjudicates → write conclusion to workspace."""
        config = phase.debate_config or {}
        topic = config.get("topic", phase.task_description)
        participants: list[str] = config.get("participants", [])
        max_rounds = min(config.get("max_rounds", 2), self.MAX_DEBATE_ROUNDS)

        # Escape hatch: skip debate entirely
        if config.get("skip", False):
            logger.info(f"Debate phase {phase.id} skipped (skip=True)")
            phase.status = PhaseStatus.COMPLETED
            result = {"content": "无需辩论", "skipped": True}
            phase.result = result
            await self._broadcast_event(
                "debate_resolved",
                {
                    "phase_id": phase.id,
                    "phase_name": phase.name,
                    "decision": "skipped",
                    "conclusion": "无需辩论",
                    "rationale": "debate_config.skip=True",
                },
            )
            return result

        lead = self._team.lead_expert
        if not lead or not lead.is_active:
            active = self._team.active_experts
            if not active:
                raise RuntimeError("No active expert available for debate")
            lead = active[0]

        # Resolve participant experts (filter to active ones)
        debate_experts: list[Expert] = []
        for name in participants:
            expert = self._team.get_expert(name)
            if expert and expert.is_active and expert.config.name != lead.config.name:
                debate_experts.append(expert)

        phase.status = PhaseStatus.RUNNING

        # 1. Lead opens the debate
        opening = await self._generate_debate_opening(lead, topic, phase, plan)
        await self._broadcast_event(
            "debate_started",
            {
                "phase_id": phase.id,
                "phase_name": phase.name,
                "topic": topic,
                "participants": [e.config.name for e in debate_experts],
                "max_rounds": max_rounds,
                "opening": opening,
            },
        )

        # Debate history for context (Lead opening + expert arguments + Lead summaries)
        history: list[dict[str, object]] = [
            {"expert": lead.config.name, "content": opening, "round": 0, "role": "moderator"}
        ]

        # 2. Debate rounds
        for round_num in range(1, max_rounds + 1):
            # Check for user intervention (/stop)
            interventions = self._consume_team_interventions()
            if self._has_stop_command(interventions):
                logger.info(f"Debate {phase.id} stopped by user at round {round_num}")
                break

            if not debate_experts:
                # No participants — Lead directly adjudicates
                break

            # Experts argue in parallel (with concurrency limit)
            async def _bounded_debate(e: object) -> str:
                async with self._phase_semaphore:
                    return await self._generate_debate_argument(e, topic, history, round_num)

            speech_results = await asyncio.gather(
                *[_bounded_debate(e) for e in debate_experts],
                return_exceptions=True,
            )

            for expert, speech in zip(debate_experts, speech_results):
                if isinstance(speech, Exception):
                    logger.warning(
                        f"Expert '{expert.config.name}' debate argument failed: {speech}"
                    )
                    continue
                history.append(
                    {
                        "expert": expert.config.name,
                        "content": speech,
                        "round": round_num,
                        "role": "expert",
                    }
                )
                await self._broadcast_event(
                    "expert_argument",
                    {
                        "phase_id": phase.id,
                        "expert_id": expert.config.name,
                        "expert_name": expert.config.name,
                        "expert_color": expert.config.color,
                        "content": speech,
                        "round": round_num,
                        "topic": topic,
                    },
                )

            # Lead summarizes the round
            summary = await self._generate_debate_summary(lead, topic, history, round_num)
            if summary:
                history.append(
                    {
                        "expert": lead.config.name,
                        "content": summary,
                        "round": round_num,
                        "role": "moderator",
                    }
                )
                await self._broadcast_event(
                    "debate_round_summary",
                    {
                        "phase_id": phase.id,
                        "moderator_name": lead.config.name,
                        "content": summary,
                        "round": round_num,
                        "continue": round_num < max_rounds,
                    },
                )

        # 3. Lead adjudicates
        verdict = await self._generate_debate_verdict(lead, topic, history)
        conclusion = verdict.get("conclusion", "")
        decision = verdict.get("decision", "inconclusive")

        await self._broadcast_event(
            "debate_resolved",
            {
                "phase_id": phase.id,
                "phase_name": phase.name,
                "decision": decision,
                "conclusion": conclusion,
                "rationale": verdict.get("rationale", ""),
            },
        )

        # 4. Write conclusion to SharedWorkspace
        result = {"content": conclusion, "verdict": verdict, "decision": decision}
        phase.status = PhaseStatus.COMPLETED
        phase.result = result

        output_key = f"{plan.id}/phase/{phase.id}/output"
        await self._team.workspace.write(output_key, conclusion, lead.config.name)

        # Emit phase_completed event (consistent with execution phases)
        result_summary = conclusion[:200] if len(conclusion) > 200 else conclusion
        await self._broadcast_event(
            "phase_completed",
            {
                "phase_id": phase.id,
                "phase_name": phase.name,
                "result_summary": result_summary,
            },
        )

        return result

    async def _generate_debate_opening(
        self, lead: Expert, topic: str, phase: PlanPhase, plan: TeamPlan
    ) -> str:
        """Generate Lead's opening statement for the debate."""
        gateway = self._get_llm_gateway(lead)
        if not gateway:
            return f"辩论主题：{topic}。请各位专家发表看法。"

        dep_context = self._build_dependency_context(phase, plan)

        prompt = (
            f"你是团队 Lead {lead.config.name}，正在主持一场结构化辩论。\n\n"
            f"辩论主题：{topic}\n"
            f"阶段任务：{phase.task_description}\n"
        )
        if dep_context:
            prompt += f"\n前置阶段产出：\n{dep_context}\n"
        prompt += (
            "\n请作为主持人开场：\n"
            "- 明确陈述分歧点或需要辩论的核心问题\n"
            "- 提供必要的上下文（来自前置阶段的产出）\n"
            "- 邀请参与专家发表立场\n"
            "- 保持简洁，3-5 句话\n"
        )

        try:
            response = await gateway.chat(
                messages=[{"role": "user", "content": prompt}],
                model=self._get_model(lead),
            )
            return response.content.strip()
        except Exception as e:
            logger.warning(f"Debate opening generation failed: {e}")
            return f"辩论主题：{topic}。请各位专家发表看法。"

    async def _generate_debate_argument(
        self, expert: Expert, topic: str, history: list[dict[str, object]], round_num: int
    ) -> str:
        """Generate an expert's debate argument for the current round."""
        gateway = self._get_llm_gateway(expert)
        if not gateway:
            return f"[{expert.config.name} 因 LLM 不可用无法发言]"

        history_text = self._format_debate_history(history)

        prompt = (
            f"你是 {expert.config.name}，正在参加一场结构化辩论。\n\n"
            f"你的角色：{expert.config.persona}\n"
            f"你的思维风格：{expert.config.thinking_style}\n"
            f"你的表达风格：{expert.config.speaking_style}\n"
            f"你的决策框架：{expert.config.decision_framework}\n\n"
            f"辩论主题：{topic}\n"
            f"当前轮次：第 {round_num} 轮\n\n"
        )
        if history_text:
            prompt += f"辩论历史：\n{history_text}\n\n"
        prompt += (
            "请基于你的角色和决策框架，就辩论主题发表你的论点：\n"
            "- 明确你的立场（支持/反对/折中）\n"
            "- 给出你的论据和理由\n"
            "- 可以引用或反驳之前发言者的观点\n"
            "- 2-4 段话，简洁有力\n"
        )

        response = await gateway.chat(
            messages=[{"role": "user", "content": prompt}],
            model=self._get_model(expert),
        )
        return response.content.strip()

    async def _generate_debate_summary(
        self, lead: Expert, topic: str, history: list[dict[str, object]], round_num: int
    ) -> str:
        """Generate Lead's summary of the current debate round."""
        gateway = self._get_llm_gateway(lead)
        if not gateway:
            return f"[第 {round_num} 轮辩论小结因 LLM 不可用无法生成]"

        round_entries = [
            h for h in history if h.get("round") == round_num and h["role"] == "expert"
        ]
        if not round_entries:
            return ""

        round_text = "\n\n".join(f"[{h['expert']}]: {h['content']}" for h in round_entries)

        prompt = (
            f"你是团队 Lead {lead.config.name}，正在主持辩论。\n\n"
            f"辩论主题：{topic}\n"
            f"当前轮次：第 {round_num} 轮\n\n"
            f"本轮专家论点：\n{round_text}\n\n"
            "请小结本轮辩论：\n"
            "- 归纳各方核心论点（2-3 句话）\n"
            "- 指出共识点和分歧点\n"
            "- 提示下一轮可以深入的方向\n"
            "- 保持简洁，3-5 句话\n"
        )

        try:
            response = await gateway.chat(
                messages=[{"role": "user", "content": prompt}],
                model=self._get_model(lead),
            )
            return response.content.strip()
        except Exception as e:
            logger.warning(f"Debate summary generation failed: {e}")
            return f"[第 {round_num} 轮辩论完成，小结生成失败]"

    async def _generate_debate_verdict(
        self, lead: Expert, topic: str, history: list[dict[str, object]]
    ) -> dict[str, object]:
        """Generate Lead's final verdict for the debate."""
        gateway = self._get_llm_gateway(lead)
        if not gateway:
            return {
                "decision": "inconclusive",
                "rationale": "LLM 不可用",
                "conclusion": f"辩论主题：{topic}。因 LLM 不可用，无法生成裁决。",
            }

        history_text = self._format_debate_history(history)

        prompt = (
            f"你是团队 Lead {lead.config.name}，需要为这场辩论做出最终裁决。\n\n"
            f"辩论主题：{topic}\n\n"
            f"完整辩论历史：\n{history_text}\n\n"
            "请给出最终裁决。输出 JSON 格式：\n"
            "```json\n"
            "{\n"
            '  "decision": "adopt|compromise|shelve|inconclusive",\n'
            '  "rationale": "裁决理由，2-3 句话",\n'
            '  "conclusion": "最终结论，作为下一阶段的输入"\n'
            "}\n"
            "```\n"
            "decision 含义：\n"
            "- adopt: 采纳某方观点\n"
            "- compromise: 折中方案\n"
            "- shelve: 搁置争议，后续再议\n"
            "- inconclusive: 无法裁决\n"
            "只输出 JSON，不要其他文字。"
        )

        try:
            response = await gateway.chat(
                messages=[{"role": "user", "content": prompt}],
                model=self._get_model(lead),
            )
            content = response.content.strip()

            # Extract JSON from response
            json_match = re.search(r"\{.*\}", content, re.DOTALL)
            if json_match:
                result = json.loads(json_match.group(0))
                return {
                    "decision": result.get("decision", "inconclusive"),
                    "rationale": result.get("rationale", ""),
                    "conclusion": result.get("conclusion", content),
                }

            # JSON parsing failed — return raw content as conclusion
            return {
                "decision": "inconclusive",
                "rationale": "JSON 解析失败",
                "conclusion": content,
            }
        except Exception as e:
            logger.warning(f"Debate verdict generation failed: {e}")
            return {
                "decision": "inconclusive",
                "rationale": f"裁决生成失败: {e}",
                "conclusion": f"辩论主题：{topic}。裁决生成失败，建议参考辩论历史自行判断。",
            }

    def _format_debate_history(self, history: list[dict[str, object]]) -> str:
        """Format debate history as readable text for LLM prompts."""
        if not history:
            return ""
        lines = []
        for h in history:
            role_tag = "主持人" if h.get("role") == "moderator" else "专家"
            round_tag = f"[第{h['round']}轮]" if h.get("round", 0) > 0 else "[开场]"
            lines.append(f"{round_tag} {role_tag} {h['expert']}:\n{h['content']}")
        return "\n\n".join(lines)

    def _build_dependency_context(self, phase: PlanPhase, plan: TeamPlan) -> str:
        """Build context text from dependency phase outputs for debate prompts."""
        if not phase.depends_on:
            return ""
        parts = []
        for dep_id in phase.depends_on:
            dep_phase = plan.get_phase(dep_id)
            if dep_phase and dep_phase.status == PhaseStatus.COMPLETED and dep_phase.result:
                content = dep_phase.result.get("content", str(dep_phase.result))
                parts.append(f"[{dep_phase.name}]:\n{content[:500]}")
        return "\n---\n".join(parts) if parts else ""
