"""
Incident API Endpoints - Phase 6.4 決策輸出層
=============================================

Endpoints:
- GET  /api/v1/incidents                     - 取得事件清單
- GET  /api/v1/incidents/{incident_id}       - 取得單一事件
- POST /api/v1/incidents/{incident_id}/proposal - 生成決策提案

Phase 6.4 核心功能:
1. 從 Incident 生成 Decision Proposal
2. 向下相容現有 ApprovalRequest 格式
3. 前端零改動即可渲染

統帥鐵律:
- 所有決策必須經過 TrustEngine 評估
- Proposal 必須關聯到 Incident
"""

from datetime import UTC, datetime, timedelta
from typing import Any

from fastapi import APIRouter, HTTPException, Query, status
from pydantic import BaseModel, Field

from src.core.logging import get_logger
from src.models.approval import ApprovalRequestResponse
from src.models.incident import Incident, IncidentStatus, Severity

# Phase 22 P1: 移除 Repository 直接存取 (2026-03-31)
# Phase 16 R3.3b (2026-03-25 台北時區): Repository 層整合 - 已移至 Service 層
from src.services.decision_manager import get_decision_manager
from src.services.incident_service import get_incident_service
from src.services.incident_timeline_service import fetch_incident_timeline
from src.services.proposal_service import get_proposal_service
from src.utils.timezone import now_taipei

router = APIRouter(prefix="/incidents", tags=["Incidents"])
logger = get_logger("awoooi.incidents")


# =============================================================================
# Response Models
# =============================================================================

class DecisionInfo(BaseModel):
    """Phase 6.5: 決策令牌資訊"""
    token: str
    state: str  # init, analyzing, ready, executing, completed
    proposal_data: dict[str, Any] | None = None
    proposal_id: str | None = None


class IncidentResponse(BaseModel):
    """事件回應 - Phase 6.5 含決策令牌"""
    incident_id: str
    status: str
    severity: str
    signal_count: int
    affected_services: list[str]
    proposal_count: int
    created_at: str
    updated_at: str
    # Phase 6.5: 決策令牌 (確保 UI 永不鎖死)
    decision: DecisionInfo | None = None

    @classmethod
    def from_incident(cls, incident: Incident, decision: DecisionInfo | None = None) -> "IncidentResponse":
        return cls(
            incident_id=incident.incident_id,
            status=incident.status.value,
            severity=incident.severity.value,
            signal_count=len(incident.signals),
            affected_services=incident.affected_services,
            proposal_count=len(incident.proposal_ids),
            created_at=incident.created_at.isoformat(),
            updated_at=incident.updated_at.isoformat(),
            decision=decision,
        )


class IncidentListResponse(BaseModel):
    """事件清單回應"""
    count: int
    incidents: list[IncidentResponse]


class ProposalGenerateResponse(BaseModel):
    """Proposal 生成回應"""
    success: bool
    message: str
    incident_id: str
    proposal: ApprovalRequestResponse | None = None
    incident_status: str | None = None


class IncidentTimelineEvent(BaseModel):
    """事件處理歷程中的一筆原始或合成事件"""
    stage: str
    status: str
    title: str
    description: str | None = None
    actor: str | None = None
    timestamp: str | None = None
    source_table: str | None = None
    data: dict[str, Any] = Field(default_factory=dict)


class IncidentTimelineStage(BaseModel):
    """事件處理歷程的標準階段"""
    stage: str
    label: str
    status: str
    timestamp: str | None = None
    title: str
    description: str | None = None
    actor: str | None = None
    source_table: str | None = None
    data: dict[str, Any] = Field(default_factory=dict)
    events: list[IncidentTimelineEvent] = Field(default_factory=list)


class IncidentTimelineResponse(BaseModel):
    """事件完整處理歷程回應"""
    incident_id: str
    title: str
    status: str
    severity: str
    started_at: str | None = None
    updated_at: str | None = None
    resolved_at: str | None = None
    affected_services: list[str] = Field(default_factory=list)
    approval_ids: list[str] = Field(default_factory=list)
    timeline: list[IncidentTimelineStage] = Field(default_factory=list)
    events: list[IncidentTimelineEvent] = Field(default_factory=list)
    ascii_timeline: str
    reconciliation: dict[str, Any] = Field(default_factory=dict)


