"""
AWOOOI AIOps Phase 2 — Coordinator Agent（指揮官）
==================================================
職責：聚合所有 Agent 輸出，做最終決策

輸入：DiagnosisReport + ActionPlan + ReviewVerdict + CriticReport
輸出：DecisionPackage（recommended_action + confidence + requires_human_approval）

聚合邏輯：
1. Reviewer REJECT → blocked_actions 全部禁止執行，強制人工審核
2. Reviewer REQUEST_REVISION → 過濾高 blast_radius 方案，使用 safe_candidates
3. Critic 有 critical challenge → confidence 降低 CRITIC_PENALTY
4. 全 Agent degraded → requires_human_approval = True（安全第一）
5. Diagnostician ABSTAIN 且無有效假設 → requires_human_approval = True
6. 最終 confidence < HUMAN_ESCALATION_THRESHOLD → requires_human_approval = True

ADR-082: Phase 2 多 Agent 協作
2026-04-15 ogt + Claude Sonnet 4.6（亞太）: Phase 2 初始建立
"""

from __future__ import annotations

import time
from typing import Any

import structlog

from src.agents.base import BaseAgent
from src.agents.protocol import (
    ActionPlan,
    AgentRole,
    AgentSessionStatus,
    AgentVote,
    CriticReport,
    DecisionPackage,
    DiagnosisReport,
    ReviewVerdict,
)

logger = structlog.get_logger(__name__)

# confidence 低於此閾值 → 強制人工審核
HUMAN_ESCALATION_THRESHOLD = 0.4

# Critic critical challenge 懲罰係數
CRITIC_PENALTY = 0.3


