"""
알림 규칙 CRUD API (모바일 기능 #45).

엔드포인트:
  GET    /api/alert-rules/            — 내 알림 규칙 목록 (tenant 필터)
  POST   /api/alert-rules/            — 알림 규칙 생성
  PUT    /api/alert-rules/{id}        — 알림 규칙 수정
  DELETE /api/alert-rules/{id}        — 알림 규칙 삭제
  PATCH  /api/alert-rules/{id}/toggle — 활성/비활성 토글

AlertRule: target_type(server/service/sr), metric(cpu/memory/disk/sla),
           threshold, operator(>/</=), channel(push/inapp/sms), enabled
"""
from __future__ import annotations

from datetime import datetime
from typing import List, Optional

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, ConfigDict, field_validator
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from core.auth import get_current_user
from database import get_db
from models import AlertRule, User

router = APIRouter(prefix="/api/alert-rules", tags=["Alert Rules"])

_VALID_TARGET = {"server", "service", "sr"}
_VALID_METRIC = {"cpu", "memory", "disk", "sla"}
_VALID_OPERATOR = {">", "<", "="}
_VALID_CHANNEL = {"push", "inapp", "sms"}


def _tenant_of(user: User) -> str:
    """사용자의 테넌트 식별자 — inst_code 우선, 없으면 username 단위 격리."""
    return user.inst_code or f"user:{user.username}"


class AlertRuleCreate(BaseModel):
    target_type: str
    target_id: Optional[str] = None
    metric: str
    threshold: float
    operator: str = ">"
    channel: str = "inapp"
    enabled: bool = True

    @field_validator("target_type")
    @classmethod
    def _v_target(cls, v: str) -> str:
        if v not in _VALID_TARGET:
            raise ValueError(f"target_type은 {_VALID_TARGET} 중 하나여야 합니다.")
        return v

    @field_validator("metric")
    @classmethod
    def _v_metric(cls, v: str) -> str:
        if v not in _VALID_METRIC:
            raise ValueError(f"metric은 {_VALID_METRIC} 중 하나여야 합니다.")
        return v

    @field_validator("operator")
    @classmethod
    def _v_op(cls, v: str) -> str:
        if v not in _VALID_OPERATOR:
            raise ValueError(f"operator는 {_VALID_OPERATOR} 중 하나여야 합니다.")
        return v

    @field_validator("channel")
    @classmethod
    def _v_ch(cls, v: str) -> str:
        if v not in _VALID_CHANNEL:
            raise ValueError(f"channel은 {_VALID_CHANNEL} 중 하나여야 합니다.")
        return v


class AlertRuleUpdate(BaseModel):
    target_type: Optional[str] = None
    target_id: Optional[str] = None
    metric: Optional[str] = None
    threshold: Optional[float] = None
    operator: Optional[str] = None
    channel: Optional[str] = None
    enabled: Optional[bool] = None


class AlertRuleOut(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: int
    target_type: str
    target_id: Optional[str]
    metric: str
    threshold: float
    operator: str
    channel: str
    enabled: bool
    created_by: Optional[str]
    created_at: Optional[datetime]


@router.get("/", response_model=List[AlertRuleOut])
async def list_alert_rules(
    enabled: Optional[bool] = None,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """내 테넌트의 알림 규칙 목록."""
    q = select(AlertRule).where(AlertRule.tenant_id == _tenant_of(current_user))
    if enabled is not None:
        q = q.where(AlertRule.enabled == enabled)
    q = q.order_by(AlertRule.created_at.desc())
    rows = (await db.execute(q)).scalars().all()
    return rows


@router.post("/", response_model=AlertRuleOut, status_code=201)
async def create_alert_rule(
    payload: AlertRuleCreate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    rule = AlertRule(
        tenant_id=_tenant_of(current_user),
        target_type=payload.target_type,
        target_id=payload.target_id,
        metric=payload.metric,
        threshold=payload.threshold,
        operator=payload.operator,
        channel=payload.channel,
        enabled=payload.enabled,
        created_by=current_user.username,
    )
    db.add(rule)
    await db.commit()
    await db.refresh(rule)
    return rule


async def _get_owned_rule(rule_id: int, db: AsyncSession, user: User) -> AlertRule:
    rule = await db.get(AlertRule, rule_id)
    if not rule or rule.tenant_id != _tenant_of(user):
        raise HTTPException(404, "알림 규칙을 찾을 수 없습니다.")
    return rule


@router.put("/{rule_id}", response_model=AlertRuleOut)
async def update_alert_rule(
    rule_id: int,
    payload: AlertRuleUpdate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    rule = await _get_owned_rule(rule_id, db, current_user)
    data = payload.model_dump(exclude_unset=True)
    # 유효성 검증
    if "target_type" in data and data["target_type"] not in _VALID_TARGET:
        raise HTTPException(422, f"target_type은 {_VALID_TARGET} 중 하나여야 합니다.")
    if "metric" in data and data["metric"] not in _VALID_METRIC:
        raise HTTPException(422, f"metric은 {_VALID_METRIC} 중 하나여야 합니다.")
    if "operator" in data and data["operator"] not in _VALID_OPERATOR:
        raise HTTPException(422, f"operator는 {_VALID_OPERATOR} 중 하나여야 합니다.")
    if "channel" in data and data["channel"] not in _VALID_CHANNEL:
        raise HTTPException(422, f"channel은 {_VALID_CHANNEL} 중 하나여야 합니다.")
    for k, v in data.items():
        setattr(rule, k, v)
    rule.updated_at = datetime.now()
    await db.commit()
    await db.refresh(rule)
    return rule


@router.delete("/{rule_id}", status_code=204)
async def delete_alert_rule(
    rule_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    rule = await _get_owned_rule(rule_id, db, current_user)
    await db.delete(rule)
    await db.commit()


@router.patch("/{rule_id}/toggle", response_model=AlertRuleOut)
async def toggle_alert_rule(
    rule_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """알림 규칙 활성/비활성 토글."""
    rule = await _get_owned_rule(rule_id, db, current_user)
    rule.enabled = not rule.enabled
    rule.updated_at = datetime.now()
    await db.commit()
    await db.refresh(rule)
    return rule