# =============================================================================
# GET /api/v1/incidents
# =============================================================================

@router.get(
    "",
    response_model=IncidentListResponse,
    summary="取得事件清單",
    description="""
    取得所有活躍事件 (INVESTIGATING 或 MITIGATING 狀態)。

    Phase 6.5 升級:
    - 每個事件自動附帶 decision_token
    - 預設只讀取已存在的 decision_token
    - 需要新決策時改由明確的 proposal / operator run 入口觸發
    """,
)
async def list_incidents(
    generate_missing_decisions: bool = Query(
        False,
        description=(
            "預設 false，列表查詢只讀既有 decision token；"
            "true 僅供明確維運操作使用，會背景產生缺少的決策。"
        ),
    ),
) -> IncidentListResponse:
    """
    取得活躍事件清單

    Phase 6.5: 附帶既有決策令牌
    - 列表查詢必須是低成本純讀路徑
    - 不可因為前端輪詢就背景觸發 LLM / Ollama / OpenClaw
    - 需要新決策時，呼叫 POST /api/v1/incidents/{incident_id}/proposal

    Returns:
        IncidentListResponse: 事件清單與計數 (含決策令牌)
    """
    # Phase 17 P0: Router 層違規修復 - 改用 Service 層
    incident_service = get_incident_service()
    decision_manager = get_decision_manager()

    try:
        # 透過 Service 取得活躍事件
        incidents = await incident_service.get_active_incidents()

        # 按時間排序 (最新優先)
        # 2026-03-26 修復: 處理 timezone-aware 與 naive datetime 混合問題
        def safe_created_at(i: Incident) -> float:
            """安全取得 timestamp，處理 timezone 混合問題"""
            dt = i.created_at
            if dt.tzinfo is None:
                # naive datetime 假設為 UTC
                dt = dt.replace(tzinfo=UTC)
            return dt.timestamp()

        incidents.sort(key=safe_created_at, reverse=True)

        # 2026-04-09 Claude Sonnet 4.6: 效能修復 — list endpoint 不同步等待 AI
        # 原設計: 每個 incident await AI 決策 (120-180s timeout)，多 incident 時乘積爆炸
        # 修復: 只取已存在的決策 token，若無則背景觸發生成，前端 poll 單筆 GET 取得結果
        #
        # 2026-05-06 Codex: 成本與推理槽修復 — 預設不再背景觸發 AI。
        # 根因: 多個前端頁面會輪詢 GET /incidents；若列表查詢偷偷 create_task，
        # 每次頁面載入都可能消耗 GCP Ollama / OpenClaw 推理槽，甚至 fallback 到 Gemini。
        # 新規則: GET list 是純讀；生成新修復建議必須走明確 proposal/operator-run 入口。
        if generate_missing_decisions:
            import asyncio

        responses = []
        background_tasks = []
        existing_tokens = await decision_manager._find_existing_tokens_for_incidents(
            [incident.incident_id for incident in incidents]
        )

        for incident in incidents:
            try:
                # 只查已快取的決策 (不等待 AI，立即返回)
                existing = existing_tokens.get(incident.incident_id)
                if existing:
                    decision_info = DecisionInfo(
                        token=existing.token,
                        state=existing.state.value,
                        proposal_data=existing.proposal_data,
                        proposal_id=existing.proposal_id,
                    )
                    responses.append(IncidentResponse.from_incident(incident, decision_info))
                else:
                    # 無快取 → 本次返回 None。列表查詢預設不觸發 AI；
                    # 前端若需要修復建議，必須呼叫明確的 proposal 入口。
                    responses.append(IncidentResponse.from_incident(incident, None))
                    if not generate_missing_decisions:
                        continue

                    # 2026-04-16 Claude Sonnet 4.6: 只對 48h 內的 incident 觸發 AI 分析
                    # 舊 incident token 每小時過期，若不限制會反覆重新分析歷史事件 → Telegram 洪水
                    _created = getattr(incident, "created_at", None)
                    _too_old = False
                    if _created:
                        if _created.tzinfo is None:
                            _created = _created.replace(tzinfo=UTC)
                        _too_old = (_created < datetime.now(UTC) - timedelta(hours=48))
                    if not _too_old:
                        timeout = 120.0 if incident.severity in (Severity.P0, Severity.P1) else 180.0
                        background_tasks.append(
                            decision_manager.get_or_create_decision(incident=incident, timeout_sec=timeout)
                        )
            except Exception as e:
                logger.warning(
                    "decision_generation_failed",
                    incident_id=incident.incident_id,
                    error=str(e),
                )
                responses.append(IncidentResponse.from_incident(incident, None))

        # 背景觸發 AI 決策（fire-and-forget，不阻塞 response）
        if background_tasks:
            for task in background_tasks:
                asyncio.create_task(task)

        logger.info(
            "incidents_listed",
            count=len(incidents),
            with_decisions=sum(1 for r in responses if r.decision is not None),
            generate_missing_decisions=generate_missing_decisions,
        )

        return IncidentListResponse(
            count=len(incidents),
            incidents=responses,
        )

    except Exception as e:
        logger.exception(
            "list_incidents_error",
            error=str(e),
        )
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to list incidents: {str(e)}",
        ) from e


