"""
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, 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"""
    return Incident(
        incident_id=record.incident_id,
        status=IncidentStatus(record.status),
        severity=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,
    )


def _incident_to_record_data(incident: Incident) -> dict[str, Any]:
    """Convert Pydantic Incident to dict for DB record"""
    return {
        "incident_id": incident.incident_id,
        "status": incident.status.value,
        "severity": incident.severity.value,
        "signals": [s.model_dump() 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,
    }


# =============================================================================
# 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.value,
            IncidentStatus.MITIGATING.value,
        ]
        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,
    ) -> 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)
            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
