"""
GUARDiA ITSM — SLA 타이머 & 자동 에스컬레이션 (Enhancement A-2)

기능:
  1. SR 생성 시 SLA 마감 시각 계산 (기관 sla_hours × 우선순위 배수)
  2. 매 30분 스케줄러 실행 → 마감 초과 SR 탐지
  3. 에스컬레이션: assigned_to → 해당 기관 PM → ADMIN 순으로 재배정
  4. 메신저/이메일 알림 발송

우선순위별 SLA 배수:
  CRITICAL → 0.5× (가장 짧음)
  HIGH     → 0.75×
  MEDIUM   → 1.0×  (기준값)
  LOW      → 2.0×

예시: 기관 sla_hours=4, CRITICAL SR → 마감 = 생성 후 2시간
"""
from __future__ import annotations

import logging
from datetime import datetime, timedelta, timezone
from typing import Optional

logger = logging.getLogger(__name__)

# 우선순위별 SLA 시간 배수
_SLA_MULTIPLIER: dict[str, float] = {
    "CRITICAL": 0.50,
    "HIGH":     0.75,
    "MEDIUM":   1.00,
    "LOW":      2.00,
}

# 기관 SLA 미설정 시 기본값 (시간)
_DEFAULT_SLA_HOURS = 8


def compute_sla_deadline(
    created_at: datetime,
    sla_hours: int,
    priority: str,
) -> datetime:
    """
    SR 생성 시각 + (sla_hours × 우선순위 배수)로 SLA 마감 시각 계산.

    Args:
        created_at: SR 생성 시각 (UTC 또는 로컬 naive datetime)
        sla_hours:  기관 SLA 기준 시간 (Institution.sla_hours)
        priority:   CRITICAL / HIGH / MEDIUM / LOW

    Returns:
        SLA 마감 datetime
    """
    multiplier = _SLA_MULTIPLIER.get(priority.upper(), 1.0)
    delta_hours = sla_hours * multiplier
    return created_at + timedelta(hours=delta_hours)


def is_sla_breached(sla_deadline: Optional[datetime]) -> bool:
    """현재 시각이 SLA 마감을 초과했는지 확인."""
    if not sla_deadline:
        return False
    now = datetime.now()
    # timezone-aware vs naive 혼용 방지
    if sla_deadline.tzinfo is not None:
        now = datetime.now(timezone.utc)
    return now > sla_deadline


def sla_remaining_minutes(sla_deadline: Optional[datetime]) -> Optional[int]:
    """
    SLA 잔여 시간(분). 음수면 초과.
    sla_deadline이 None 이면 None 반환.
    """
    if not sla_deadline:
        return None
    now = datetime.now()
    if sla_deadline.tzinfo is not None:
        now = datetime.now(timezone.utc)
    delta = sla_deadline - now
    return int(delta.total_seconds() / 60)


# ── SR 생성 시 SLA 마감 자동 계산 ─────────────────────────────────────────────

async def set_sla_on_create(sr_id: str, db) -> None:
    """
    SR 생성 직후 호출 — 기관 sla_hours 조회 후 sla_deadline 계산·저장.

    routers/tasks.py의 create_sr() 내부에서 호출:
        from core.sla import set_sla_on_create
        await set_sla_on_create(sr.sr_id, db)
    """
    from sqlalchemy import select
    from models import SRRequest, Institution

    sr = (await db.execute(
        select(SRRequest).where(SRRequest.sr_id == sr_id)
    )).scalars().first()

    if not sr or sr.sla_deadline:
        return

    # 기관 SLA 시간 조회
    sla_hours = _DEFAULT_SLA_HOURS
    if sr.inst_id:
        inst = (await db.execute(
            select(Institution).where(Institution.id == sr.inst_id)
        )).scalars().first()
        if inst and inst.sla_hours:
            sla_hours = inst.sla_hours

    sr.sla_deadline = compute_sla_deadline(sr.created_at, sla_hours, sr.priority)
    await db.commit()
    logger.debug(
        "SLA 마감 설정: sr_id=%s priority=%s deadline=%s",
        sr_id, sr.priority, sr.sla_deadline,
    )


# ── 에스컬레이션 ─────────────────────────────────────────────────────────────