# =============================================================================
# GET /api/v1/incidents/{incident_id}
# =============================================================================

@router.get(
    "/{incident_id}",
    response_model=IncidentResponse,
    summary="取得單一事件",
    description="取得特定事件的詳細資訊。",
)
async def get_incident(incident_id: str) -> IncidentResponse:
    """
    取得單一事件

    Args:
        incident_id: 事件 ID

    Returns:
        IncidentResponse: 事件詳細資訊

    Raises:
        HTTPException: 404 事件不存在
    """
    # Phase 17 P0: Router 層違規修復 - 改用 Service 層
    incident_service = get_incident_service()

    try:
        incident = await incident_service.get_from_working_memory(incident_id)
        if incident is None:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Incident not found: {incident_id}",
            )

        logger.info(
            "incident_fetched",
            incident_id=incident_id,
            status=incident.status.value,
        )

        return IncidentResponse.from_incident(incident)

    except HTTPException:
        raise
    except Exception as e:
        logger.exception(
            "get_incident_error",
            incident_id=incident_id,
            error=str(e),
        )
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to get incident: {str(e)}",
        ) from e


# =============================================================================
# GET /api/v1/incidents/{incident_id}/timeline
# =============================================================================

@router.get(
    "/{incident_id}/timeline",
    response_model=IncidentTimelineResponse,
    summary="取得事件完整處理歷程",
    description="彙整 webhook、AI、目標、風險、安全閘、執行、驗證、KM 與結案事件。",
)
async def get_incident_timeline(incident_id: str) -> IncidentTimelineResponse:
    """
    取得單一 Incident 的端到端處理歷程。
    """
    try:
        timeline = await fetch_incident_timeline(incident_id)
        if timeline is None:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Incident not found: {incident_id}",
            )

        logger.info(
            "incident_timeline_fetched",
            incident_id=incident_id,
            stage_count=len(timeline.get("timeline", [])),
            event_count=len(timeline.get("events", [])),
        )
        return IncidentTimelineResponse.model_validate(timeline)

    except HTTPException:
        raise
    except Exception as e:
        logger.exception(
            "get_incident_timeline_error",
            incident_id=incident_id,
            error=str(e),
        )
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to get incident timeline: {str(e)}",
        ) from e


# =============================================================================
# POST /api/v1/incidents/{incident_id}/proposal
# =============================================================================

@router.post(
    "/{incident_id}/proposal",
    response_model=ProposalGenerateResponse,
    summary="生成決策提案",
    description="""
    根據 Incident 生成 Decision Proposal。

    流程:
    1. 分析 Incident 的 signals 決定修復動作
    2. 透過 TrustEngine 評估風險等級
    3. 建立 ApprovalRequest (向下相容前端)
    4. 關聯 Proposal 到 Incident
    5. 推進 Incident 狀態為 MITIGATING

    生成的 Proposal 會出現在 /api/v1/approvals/pending 清單中，
    前端無需任何改動即可渲染。
    """,
)
async def generate_proposal(incident_id: str) -> ProposalGenerateResponse:
    """
    從 Incident 生成 Decision Proposal

    Args:
        incident_id: 事件 ID

    Returns:
        ProposalGenerateResponse: 生成結果

    Raises:
        HTTPException: 404 事件不存在, 400 無法生成
    """
    service = get_proposal_service()
    approval, message = await service.generate_proposal(incident_id)

    if approval is None:
        if "not found" in message.lower():
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=message,
            )
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=message,
        )

    logger.info(
        "proposal_generated",
        incident_id=incident_id,
        approval_id=str(approval.id),
        risk_level=approval.risk_level.value,
    )

    # Phase 17 P0: 改用 Service 層取得狀態
    incident_service = get_incident_service()
    incident_status = None
    try:
        incident = await incident_service.get_from_working_memory(incident_id)
        if incident:
            incident_status = incident.status.value
    except Exception:
        pass

    return ProposalGenerateResponse(
        success=True,
        message=message,
        incident_id=incident_id,
        proposal=ApprovalRequestResponse.from_approval(approval),
        incident_status=incident_status,
    )


