"""
Alert Operation Log Repository - Phase 11
==========================================
告警操作完整溯源 (Event Sourcing)

2026-04-08 Claude Code: 統帥指令「所有操作都必須被記錄，寫入資料庫」

設計:
- 不可變 (Immutable) — 只 INSERT，不 UPDATE/DELETE
- fire-and-forget 友善 — 所有寫入錯誤只記錄 log，不拋出
- leWOOOgo 積木化: Router → Service → Repository → DB
"""

from typing import Any

import structlog
from sqlalchemy import func, select

from src.db.base import get_db_context
from src.db.models import AlertOperationLog

logger = structlog.get_logger(__name__)

# 合法的 event_type 值 (對應 DB ENUM)
# Sprint 5.1 新增 8 個 (2026-04-08 Claude Sonnet 4.6 Asia/Taipei)
ALERT_EVENT_TYPES = {
    # 原有 10 個
    "ALERT_RECEIVED",
    "TELEGRAM_SENT",
    "USER_ACTION",
    "AUTO_REPAIR_TRIGGERED",
    "EXECUTION_STARTED",
    "EXECUTION_COMPLETED",
    "TELEGRAM_RESULT_SENT",
    "RESOLVED",
    "SILENCED",
    "ESCALATED",
    # Sprint 5.1 Guardrail / Pre-flight / MultiSig / 備份追蹤
    "GUARDRAIL_BLOCKED",
    "PRE_FLIGHT_PASSED",
    "PRE_FLIGHT_FAILED",
    "BACKUP_TRIGGERED",
    "BACKUP_COMPLETED",
    "BACKUP_FAILED",
    "APPROVAL_ESCALATED",
    "CHANGE_APPLIED",
    # ADR-071 通知生命週期 (2026-04-11 Claude Sonnet 4.6 Asia/Taipei)
    "NOTIFICATION_CLASSIFIED",
    "MANUAL_FIX_RECORDED",
    "KM_CONVERTED",
    "PLAYBOOK_DRAFT_CREATED",
    "STATE_GUARD_BLOCKED",
}


class AlertOperationLogRepository:
    """
    告警操作日誌 Repository

    所有 write 方法設計為 fire-and-forget 友善：
    失敗時只記錄 warning log，不中斷主流程。
    """

    async def append(
        self,
        event_type: str,
        *,
        incident_id: str | None = None,
        approval_id: str | None = None,
        audit_log_id: str | None = None,
        auto_repair_id: str | None = None,
        actor: str | None = None,
        action_detail: str | None = None,
        success: bool | None = None,
        error_message: str | None = None,
        context: dict[str, Any] | None = None,
    ) -> AlertOperationLog | None:
        """
        寫入一筆操作事件

        Args:
            event_type: 事件類型 (見 ALERT_EVENT_TYPES)
            incident_id: 關聯 incident
            approval_id: 關聯 approval_record
            audit_log_id: 關聯 audit_log
            auto_repair_id: 關聯 auto_repair_execution
            actor: 觸發者 (alertmanager / telegram:user_id / auto_repair / system)
            action_detail: 具體動作描述
            success: 結果 (None=不適用)
            error_message: 錯誤訊息
            context: 額外結構化資訊

        Returns:
            寫入的記錄，失敗時返回 None
        """
        if event_type not in ALERT_EVENT_TYPES:
            logger.warning(
                "alert_op_log_invalid_event_type",
                event_type=event_type,
                valid_types=list(ALERT_EVENT_TYPES),
            )
            return None

        try:
            async with get_db_context() as db:
                record = AlertOperationLog(
                    incident_id=incident_id,
                    approval_id=approval_id,
                    audit_log_id=audit_log_id,
                    auto_repair_id=auto_repair_id,
                    event_type=event_type,
                    actor=actor,
                    action_detail=action_detail,
                    success=success,
                    error_message=error_message,
                    context=context or {},
                )
                db.add(record)
                await db.flush()
                await db.refresh(record)
                logger.debug(
                    "alert_op_log_appended",
                    event_type=event_type,
                    incident_id=incident_id,
                    approval_id=approval_id,
                )
                return record
        except Exception as e:
            logger.error(
                "alert_op_log_write_failed",
                event_type=event_type,
                incident_id=incident_id,
                error=str(e),
            )
            return None

    async def list_by_incident(
        self,
        incident_id: str,
        limit: int = 100,
    ) -> list[AlertOperationLog]:
        """查詢某 incident 的完整操作時間軸"""
        async with get_db_context() as db:
            result = await db.execute(
                select(AlertOperationLog)
                .where(AlertOperationLog.incident_id == incident_id)
                .order_by(AlertOperationLog.created_at.asc())
                .limit(limit)
            )
            return list(result.scalars().all())

    async def list_by_approval(
        self,
        approval_id: str,
    ) -> list[AlertOperationLog]:
        """查詢某 approval 的操作記錄"""
        async with get_db_context() as db:
            result = await db.execute(
                select(AlertOperationLog)
                .where(AlertOperationLog.approval_id == approval_id)
                .order_by(AlertOperationLog.created_at.asc())
            )
            return list(result.scalars().all())

    async def list_recent(
        self,
        limit: int = 50,
        offset: int = 0,
        event_type: str | None = None,
        incident_id: str | None = None,
    ) -> tuple[list[AlertOperationLog], int]:
        """通用分頁列表（最新優先）"""
        from sqlalchemy import func as sa_func

        async with get_db_context() as db:
            query = select(AlertOperationLog)
            count_query = select(sa_func.count()).select_from(AlertOperationLog)

            if event_type:
                query = query.where(AlertOperationLog.event_type == event_type)
                count_query = count_query.where(AlertOperationLog.event_type == event_type)
            if incident_id:
                query = query.where(AlertOperationLog.incident_id == incident_id)
                count_query = count_query.where(AlertOperationLog.incident_id == incident_id)

            total_result = await db.execute(count_query)
            total = total_result.scalar() or 0

            result = await db.execute(
                query.order_by(AlertOperationLog.created_at.desc())
                .limit(limit)
                .offset(offset)
            )
            return list(result.scalars().all()), total

    async def get_stats(self, since_hours: int = 24) -> dict[str, Any]:
        """統計最近 N 小時的事件分佈"""
        from datetime import timedelta
        from src.utils.timezone import now_taipei

        since = now_taipei() - timedelta(hours=since_hours)
        async with get_db_context() as db:
            # 按事件類型統計
            type_result = await db.execute(
                select(
                    AlertOperationLog.event_type,
                    func.count(AlertOperationLog.id),
                )
                .where(AlertOperationLog.created_at >= since)
                .group_by(AlertOperationLog.event_type)
            )
            by_type = {str(row[0]): row[1] for row in type_result.all()}

            # 總計
            total = sum(by_type.values())

            return {
                "total": total,
                "since_hours": since_hours,
                "by_event_type": by_type,
            }


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

_alert_op_log_repo: AlertOperationLogRepository | None = None


def get_alert_operation_log_repository() -> AlertOperationLogRepository:
    """取得 AlertOperationLogRepository 實例 (Singleton)"""
    global _alert_op_log_repo
    if _alert_op_log_repo is None:
        _alert_op_log_repo = AlertOperationLogRepository()
    return _alert_op_log_repo