class CoordinatorAgent(BaseAgent):
    """
    Coordinator Agent — 指揮官（最終決策者）

    Usage:
        agent = CoordinatorAgent()
        package = await agent.run(diagnosis, plan, verdict, critic)
    """

    AGENT_NAME = AgentRole.COORDINATOR.value
    AGENT_DESCRIPTION = (
        "Final decision synthesizer. Aggregates all agent outputs into "
        "a single actionable DecisionPackage."
    )

    async def run(
        self,
        diagnosis: DiagnosisReport,
        plan: ActionPlan,
        verdict: ReviewVerdict,
        critic: CriticReport,
    ) -> DecisionPackage:
        """
        聚合所有 Agent 輸出，決定最終行動。

        Args:
            diagnosis: Diagnostician 輸出
            plan: Solver 輸出
            verdict: Reviewer 安全審查結果
            critic: Critic 批判性審查結果

        Returns:
            DecisionPackage（不熔斷，Coordinator 必須輸出決策）
        """
        start_ms = int(time.monotonic() * 1000)

        package = self._synthesize(diagnosis, plan, verdict, critic)
        package.latency_ms = int(time.monotonic() * 1000) - start_ms

        logger.info(
            "coordinator_done",
            recommended_action=package.recommended_action,
            confidence=package.confidence,
            requires_human=package.requires_human_approval,
            all_degraded=package.all_agents_degraded,
            session_status=package.session_status,
            latency_ms=package.latency_ms,
        )
        return package

    def _synthesize(
        self,
        diagnosis: DiagnosisReport,
        plan: ActionPlan,
        verdict: ReviewVerdict,
        critic: CriticReport,
    ) -> DecisionPackage:
        """核心聚合邏輯（純函數，無 LLM，不可失敗）。"""
        top = plan.top_candidate
        base_confidence = top.confidence if top else 0.0
        selected = top

        # ── 1. Reviewer REJECT → 最優先安全門 ──────────────────────────
        if verdict.vote == AgentVote.REJECT:
            return DecisionPackage(
                recommended_action=None,
                confidence=0.0,
                requires_human_approval=True,
                debate_summary=_build_summary(diagnosis, plan, verdict, critic),
                session_status=AgentSessionStatus.COMPLETED,
                latency_ms=0,
                diagnosis=diagnosis,
                action_plan=plan,
                reviewer_verdict=verdict,
                critic_report=critic,
                blocked_reason=f"Reviewer 拒絕：{verdict.reason}",
            )

        # ── 2. Reviewer REQUEST_REVISION → 強制人工審核（Solver 未修訂，不可自動執行）─
        # Gate 2: REQUEST_REVISION 代表「請 Solver 重新設計方案」，此 Phase 無迭代機制
        # → 保留 safe_candidates 供人工參考，但 requires_human_approval 必須 True
        if verdict.vote == AgentVote.REQUEST_REVISION:
            safe_candidates = [
                c for c in plan.candidates
                if c.action not in verdict.blocked_actions
            ]
            selected = safe_candidates[0] if safe_candidates else None

            # 2026-04-17 ogt + Claude Sonnet 4.6: 無安全候選 → 回退 Solver 原始最優方案
            # 根因：safe_candidates=[] → selected=None → recommended_action=None
            #       → decision_manager action="" → TG 卡顯示「待分析」（資訊流斷裂）
            # 修復：強制輸出 Solver 原始最優建議（標記 [Reviewer 未核准，僅供參考]）
            #       資訊流絕不可斷，SRE 永遠需要看到 AI 的建議作為參考依據
            _all_blocked = (selected is None and bool(plan.candidates))
            if selected is None and plan.top_candidate:
                selected = plan.top_candidate

            base_confidence = selected.confidence if selected else 0.0
            if selected:
                _recommended = (
                    f"[Reviewer 未核准，僅供參考] {selected.action}"
                    if _all_blocked
                    else selected.action
                )
            else:
                _recommended = "(無可用方案，請人工研判根因後執行)"

            return DecisionPackage(
                recommended_action=_recommended,
                confidence=base_confidence,
                requires_human_approval=True,
                debate_summary=_build_summary(diagnosis, plan, verdict, critic),
                session_status=AgentSessionStatus.COMPLETED,
                latency_ms=0,
                diagnosis=diagnosis,
                action_plan=plan,
                reviewer_verdict=verdict,
                critic_report=critic,
                blocked_reason=f"Reviewer REQUEST_REVISION：{verdict.reason}",
            )

        # ── 3. Critic REJECT（critical challenge）→ 硬閘強制人工 ─────────
        # 驗證發現：penalty 策略（0.82-0.30=0.52）仍可穿透 0.4 閾值
        # Critic 投 REJECT 代表「這個決策不能執行」，應等同 Reviewer REJECT 效力
        if critic.vote == AgentVote.REJECT:
            top_challenge = critic.challenges[0] if critic.challenges else None
            return DecisionPackage(
                recommended_action=selected.action if selected else None,
                confidence=base_confidence,
                requires_human_approval=True,
                debate_summary=_build_summary(diagnosis, plan, verdict, critic),
                session_status=AgentSessionStatus.COMPLETED,
                latency_ms=0,
                diagnosis=diagnosis,
                action_plan=plan,
                reviewer_verdict=verdict,
                critic_report=critic,
                blocked_reason=(
                    f"Critic REJECT：{top_challenge.argument[:100]}"
                    if top_challenge else "Critic 強烈反對此方案"
                ),
            )

        # ── 3.5 Critic major/minor challenge → 信心懲罰（軟降，不強制人工）
        adjusted_confidence = base_confidence
        if critic.has_critical_challenge:
            # has_critical_challenge 為 True 但 vote != REJECT 理論上不應發生
            # 保留 penalty 作為 defense-in-depth
            adjusted_confidence = max(0.0, base_confidence - CRITIC_PENALTY)
            logger.info(
                "coordinator_critic_penalty",
                before=base_confidence,
                after=adjusted_confidence,
            )

        # ── 4. 全 Agent 降級 → 強制人工 ──────────────────────────────────
        # Gate 2: 原本遺漏 verdict.degraded，Reviewer 熔斷時 all_degraded 被低估
        all_degraded = diagnosis.degraded and plan.degraded and verdict.degraded and critic.degraded
        if all_degraded:
            return DecisionPackage(
                recommended_action=selected.action if selected else None,
                confidence=adjusted_confidence,
                requires_human_approval=True,
                debate_summary=_build_summary(diagnosis, plan, verdict, critic),
                session_status=AgentSessionStatus.DEGRADED,
                latency_ms=0,
                diagnosis=diagnosis,
                action_plan=plan,
                reviewer_verdict=verdict,
                critic_report=critic,
                blocked_reason="所有 Agent 皆降級，信心不可信，強制人工審核",
                all_agents_degraded=True,
            )

        # ── 5. Diagnostician 無有效假設 → 強制人工 ───────────────────────
        if not diagnosis.hypotheses or diagnosis.vote == AgentVote.ABSTAIN:
            return DecisionPackage(
                recommended_action=selected.action if selected else None,
                confidence=adjusted_confidence,
                requires_human_approval=True,
                debate_summary=_build_summary(diagnosis, plan, verdict, critic),
                session_status=AgentSessionStatus.DEGRADED,
                latency_ms=0,
                diagnosis=diagnosis,
                action_plan=plan,
                reviewer_verdict=verdict,
                critic_report=critic,
                blocked_reason="Diagnostician ABSTAIN：感官情報不足，需人工判斷根因",
            )

        # ── 6. confidence 低於閾值 → 強制人工 ────────────────────────────
        if adjusted_confidence < HUMAN_ESCALATION_THRESHOLD:
            return DecisionPackage(
                recommended_action=selected.action if selected else None,
                confidence=adjusted_confidence,
                requires_human_approval=True,
                debate_summary=_build_summary(diagnosis, plan, verdict, critic),
                session_status=AgentSessionStatus.DEGRADED,
                latency_ms=0,
                diagnosis=diagnosis,
                action_plan=plan,
                reviewer_verdict=verdict,
                critic_report=critic,
                blocked_reason=(
                    f"信心 {adjusted_confidence:.0%} < 閾值 "
                    f"{HUMAN_ESCALATION_THRESHOLD:.0%}，需人工確認"
                ),
            )

        # ── 7. 自動執行 ────────────────────────────────────────────────────
        return DecisionPackage(
            recommended_action=selected.action if selected else None,
            confidence=adjusted_confidence,
            requires_human_approval=False,
            debate_summary=_build_summary(diagnosis, plan, verdict, critic),
            session_status=AgentSessionStatus.COMPLETED,
            latency_ms=0,
            diagnosis=diagnosis,
            action_plan=plan,
            reviewer_verdict=verdict,
            critic_report=critic,
        )

    def _build_prompt(self, _context: dict[str, Any]) -> str:
        return ""  # Coordinator 不使用 LLM（純規則聚合）

    def _parse_response(self, _response: str) -> dict[str, Any]:
        return {}

    def analyze(self, context: dict[str, Any]) -> Any:
        raise NotImplementedError("Use run() for Phase 2 agents")