# =============================================================================
# PUT /api/v1/incidents/{incident_id}/feedback - Phase 6.6 人類回饋 API
# =============================================================================


class FeedbackRequest(BaseModel):
    """人類回饋請求"""

    effectiveness_score: int | None = Field(
        None,
        ge=1,
        le=5,
        description="有效性評分 (1-5 分, 5 最有效)",
    )
    human_feedback: str | None = Field(
        None,
        max_length=1000,
        description="文字回饋 (如 '這個建議很準' 或 '下次應該先檢查 X')",
    )
    learning_notes: str | None = Field(
        None,
        max_length=2000,
        description="給未來 AI 的學習筆記",
    )
    should_remember: bool = Field(
        default=True,
        description="是否納入長期記憶 (Episodic Memory)",
    )


class FeedbackResponse(BaseModel):
    """人類回饋回應"""

    success: bool
    message: str
    incident_id: str
    outcome: dict[str, Any] | None = None


@router.put(
    "/{incident_id}/feedback",
    response_model=FeedbackResponse,
    summary="提交人類回饋",
    description="""
    Phase 6.6: 人類回饋 API

    用途:
    - 收集人類對 AI 建議的有效性評分
    - 記錄文字回饋與學習筆記
    - 作為 AI 改進的訓練數據

    工作流程:
    1. 更新 Incident 的 outcome 欄位
    2. 同步到 Redis (Working Memory) 和 PostgreSQL (Episodic Memory)
    3. 供統計分析 API (/api/v1/stats/feedback/summary) 聚合查詢
    """,
)
async def submit_feedback(
    incident_id: str,
    request: FeedbackRequest,
) -> FeedbackResponse:
    """
    提交人類對 AI 建議的回饋

    Args:
        incident_id: 事件 ID
        request: 回饋內容

    Returns:
        FeedbackResponse: 更新結果

    Raises:
        HTTPException: 404 事件不存在
    """
    # Phase 17 P0: Router 層違規修復 - 改用 Service 層
    incident_service = get_incident_service()

    try:
        incident = await incident_service.update_outcome(
            incident_id=incident_id,
            effectiveness_score=request.effectiveness_score,
            human_feedback=request.human_feedback,
            learning_notes=request.learning_notes,
            should_remember=request.should_remember,
        )

        if incident is None:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Incident not found: {incident_id}",
            )

        logger.info(
            "feedback_updated",
            incident_id=incident_id,
            effectiveness_score=request.effectiveness_score,
        )

        return FeedbackResponse(
            success=True,
            message="Feedback submitted successfully",
            incident_id=incident_id,
            outcome=incident.outcome.model_dump(mode="json") if incident.outcome else {},
        )

    except HTTPException:
        raise
    except Exception as e:
        logger.exception("feedback_update_error", incident_id=incident_id)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to update feedback: {str(e)}",
        ) from e


# =============================================================================
# DEBUG: 測試 Incident 狀態更新
# =============================================================================

