"""DivergenceDetectorMixin — 分歧检测 + 动态辩论插入。

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

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

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

if TYPE_CHECKING:
    from .team import ExpertTeam

logger = logging.getLogger(__name__)


class DivergenceDetectorMixin:
    """Mixin: 检测阶段产出分歧 + 动态插入辩论阶段。由 TeamOrchestrator 组合。"""

    # Shared state provided by TeamOrchestrator (annotations only)
    _team: ExpertTeam
    _debate_count: int
    _checkpoint: object
    MAX_DEBATES: int

    async def _maybe_add_plan_review_debate(self, lead: Expert, plan: TeamPlan, task: str) -> None:
        """Optionally add a plan review debate phase before execution.

        Skips for simple tasks (<= 2 phases) or when LLM judges it unnecessary.
        When added, all existing phases depend on the debate phase so it runs first.
        """
        if len(plan.phases) <= 2:
            return  # Simple task, skip plan review

        if self._debate_count >= self.MAX_DEBATES:
            return

        gateway = self._get_llm_gateway(lead)
        if not gateway:
            return

        member_names = [
            e.config.name for e in self._team.active_experts if e.config.name != lead.config.name
        ]
        if not member_names:
            return

        prompt = (
            f"你是团队 Lead {lead.config.name}，需要判断以下任务是否需要方案评审辩论。\n\n"
            f"任务：{task}\n"
            f"分解的阶段：{', '.join(ph.name for ph in plan.phases)}\n"
            f"团队成员：{', '.join(member_names)}\n\n"
            "以下情况需要方案评审：\n"
            "1) 任务复杂，涉及多个技术方向\n"
            "2) 方案选择影响重大，值得先讨论再执行\n"
            "3) 团队成员可能有不同观点\n"
            "简单任务不需要评审。\n\n"
            "只回答 true 或 false。"
        )

        try:
            response = await gateway.chat(
                messages=[{"role": "user", "content": prompt}],
                model=self._get_model(lead),
            )
            if not response.content.strip().lower().startswith("true"):
                return
        except Exception as e:
            logger.warning(f"Plan review judgment failed: {e}")
            return

        # Insert plan review DEBATE phase at the head
        debate_phase = PlanPhase(
            name="方案评审",
            assigned_expert=lead.config.name,
            task_description=f"方案评审：{task}",
            depends_on=[],
            phase_type=PhaseType.DEBATE,
            debate_config={
                "topic": f"方案评审：{task}",
                "participants": member_names,
                "max_rounds": 2,
            },
        )

        # All existing phases now depend on the debate phase
        for ph in plan.phases:
            ph.depends_on.append(debate_phase.id)

        plan.phases.insert(0, debate_phase)
        self._debate_count += 1
        logger.info(f"Added plan review debate phase {debate_phase.id}")

    async def _detect_divergence(
        self, lead: Expert, completed_phase: PlanPhase, plan: TeamPlan
    ) -> bool:
        """Use LLM to detect if a completed phase's output has divergence worth debating.

        Returns False if LLM unavailable, detection fails, or no other completed
        phases to compare against. Prefers false negatives over false positives.
        """
        gateway = self._get_llm_gateway(lead)
        if not gateway:
            return False

        # Need other completed phases to compare against
        other_completed = [
            ph for ph in plan.completed_phases if ph.id != completed_phase.id and ph.result
        ]
        if not other_completed:
            return False

        other_outputs = []
        for ph in other_completed:
            content = ph.result.get("content", str(ph.result)) if ph.result else ""
            other_outputs.append(f"[{ph.name}]:\n{content[:300]}")

        current_output = ""
        if completed_phase.result:
            current_output = completed_phase.result.get("content", str(completed_phase.result))[
                :500
            ]

        prompt = (
            f"你是团队 Lead {lead.config.name}，需要判断刚完成的阶段产出是否与其他阶段存在分歧。\n\n"
            f"原始任务：{plan.task}\n\n"
            f"刚完成的阶段：{completed_phase.name}\n"
            f"产出：{current_output}\n\n"
            f"其他已完成阶段的产出：\n" + "\n---\n".join(other_outputs) + "\n\n"
            "请判断是否值得发起辩论。以下情况值得辩论：\n"
            "1) 两个阶段产出存在矛盾或冲突\n"
            "2) 阶段产出与原始任务约束冲突\n"
            "3) 存在多个合理方案需要抉择\n"
            "其他情况不值得辩论。\n\n"
            "只回答 true 或 false，不要其他文字。"
        )

        try:
            response = await gateway.chat(
                messages=[{"role": "user", "content": prompt}],
                model=self._get_model(lead),
            )
            return response.content.strip().lower().startswith("true")
        except Exception as e:
            logger.warning(f"Divergence detection failed: {e}")
            return False

    def _insert_debate_phase(
        self,
        plan: TeamPlan,
        trigger_phase: PlanPhase,
        topic: str,
        participants: list[str],
    ) -> PlanPhase | None:
        """Insert a DEBATE phase after the trigger phase, rewiring dependents.

        Phases that depended on trigger_phase now depend on the DEBATE phase,
        so they wait for the debate conclusion before executing.
        """
        if not participants:
            return None

        lead = self._team.lead_expert
        assigned = lead.config.name if lead else trigger_phase.assigned_expert

        debate_phase = PlanPhase(
            name=f"辩论: {topic[:20]}",
            assigned_expert=assigned,
            task_description=topic,
            depends_on=[trigger_phase.id],
            phase_type=PhaseType.DEBATE,
            debate_config={
                "topic": topic,
                "participants": participants,
                "max_rounds": 2,
            },
        )

        # Rewire: phases that depended on trigger_phase now depend on debate_phase
        for ph in plan.phases:
            if trigger_phase.id in ph.depends_on:
                ph.depends_on.remove(trigger_phase.id)
                ph.depends_on.append(debate_phase.id)

        plan.phases.append(debate_phase)
        self._debate_count += 1
        logger.info(f"Inserted debate phase {debate_phase.id} after {trigger_phase.id}")
        return debate_phase

    async def _check_divergence_and_insert_debates(
        self,
        lead: Expert,
        plan: TeamPlan,
        completed_in_layer: list[PlanPhase],
    ) -> None:
        """Check for divergence on newly completed phases and insert debates.

        Called after each layer completes. Stops early if MAX_DEBATES is reached.
        """
        for ph in completed_in_layer:
            if ph.status != PhaseStatus.COMPLETED:
                continue
            if self._debate_count >= self.MAX_DEBATES:
                logger.info(
                    f"Max debates ({self.MAX_DEBATES}) reached, skipping divergence detection"
                )
                return

            has_divergence = await self._detect_divergence(lead, ph, plan)
            if not has_divergence:
                continue

            # Determine participants: all active experts except lead
            participants = [
                e.config.name
                for e in self._team.active_experts
                if e.config.name != lead.config.name
            ]
            topic = f"阶段 '{ph.name}' 产出分歧"
            debate = self._insert_debate_phase(plan, ph, topic, participants)
            if debate:
                await self._broadcast_event(
                    "plan_update",
                    {
                        "plan_id": plan.id,
                        "plan_phases": [p.to_dict() for p in plan.phases],
                        "debate_inserted": debate.id,
                    },
                )
                # P1 #7: Persist dynamically inserted DEBATE phase so resume sees it
                if self._checkpoint is not None:
                    try:
                        await self._checkpoint.save_plan(plan)
                    except Exception as e:
                        logger.warning(f"Checkpoint save_plan (debate insert) failed: {e}")
