"""
GUARDiA ITSM — 운영 이벤트 타임라인 API (Enhancement A-4)

기능:
  - SR 생성/상태 변경, 배포(VibeSession), 배치 실행(BatchRun),
    On-Call 당직, 인시던트, SLA 위반 이벤트를 단일 타임라인으로 통합
  - 기간/유형/우선순위 필터
  - 무한 스크롤 페이지네이션 (cursor 기반)

엔드포인트:
  GET /api/timeline                    — 통합 이벤트 타임라인
  GET /api/timeline/summary            — 기간별 이벤트 수 요약
"""
from __future__ import annotations

import logging
from datetime import date, datetime, timedelta
from typing import List, Optional

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy import select, and_, or_
from sqlalchemy.ext.asyncio import AsyncSession

from core.auth import get_current_user
from database import get_db
from models import (
    User, UserRole,
    SRRequest, SRStatus,
    VibeSession, VibeSessionStatus,
    AuditLog,
)

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/timeline", tags=["timeline"])

# ── 이벤트 타입 정의 ──────────────────────────────────────────────────────────
EVENT_TYPES = {
    "sr_created",
    "sr_status_changed",
    "sr_sla_violated",
    "sr_escalated",
    "deploy_started",
    "deploy_completed",
    "deploy_failed",
    "batch_started",
    "batch_completed",
    "batch_failed",
    "oncall_assigned",
    "incident_created",
    "incident_resolved",
}


def _require_non_customer(current_user: User) -> None:
    if current_user.role == UserRole.CUSTOMER:
        raise HTTPException(403, "권한이 없습니다.")


# ── 이벤트 수집 헬퍼 ─────────────────────────────────────────────────────────

async def _collect_sr_events(db, start: datetime, end: datetime, filter_types: set) -> List[dict]:
    """SR 생성 + SLA 위반 + 에스컬레이션 이벤트."""
    events = []
    if not ({"sr_created", "sr_sla_violated", "sr_escalated"} & filter_types):
        return events

    rows = (await db.execute(
        select(SRRequest).where(
            and_(
                SRRequest.created_at >= start,
                SRRequest.created_at <= end,
            )
        ).order_by(SRRequest.created_at.desc()).limit(500)
    )).scalars().all()

    for sr in rows:
        if "sr_created" in filter_types:
            events.append({
                "id":         f"sr_created_{sr.sr_id}",
                "type":       "sr_created",
                "timestamp":  sr.created_at.isoformat(),
                "title":      f"SR 접수: {sr.title}",
                "detail":     f"우선순위: {sr.priority} | 담당: {sr.assigned_to or '미배정'}",
                "priority":   sr.priority,
                "ref_id":     sr.sr_id,
                "actor":      sr.requested_by,
                "icon":       "ticket",
                "color":      "#2563eb",
            })
        if "sr_sla_violated" in filter_types and sr.sla_breached:
            events.append({
                "id":         f"sr_sla_violated_{sr.sr_id}",
                "type":       "sr_sla_violated",
                "timestamp":  sr.escalated_at.isoformat() if sr.escalated_at else sr.created_at.isoformat(),
                "title":      f"SLA 위반: {sr.title}",
                "detail":     f"담당: {sr.assigned_to or '미배정'} | 에스컬레이션: {sr.escalated_to or '없음'}",
                "priority":   sr.priority,
                "ref_id":     sr.sr_id,
                "actor":      "SYSTEM",
                "icon":       "alert",
                "color":      "#dc2626",
            })
        if "sr_escalated" in filter_types and sr.escalated_at:
            events.append({
                "id":         f"sr_escalated_{sr.sr_id}",
                "type":       "sr_escalated",
                "timestamp":  sr.escalated_at.isoformat(),
                "title":      f"에스컬레이션: {sr.title}",
                "detail":     f"에스컬레이션 대상: {sr.escalated_to}",
                "priority":   sr.priority,
                "ref_id":     sr.sr_id,
                "actor":      "SYSTEM",
                "icon":       "escalate",
                "color":      "#ea580c",
            })
    return events


async def _collect_audit_events(db, start: datetime, end: datetime, filter_types: set) -> List[dict]:
    """감사 로그 기반 SR 상태 변경 이벤트."""
    if "sr_status_changed" not in filter_types:
        return []

    rows = (await db.execute(
        select(AuditLog).where(
            and_(
                AuditLog.action == "STATUS_CHANGED",
                AuditLog.created_at >= start,
                AuditLog.created_at <= end,
            )
        ).order_by(AuditLog.created_at.desc()).limit(500)
    )).scalars().all()

    return [
        {
            "id":        f"sr_status_{row.id}",
            "type":      "sr_status_changed",
            "timestamp": row.created_at.isoformat(),
            "title":     f"SR 상태 변경: {row.sr_id}",
            "detail":    row.detail or "",
            "priority":  None,
            "ref_id":    row.sr_id,
            "actor":     row.actor,
            "icon":      "refresh",
            "color":     "#0891b2",
        }
        for row in rows
    ]


