"""
Incident Repository - PostgreSQL 實作
======================================
Phase 16 R3.3: Repository 層實作

職責: IncidentRecord 的 CRUD 操作
設計: 實作 IIncidentRepository Protocol

版本: v1.0
建立: 2026-03-26 (台北時區)
建立者: Claude Code (Phase 16 架構重構)
"""

from datetime import UTC, datetime
from typing import Any

import structlog
from sqlalchemy import select

from src.db.base import get_db_context
from src.db.models import IncidentRecord
from src.models.incident import (
    Incident,
    IncidentFrequencyStats,
    IncidentStatus,
    Severity,
)
from src.repositories.interfaces import IIncidentRepository

logger = structlog.get_logger(__name__)


# =============================================================================
# Conversion Helpers
# =============================================================================

def _record_to_incident(record: IncidentRecord) -> Incident:
    """Convert DB IncidentRecord to Pydantic Incident"""
    # Phase 27: 從 DB 快照還原 frequency_stats
    frequency_stats = None
    if getattr(record, "frequency_snapshot", None):
        try:
            frequency_stats = IncidentFrequencyStats(**record.frequency_snapshot)
        except Exception:
            pass  # 欄位結構不符時跳過，不影響主流程

    return Incident(
        incident_id=record.incident_id,
        status=IncidentStatus(_normalize_status(record.status)),
        severity=Severity(_normalize_severity(record.severity)),
        signals=record.signals or [],
        affected_services=record.affected_services or [],
        proposal_ids=record.proposal_ids or [],
        created_at=record.created_at,
        updated_at=record.updated_at,
        resolved_at=record.resolved_at,
        closed_at=record.closed_at,
        frequency_stats=frequency_stats,
    )


def _incident_to_record_data(incident: Incident) -> dict[str, Any]:
    """Convert Pydantic Incident to dict for DB record"""
    # Phase 27: 序列化 frequency_stats 快照到 DB
    frequency_snapshot = None
    if incident.frequency_stats:
        frequency_snapshot = incident.frequency_stats.model_dump()

    return {
        "incident_id": incident.incident_id,
        "project_id": getattr(incident, "project_id", "awoooi"),  # AwoooP Phase 2.3
        "status": incident.status.value,
        "severity": incident.severity.value,
        "signals": [
            {
                **s.model_dump(),
                "alertname": s.alert_name,  # ADR-073: DB query 用 signals->0->>'alertname'，補充 alias
            }
            for s in incident.signals
        ],
        "affected_services": incident.affected_services,
        "proposal_ids": incident.proposal_ids,
        "created_at": incident.created_at,
        "updated_at": incident.updated_at,
        "resolved_at": incident.resolved_at,
        "closed_at": incident.closed_at,
        "frequency_snapshot": frequency_snapshot,
        # ADR-073 Phase 2-1: alertname 欄位 — 優先從 signals[0].alert_name，
        # fallback 到 signals[0].labels["alertname"]（防止 signal 建立時 alert_name 為空）
        # 2026-04-15 ogt: 補 labels fallback（4 筆歷史 NULL 根因修復）
        "alertname": (
            (incident.signals[0].alert_name or
             (incident.signals[0].labels or {}).get("alertname"))
            if incident.signals else None
        ),
        "notification_type": incident.notification_type,  # ADR-073 Phase 2-2
        "alert_category": incident.alert_category,         # ADR-073 Phase 2-2
    }


def _normalize_status(value: str | IncidentStatus) -> str:
    if isinstance(value, IncidentStatus):
        return value.value
    raw = str(value)
    if raw in IncidentStatus.__members__:
        return IncidentStatus[raw].value
    normalized = raw.strip().lower()
    if normalized == "open":
        return IncidentStatus.INVESTIGATING.value
    return normalized


def _normalize_severity(value: str | Severity) -> str:
    if isinstance(value, Severity):
        return value.value
    raw = str(value)
    if raw in Severity.__members__:
        return Severity[raw].value
    legacy_map = {
        "critical": Severity.P0.value,
        "high": Severity.P1.value,
        "warning": Severity.P2.value,
        "medium": Severity.P2.value,
        "info": Severity.P3.value,
        "low": Severity.P3.value,
        "none": Severity.P3.value,
    }
    return legacy_map.get(raw.strip().lower(), raw)


# =============================================================================
# IncidentDBRepository
# =============================================================================