async def escalate_sr(sr_id: str, db) -> Optional[str]:
    """
    SLA 초과 SR을 에스컬레이션합니다.

    에스컬레이션 체인:
      1. 기관 담당자(role=PM) → ITSM PM 사용자
      2. 없으면 → ADMIN 사용자

    Returns:
        에스컬레이션 대상 username 또는 None
    """
    from sqlalchemy import select
    from models import SRRequest, User, UserRole, Institution, InstContact

    sr = (await db.execute(
        select(SRRequest).where(SRRequest.sr_id == sr_id)
    )).scalars().first()

    if not sr or sr.escalated_at:
        return None   # 이미 에스컬레이션됨

    escalate_target: Optional[str] = None

    # 1차: 기관 PM 연락처 조회
    if sr.inst_id:
        contact = (await db.execute(
            select(InstContact).where(
                InstContact.inst_id == sr.inst_id,
                InstContact.role == "PM",
                InstContact.is_active.is_(True),
            ).order_by(InstContact.is_primary.desc())
        )).scalars().first()
        if contact and contact.email:
            escalate_target = contact.email

    # 2차: ITSM PM 사용자 조회
    if not escalate_target:
        pm_user = (await db.execute(
            select(User).where(
                User.role == UserRole.PM,
                User.is_active.is_(True),
            ).limit(1)
        )).scalars().first()
        if pm_user:
            escalate_target = pm_user.username

    # 3차: ADMIN 사용자 조회
    if not escalate_target:
        admin = (await db.execute(
            select(User).where(
                User.role == UserRole.ADMIN,
                User.is_active.is_(True),
            ).limit(1)
        )).scalars().first()
        if admin:
            escalate_target = admin.username

    if not escalate_target:
        logger.warning("SLA 에스컬레이션 대상 없음: sr_id=%s", sr_id)
        return None

    now = datetime.now()
    previous_assignee = sr.assigned_to
    sr.assigned_to   = escalate_target
    sr.escalated_at  = now
    sr.escalated_to  = escalate_target
    sr.sla_breached  = True
    sr.updated_at    = now
    await db.commit()

    logger.warning(
        "SLA 에스컬레이션: sr_id=%s %s → %s",
        sr_id, previous_assignee, escalate_target,
    )
    return escalate_target


# ── 스케줄러 작업 — 매 30분 실행 ─────────────────────────────────────────────

async def check_sla_violations() -> None:
    """
    SLA 마감이 지났으나 미완료 상태인 SR을 탐지하여 에스컬레이션.

    core/scheduler.py start_scheduler()에서 등록:
        scheduler.add_job(check_sla_violations, 'interval', minutes=30, ...)
    """
    from database import SessionLocal
    from models import SRRequest, SRStatus
    from sqlalchemy import select, and_
    from core.notify import send_messenger

    # 최종 상태가 아닌 SR 목록
    terminal = {SRStatus.COMPLETED, SRStatus.REJECTED, SRStatus.FAILED_ROLLBACK}

    try:
        async with SessionLocal() as db:
            now = datetime.now()
            # sla_deadline 초과 + 미완료 + 에스컬레이션 미발생
            candidates = (await db.execute(
                select(SRRequest).where(
                    and_(
                        SRRequest.sla_deadline.isnot(None),
                        SRRequest.sla_deadline < now,
                        SRRequest.sla_breached.is_(False),
                        SRRequest.status.notin_([s.value for s in terminal]),
                    )
                )
            )).scalars().all()

        if not candidates:
            logger.debug("SLA 위반 SR 없음")
            return

        logger.warning("SLA 위반 SR 탐지: %d건", len(candidates))

        for sr in candidates:
            async with SessionLocal() as db:
                target = await escalate_sr(sr.sr_id, db)
            if target:
                # 에스컬레이션 알림
                overdue_min = abs(sla_remaining_minutes(sr.sla_deadline) or 0)
                msg = (
                    f"🚨 SLA 위반 에스컬레이션\n"
                    f"SR: {sr.sr_id}\n"
                    f"제목: {sr.title}\n"
                    f"우선순위: {sr.priority}\n"
                    f"초과 시간: {overdue_min // 60}시간 {overdue_min % 60}분\n"
                    f"담당자 변경: {sr.escalated_to}"
                )
                try:
                    await send_messenger(
                        room="ops",
                        text=msg,
                        title=f"[SLA 위반] {sr.sr_id}",
                    )
                except Exception:
                    pass  # 알림 실패는 에스컬레이션을 막지 않음

    except Exception as exc:
        logger.error("SLA 위반 점검 오류: %s", exc, exc_info=True)