async def _collect_deploy_events(db, start: datetime, end: datetime, filter_types: set) -> List[dict]:
    """배포 세션 이벤트 (시작/완료/실패)."""
    deploy_types = {"deploy_started", "deploy_completed", "deploy_failed"}
    if not (deploy_types & filter_types):
        return []

    rows = (await db.execute(
        select(VibeSession).where(
            and_(
                VibeSession.started_at >= start,
                VibeSession.started_at <= end,
                VibeSession.status.notin_([VibeSessionStatus.PENDING, VibeSessionStatus.CODING]),
            )
        ).order_by(VibeSession.started_at.desc()).limit(200)
    )).scalars().all()

    events = []
    for s in rows:
        if "deploy_started" in filter_types and s.started_at:
            events.append({
                "id":        f"deploy_start_{s.id}",
                "type":      "deploy_started",
                "timestamp": s.started_at.isoformat(),
                "title":     f"배포 시작: 세션 #{s.id}",
                "detail":    f"SR: {s.sr_id or '없음'} | 시작: {s.started_by or '시스템'}",
                "priority":  None,
                "ref_id":    str(s.id),
                "actor":     s.started_by or "system",
                "icon":      "rocket",
                "color":     "#7c3aed",
            })
        if s.status == VibeSessionStatus.COMPLETED and "deploy_completed" in filter_types and s.deployed_at:
            events.append({
                "id":        f"deploy_done_{s.id}",
                "type":      "deploy_completed",
                "timestamp": s.deployed_at.isoformat(),
                "title":     f"배포 완료: 세션 #{s.id}",
                "detail":    (s.deploy_log or "")[:100],
                "priority":  None,
                "ref_id":    str(s.id),
                "actor":     s.started_by or "system",
                "icon":      "check-circle",
                "color":     "#16a34a",
            })
        if s.status == VibeSessionStatus.FAILED and "deploy_failed" in filter_types:
            ts = s.deployed_at or s.started_at
            events.append({
                "id":        f"deploy_fail_{s.id}",
                "type":      "deploy_failed",
                "timestamp": ts.isoformat() if ts else datetime.now().isoformat(),
                "title":     f"배포 실패: 세션 #{s.id}",
                "detail":    (s.error_msg or "")[:100],
                "priority":  None,
                "ref_id":    str(s.id),
                "actor":     s.started_by or "system",
                "icon":      "x-circle",
                "color":     "#dc2626",
            })
    return events


async def _collect_batch_events(db, start: datetime, end: datetime, filter_types: set) -> List[dict]:
    """배치 실행 이벤트."""
    batch_types = {"batch_started", "batch_completed", "batch_failed"}
    if not (batch_types & filter_types):
        return []

    try:
        from models import BatchRun, BatchRunResult, BatchJob
        rows = (await db.execute(
            select(BatchRun, BatchJob).join(
                BatchJob, BatchRun.job_id == BatchJob.id, isouter=True
            ).where(
                and_(
                    BatchRun.started_at >= start,
                    BatchRun.started_at <= end,
                )
            ).order_by(BatchRun.started_at.desc()).limit(200)
        )).all()

        events = []
        for run, job in rows:
            job_name = job.job_name if job else f"배치 #{run.job_id}"
            if "batch_started" in filter_types:
                events.append({
                    "id":        f"batch_start_{run.id}",
                    "type":      "batch_started",
                    "timestamp": run.started_at.isoformat(),
                    "title":     f"배치 시작: {job_name}",
                    "detail":    f"Run ID: {run.id}",
                    "priority":  None,
                    "ref_id":    str(run.id),
                    "actor":     "scheduler",
                    "icon":      "cpu",
                    "color":     "#6366f1",
                })
            if run.result == BatchRunResult.SUCCESS and "batch_completed" in filter_types and run.ended_at:
                events.append({
                    "id":        f"batch_done_{run.id}",
                    "type":      "batch_completed",
                    "timestamp": run.ended_at.isoformat(),
                    "title":     f"배치 완료: {job_name}",
                    "detail":    f"종료코드: {run.exit_code}",
                    "priority":  None,
                    "ref_id":    str(run.id),
                    "actor":     "scheduler",
                    "icon":      "check",
                    "color":     "#16a34a",
                })
            if run.result in (BatchRunResult.FAILED, BatchRunResult.TIMEOUT) and "batch_failed" in filter_types and run.ended_at:
                events.append({
                    "id":        f"batch_fail_{run.id}",
                    "type":      "batch_failed",
                    "timestamp": run.ended_at.isoformat(),
                    "title":     f"배치 실패: {job_name} ({run.result})",
                    "detail":    (run.error_msg or "")[:100],
                    "priority":  None,
                    "ref_id":    str(run.id),
                    "actor":     "scheduler",
                    "icon":      "alert-triangle",
                    "color":     "#dc2626",
                })
        return events
    except Exception:
        return []