class IncidentDBRepository(IIncidentRepository):
    """
    Incident Repository - PostgreSQL 實作

    純 CRUD 操作，不含業務邏輯
    業務邏輯請放在 Service 層或 Engine
    """

    async def create(self, incident: Incident) -> Incident:
        """建立新的 Incident Record"""
        async with get_db_context() as db:
            data = _incident_to_record_data(incident)
            record = IncidentRecord(**data)
            db.add(record)
            await db.flush()
            await db.refresh(record)

            logger.debug(
                "incident_created_db",
                incident_id=record.incident_id,
                status=record.status,
            )

            return _record_to_incident(record)

    async def get_by_id(self, incident_id: str) -> Incident | None:
        """根據 ID 取得 Incident"""
        async with get_db_context() as db:
            result = await db.execute(
                select(IncidentRecord).where(
                    IncidentRecord.incident_id == incident_id
                )
            )
            record = result.scalar_one_or_none()
            return _record_to_incident(record) if record else None

    async def get_active(self) -> list[Incident]:
        """取得所有活躍的 Incident"""
        active_statuses = [
            IncidentStatus.INVESTIGATING,
            IncidentStatus.MITIGATING,
        ]
        async with get_db_context() as db:
            result = await db.execute(
                select(IncidentRecord)
                .where(IncidentRecord.status.in_(active_statuses))
                .order_by(IncidentRecord.created_at.desc())
            )
            records = result.scalars().all()
            return [_record_to_incident(r) for r in records]

    async def update(self, incident: Incident) -> Incident | None:
        """更新 Incident"""
        async with get_db_context() as db:
            result = await db.execute(
                select(IncidentRecord).where(
                    IncidentRecord.incident_id == incident.incident_id
                )
            )
            record = result.scalar_one_or_none()

            if not record:
                return None

            # 更新欄位
            record.status = incident.status.value
            record.severity = incident.severity.value
            record.signals = [s.model_dump() for s in incident.signals]
            record.affected_services = incident.affected_services
            record.proposal_ids = incident.proposal_ids
            record.updated_at = datetime.now(UTC)
            record.resolved_at = incident.resolved_at
            record.closed_at = incident.closed_at

            await db.flush()
            await db.refresh(record)

            logger.debug(
                "incident_updated_db",
                incident_id=record.incident_id,
                status=record.status,
            )

            return _record_to_incident(record)

    async def upsert(self, incident: Incident) -> bool:
        """Upsert Incident (存在則更新，不存在則建立)"""
        existing = await self.get_by_id(incident.incident_id)

        if existing:
            result = await self.update(incident)
            return result is not None
        else:
            result = await self.create(incident)
            return result is not None

    # -------------------------------------------------------------------------
    # Phase 16 R3.3a (2026-03-25 台北時區)
    # 新增: update_outcome() - 支援 feedback endpoint 更新 outcome 欄位
    # 執行者: Claude Code
    # -------------------------------------------------------------------------
    async def update_outcome(
        self,
        incident_id: str,
        outcome: dict[str, Any],
        updated_at: datetime | None = None,
    ) -> bool:
        """
        更新 Incident 的 outcome 欄位

        Args:
            incident_id: Incident ID
            outcome: Outcome 資料 (dict)
            updated_at: 更新時間 (預設為當前時間)

        Returns:
            bool: 是否更新成功 (False = 找不到記錄)
        """
        async with get_db_context() as db:
            result = await db.execute(
                select(IncidentRecord).where(
                    IncidentRecord.incident_id == incident_id
                )
            )
            record = result.scalar_one_or_none()

            if not record:
                logger.warning(
                    "update_outcome_not_found",
                    incident_id=incident_id,
                )
                return False

            record.outcome = outcome
            record.updated_at = updated_at or datetime.now(UTC)
            await db.commit()

            logger.debug(
                "incident_outcome_updated",
                incident_id=incident_id,
            )

            return True


    # -------------------------------------------------------------------------
    # Phase 16 R3.4 (2026-03-26 台北時區)
    # 新增: get_status() / update_status() - 支援 debug 端點重構
    # 執行者: Claude Code
    # -------------------------------------------------------------------------
    async def get_status(self, incident_id: str) -> str | None:
        """取得 Incident 的狀態 (for debug)"""
        async with get_db_context() as db:
            result = await db.execute(
                select(IncidentRecord).where(
                    IncidentRecord.incident_id == incident_id
                )
            )
            record = result.scalar_one_or_none()
            return record.status if record else None

    async def update_status(
        self,
        incident_id: str,
        status: str,
        updated_at: datetime | None = None,
        resolved_at: datetime | None = None,
    ) -> bool:
        """更新 Incident 的狀態 (for debug)"""
        async with get_db_context() as db:
            result = await db.execute(
                select(IncidentRecord).where(
                    IncidentRecord.incident_id == incident_id
                )
            )
            record = result.scalar_one_or_none()

            if not record:
                return False

            record.status = status
            record.updated_at = updated_at or datetime.now(UTC)
            if resolved_at is not None:
                record.resolved_at = resolved_at
            await db.commit()

            logger.debug(
                "incident_status_updated",
                incident_id=incident_id,
                status=status,
            )

            return True


# =============================================================================
# Singleton
# =============================================================================

_incident_repository: IncidentDBRepository | None = None


def get_incident_repository() -> IncidentDBRepository:
    """取得 IncidentDBRepository 實例 (Singleton)"""
    global _incident_repository
    if _incident_repository is None:
        _incident_repository = IncidentDBRepository()
    return _incident_repository