@router.post(
    "/{incident_id}/resolve",
    summary="[DEBUG] 直接將 Incident 標記為 RESOLVED",
    description="測試用端點，直接更新 Incident 狀態",
)
async def debug_resolve_incident(incident_id: str) -> dict[str, Any]:
    """
    DEBUG: 直接更新 Incident 狀態為 RESOLVED
    用於測試 resolve_incident_after_approval 邏輯

    Phase 22 P1: Router 層違規修復 - 完全使用 Service 層 (2026-03-31)
    """
    incident_service = get_incident_service()
    error_msg = None

    # 1. 取得更新前狀態 (透過 Service)
    before_status = None
    try:
        incident = await incident_service.get_from_working_memory(incident_id)
        if incident:
            before_status = incident.status.value
    except Exception as e:
        error_msg = f"Read error: {e}"

    # 2. 透過 Service 更新狀態
    updated = False
    try:
        resolved = await incident_service.resolve_incident(incident_id)
        if resolved:
            updated = True
    except Exception as e:
        error_msg = f"Resolve error: {e}"

    # 3. 驗證更新後狀態 (透過 Service)
    after_status = None
    try:
        incident = await incident_service.get_from_working_memory(incident_id)
        if incident:
            after_status = incident.status.value
    except Exception:
        pass

    return {
        "incident_id": incident_id,
        "before_status": before_status,
        "after_status": after_status,
        "updated": updated,
        "error": error_msg,
    }


# =============================================================================
# POST /api/v1/incidents/sync-from-approvals - 補建 Incidents
# =============================================================================

class SyncResult(BaseModel):
    """同步結果"""
    synced: int
    skipped: int
    errors: list[str]
    details: list[dict[str, Any]]


@router.post(
    "/sync-from-approvals",
    response_model=SyncResult,
    summary="從現有 Approvals 補建 Incidents",
    description="""
    修復歷史數據：為沒有對應 Incident 的 Approvals 補建。

    用途：
    - 修復 b645981 之前創建的 Approvals（無 Incident）
    - 確保「活躍事件」與「待簽核」數量一致
    """,
)
async def sync_incidents_from_approvals() -> SyncResult:
    """
    掃描所有 pending Approvals，為缺少 Incident 的補建

    Phase 17 P0: Router 層違規修復 - 改用 Service 層
    """
    from uuid import UUID

    from src.models.incident import Signal
    from src.services.approval_db import get_approval_service

    # Phase 17 P0: 改用 Service 層
    incident_service = get_incident_service()
    approval_service = get_approval_service()

    synced = 0
    skipped = 0
    errors: list[str] = []
    details: list[dict[str, Any]] = []

    # 取得所有 pending approvals
    pending_approvals = await approval_service.get_pending_approvals()

    for approval in pending_approvals:
        approval_id = str(approval.id)

        # Phase 17 P0: 透過 Service 查找關聯 Incident
        existing = await incident_service.find_by_proposal_id(approval_id)
        if existing:
            skipped += 1
            continue

        # 補建 Incident
        try:
            # 映射 risk_level -> severity
            risk_map = {
                "critical": Severity.P0,
                "high": Severity.P1,
                "medium": Severity.P2,
                "low": Severity.P3,
            }
            severity = risk_map.get(approval.risk_level.value.lower(), Severity.P2)

            # 解析 action 取得資源名稱
            action_parts = approval.action.split("|")
            resource_name = action_parts[0].strip() if action_parts else "unknown"

            signal = Signal(
                alert_name="sync_from_approval",
                severity=severity,
                source="backfill",
                fired_at=approval.created_at,
                labels={"approval_id": approval_id},
                annotations={"description": approval.description or ""},
            )

            incident = Incident(
                status=IncidentStatus.INVESTIGATING,
                severity=severity,
                signals=[signal],
                affected_services=[resource_name],
                proposal_ids=[UUID(approval_id)],
                created_at=approval.created_at,
                updated_at=now_taipei(),
            )

            # Phase 17 P0: 透過 Service 儲存
            saved = await incident_service.save_to_working_memory(incident)
            if not saved:
                errors.append(f"Failed to save incident for approval {approval_id}")
                continue

            synced += 1
            details.append({
                "approval_id": approval_id,
                "incident_id": incident.incident_id,
                "severity": severity.value,
            })

            logger.info(
                "incident_synced_from_approval",
                approval_id=approval_id,
                incident_id=incident.incident_id,
            )

        except Exception as e:
            errors.append(f"{approval_id}: {str(e)}")
            logger.error(
                "incident_sync_error",
                approval_id=approval_id,
                error=str(e),
            )

    logger.info(
        "sync_from_approvals_completed",
        synced=synced,
        skipped=skipped,
        errors=len(errors),
    )

    return SyncResult(
        synced=synced,
        skipped=skipped,
        errors=errors,
        details=details,
    )