# ═══════════════════════════════════════════════════════════════════════════════
# ── 엔드포인트 ────────────────────────────────────────────────────────────────
# ═══════════════════════════════════════════════════════════════════════════════

@router.get("")
async def get_timeline(
    days: int              = Query(7, ge=1, le=90, description="조회 기간(일)"),
    event_types: Optional[str] = Query(None, description="콤마 구분 이벤트 타입 필터"),
    priority: Optional[str]    = Query(None, description="우선순위 필터 (CRITICAL/HIGH/MEDIUM/LOW)"),
    skip: int = Query(0, ge=0),
    limit: int = Query(50, ge=1, le=200),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """
    통합 운영 이벤트 타임라인.

    SR/배포/배치/인시던트/온콜 이벤트를 시간 역순으로 통합 반환.
    다양한 소스에서 이벤트를 수집하여 단일 피드로 제공합니다.
    """
    _require_non_customer(current_user)

    end   = datetime.now()
    start = end - timedelta(days=days)

    # 필터 타입 파싱
    if event_types:
        filter_types = {t.strip() for t in event_types.split(",") if t.strip() in EVENT_TYPES}
    else:
        filter_types = set(EVENT_TYPES)  # 전체

    # 각 소스에서 이벤트 병렬 수집
    import asyncio
    results = await asyncio.gather(
        _collect_sr_events(db, start, end, filter_types),
        _collect_audit_events(db, start, end, filter_types),
        _collect_deploy_events(db, start, end, filter_types),
        _collect_batch_events(db, start, end, filter_types),
        return_exceptions=True,
    )

    all_events = []
    for r in results:
        if isinstance(r, list):
            all_events.extend(r)
        elif isinstance(r, Exception):
            logger.debug("타임라인 이벤트 수집 오류: %s", r)

    # 우선순위 필터
    if priority:
        all_events = [e for e in all_events if e.get("priority") == priority.upper()]

    # 시간 역순 정렬
    all_events.sort(key=lambda e: e["timestamp"], reverse=True)

    total = len(all_events)
    paged = all_events[skip:skip + limit]

    return {
        "from":         start.isoformat(),
        "to":           end.isoformat(),
        "total":        total,
        "skip":         skip,
        "limit":        limit,
        "has_more":     (skip + limit) < total,
        "event_types":  sorted(filter_types),
        "events":       paged,
    }


@router.get("/summary")
async def timeline_summary(
    days: int = Query(7, ge=1, le=90),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """기간별 이벤트 수 요약 (일별 카운트)."""
    _require_non_customer(current_user)

    end   = datetime.now()
    start = end - timedelta(days=days)

    # 빠른 카운트 집계 (SR만 기준)
    sr_rows = (await db.execute(
        select(SRRequest).where(
            SRRequest.created_at >= start,
            SRRequest.created_at <= end,
        )
    )).scalars().all()

    deploy_rows = (await db.execute(
        select(VibeSession).where(
            VibeSession.started_at >= start,
            VibeSession.started_at <= end,
        )
    )).scalars().all()

    by_day: dict[str, dict] = {}
    for d_offset in range(days):
        d = (end - timedelta(days=d_offset)).date()
        by_day[d.isoformat()] = {"sr": 0, "deploy": 0, "sla_violation": 0}

    for sr in sr_rows:
        key = sr.created_at.date().isoformat()
        if key in by_day:
            by_day[key]["sr"] += 1
            if sr.sla_breached:
                by_day[key]["sla_violation"] += 1

    for s in deploy_rows:
        key = s.started_at.date().isoformat()
        if key in by_day:
            by_day[key]["deploy"] += 1

    sorted_days = sorted(by_day.items())
    return {
        "from":    start.date().isoformat(),
        "to":      end.date().isoformat(),
        "data": [
            {"date": d, **counts}
            for d, counts in sorted_days
        ],
        "totals": {
            "sr":            sum(v["sr"] for v in by_day.values()),
            "deploy":        sum(v["deploy"] for v in by_day.values()),
            "sla_violation": sum(v["sla_violation"] for v in by_day.values()),
        },
    }
