"""Monitors API endpoints."""

from uuid import UUID

from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel, Field
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.dependencies import get_db, verify_api_key
from app.models.models import Monitor, MonitorResult

router = APIRouter()


class MonitorCreate(BaseModel):
    service_id: UUID
    url: str = Field(..., max_length=500)
    method: str = Field("GET", pattern=r"^(GET|POST|HEAD)$")
    expected_status: int = 200
    timeout_seconds: int = Field(10, ge=1, le=60)
    interval_seconds: int = Field(60, ge=30, le=3600)


class MonitorUpdate(BaseModel):
    url: str | None = Field(None, max_length=500)
    method: str | None = Field(None, pattern=r"^(GET|POST|HEAD)$")
    expected_status: int | None = None
    timeout_seconds: int | None = Field(None, ge=1, le=60)
    interval_seconds: int | None = Field(None, ge=30, le=3600)
    is_active: bool | None = None


def serialize_monitor(m: Monitor) -> dict:
    return {
        "id": m.id,
        "service_id": m.service_id,
        "url": m.url,
        "method": m.method,
        "expected_status": m.expected_status,
        "timeout_seconds": m.timeout_seconds,
        "interval_seconds": m.interval_seconds,
        "is_active": m.is_active,
        "created_at": m.created_at.isoformat() if m.created_at else None,
        "updated_at": m.updated_at.isoformat() if m.updated_at else None,
    }


@router.get("/")
async def list_monitors(db: AsyncSession = Depends(get_db)):
    """List all monitors."""
    result = await db.execute(select(Monitor))
    monitors = result.scalars().all()
    return [serialize_monitor(m) for m in monitors]


@router.post("/", status_code=status.HTTP_201_CREATED)
async def create_monitor(
    data: MonitorCreate,
    db: AsyncSession = Depends(get_db),
    api_key: str = Depends(verify_api_key),
):
    """Create a new monitor."""
    monitor = Monitor(
        service_id=str(data.service_id),
        url=data.url,
        method=data.method,
        expected_status=data.expected_status,
        timeout_seconds=data.timeout_seconds,
        interval_seconds=data.interval_seconds,
    )
    db.add(monitor)
    await db.flush()
    await db.refresh(monitor)
    return serialize_monitor(monitor)


@router.get("/{monitor_id}")
async def get_monitor(monitor_id: UUID, db: AsyncSession = Depends(get_db)):
    """Get a monitor with recent results."""
    result = await db.execute(select(Monitor).where(Monitor.id == str(monitor_id)))
    monitor = result.scalar_one_or_none()
    if not monitor:
        raise HTTPException(status_code=404, detail="Monitor not found")

    # Query recent results separately
    results_query = (
        select(MonitorResult)
        .where(MonitorResult.monitor_id == str(monitor_id))
        .order_by(MonitorResult.checked_at.desc())
        .limit(10)
    )
    results_result = await db.execute(results_query)
    recent_results = results_result.scalars().all()

    data = serialize_monitor(monitor)
    data["recent_results"] = [
        {
            "id": r.id,
            "status": r.status,
            "response_time_ms": r.response_time_ms,
            "status_code": r.status_code,
            "error_message": r.error_message,
            "checked_at": r.checked_at.isoformat() if r.checked_at else None,
        }
        for r in recent_results
    ]
    return data


@router.patch("/{monitor_id}")
async def update_monitor(
    monitor_id: UUID,
    data: MonitorUpdate,
    db: AsyncSession = Depends(get_db),
    api_key: str = Depends(verify_api_key),
):
    """Update a monitor."""
    result = await db.execute(select(Monitor).where(Monitor.id == str(monitor_id)))
    monitor = result.scalar_one_or_none()
    if not monitor:
        raise HTTPException(status_code=404, detail="Monitor not found")

    update_data = data.model_dump(exclude_unset=True)
    for field, value in update_data.items():
        setattr(monitor, field, value)

    await db.flush()
    await db.refresh(monitor)
    return serialize_monitor(monitor)


@router.delete("/{monitor_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_monitor(
    monitor_id: UUID,
    db: AsyncSession = Depends(get_db),
    api_key: str = Depends(verify_api_key),
):
    """Delete a monitor."""
    result = await db.execute(select(Monitor).where(Monitor.id == str(monitor_id)))
    monitor = result.scalar_one_or_none()
    if not monitor:
        raise HTTPException(status_code=404, detail="Monitor not found")
    await db.delete(monitor)


@router.post("/{monitor_id}/check")
async def trigger_check(
    monitor_id: UUID,
    db: AsyncSession = Depends(get_db),
    api_key: str = Depends(verify_api_key),
):
    """Trigger a manual uptime check for this monitor."""
    from app.services.uptime import check_monitor

    result = await db.execute(select(Monitor).where(Monitor.id == str(monitor_id)))
    monitor = result.scalar_one_or_none()
    if not monitor:
        raise HTTPException(status_code=404, detail="Monitor not found")

    monitor_result = await check_monitor(monitor, db)
    return {
        "status": monitor_result.status,
        "response_time_ms": monitor_result.response_time_ms,
        "status_code": monitor_result.status_code,
    }