# ─────────────────────────────────────────────────────────────────────────────
# Helpers
# ─────────────────────────────────────────────────────────────────────────────

def _build_summary(
    diagnosis: DiagnosisReport,
    plan: ActionPlan,
    verdict: ReviewVerdict,
    critic: CriticReport,
) -> str:
    """產生 debate_summary（結構化文字，限 1000 字）。"""
    parts = []

    top_h = diagnosis.top_hypothesis
    if top_h:
        parts.append(
            f"診斷：{top_h.description[:200]}（信心 {top_h.confidence:.0%}，"
            f"{'降級' if diagnosis.degraded else '正常'}）"
        )
    else:
        parts.append("診斷：無有效假設（ABSTAIN）")

    top_c = plan.top_candidate
    if top_c:
        parts.append(
            f"方案：{top_c.action[:100]}（blast_radius={top_c.blast_radius}，"
            f"{'降級' if plan.degraded else '正常'}）"
        )
    else:
        parts.append("方案：無候選動作")

    parts.append(
        f"安全審查：{verdict.vote.value}（{verdict.reason[:100]}，"
        f"{'降級' if verdict.degraded else '正常'}）"
    )

    if critic.challenges:
        top_ch = critic.challenges[0]
        parts.append(
            f"質疑：{top_ch.severity} — {top_ch.argument[:150]}（"
            f"共 {critic.challenge_count} 項，"
            f"{'降級' if critic.degraded else '正常'}）"
        )
    else:
        parts.append(f"質疑：無（{'降級' if critic.degraded else '通過審查'}）")

    return "；".join(parts)[:1000]


# ─────────────────────────────────────────────────────────────────────────────
# Singleton
# ─────────────────────────────────────────────────────────────────────────────

_agent: CoordinatorAgent | None = None


def get_coordinator_agent() -> CoordinatorAgent:
    global _agent
    if _agent is None:
        _agent = CoordinatorAgent()
    return _agent
