"""Incidents API endpoints."""

from datetime import datetime
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 Incident, IncidentUpdate

router = APIRouter()


class IncidentCreate(BaseModel):
    service_id: UUID
    title: str = Field(..., max_length=200)
    status: str = Field(..., pattern=r"^(investigating|identified|monitoring|resolved)$")
    severity: str = Field(..., pattern=r"^(minor|major|outage)$")
    started_at: datetime | None = None


class IncidentUpdateCreate(BaseModel):
    status: str = Field(..., pattern=r"^(investigating|identified|monitoring|resolved)$")
    body: str


class IncidentPatch(BaseModel):
    title: str | None = None
    status: str | None = Field(None, pattern=r"^(investigating|identified|monitoring|resolved)$")
    severity: str | None = Field(None, pattern=r"^(minor|major|outage)$")


def serialize_incident(i: Incident) -> dict:
    return {
        "id": i.id,
        "service_id": i.service_id,
        "title": i.title,
        "status": i.status,
        "severity": i.severity,
        "started_at": i.started_at.isoformat() if i.started_at else None,
        "resolved_at": i.resolved_at.isoformat() if i.resolved_at else None,
        "created_at": i.created_at.isoformat() if i.created_at else None,
        "updated_at": i.updated_at.isoformat() if i.updated_at else None,
    }


async def serialize_incident_detail(i: Incident, db: AsyncSession) -> dict:
    """Serialize incident with its updates, querying explicitly to avoid lazy-load issues."""
    data = serialize_incident(i)
    # Explicitly query updates instead of relying on lazy-loaded relationship
    result = await db.execute(
        select(IncidentUpdate)
        .where(IncidentUpdate.incident_id == i.id)
        .order_by(IncidentUpdate.created_at)
    )
    updates = result.scalars().all()
    data["updates"] = [
        {
            "id": u.id,
            "status": u.status,
            "body": u.body,
            "created_at": u.created_at.isoformat() if u.created_at else None,
        }
        for u in updates
    ]
    return data


@router.get("/")
async def list_incidents(
    service_id: UUID | None = None,
    status: str | None = None,
    limit: int = 50,
    offset: int = 0,
    db: AsyncSession = Depends(get_db),
):
    """List incidents with optional filtering."""
    query = select(Incident).order_by(Incident.started_at.desc())
    if service_id:
        query = query.where(Incident.service_id == str(service_id))
    if status:
        query = query.where(Incident.status == status)
    query = query.offset(offset).limit(limit)
    result = await db.execute(query)
    incidents = result.scalars().all()
    return [serialize_incident(i) for i in incidents]


@router.post("/", status_code=status.HTTP_201_CREATED)
async def create_incident(
    data: IncidentCreate,
    db: AsyncSession = Depends(get_db),
    api_key: str = Depends(verify_api_key),
):
    """Create a new incident."""
    incident = Incident(
        service_id=str(data.service_id),
        title=data.title,
        status=data.status,
        severity=data.severity,
        started_at=data.started_at or datetime.utcnow(),
    )
    db.add(incident)
    await db.flush()
    await db.refresh(incident)
    return serialize_incident(incident)


@router.get("/{incident_id}")
async def get_incident(incident_id: UUID, db: AsyncSession = Depends(get_db)):
    """Get an incident with its updates."""
    result = await db.execute(select(Incident).where(Incident.id == str(incident_id)))
    incident = result.scalar_one_or_none()
    if not incident:
        raise HTTPException(status_code=404, detail="Incident not found")
    return await serialize_incident_detail(incident, db)


@router.patch("/{incident_id}")
async def update_incident(
    incident_id: UUID,
    data: IncidentPatch,
    db: AsyncSession = Depends(get_db),
    api_key: str = Depends(verify_api_key),
):
    """Update incident fields (title, status, severity)."""
    result = await db.execute(select(Incident).where(Incident.id == str(incident_id)))
    incident = result.scalar_one_or_none()
    if not incident:
        raise HTTPException(status_code=404, detail="Incident not found")

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

    # If status changed to resolved, set resolved_at
    if data.status == "resolved" and "status" in update_data:
        incident.resolved_at = datetime.utcnow()

    await db.flush()
    await db.refresh(incident)
    return await serialize_incident_detail(incident, db)


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


@router.post("/{incident_id}/updates", status_code=status.HTTP_201_CREATED)
async def create_incident_update(
    incident_id: UUID,
    data: IncidentUpdateCreate,
    db: AsyncSession = Depends(get_db),
    api_key: str = Depends(verify_api_key),
):
    """Add an update to an incident."""
    result = await db.execute(select(Incident).where(Incident.id == str(incident_id)))
    incident = result.scalar_one_or_none()
    if not incident:
        raise HTTPException(status_code=404, detail="Incident not found")

    update = IncidentUpdate(
        incident_id=str(incident_id),
        status=data.status,
        body=data.body,
    )
    db.add(update)
    # Also update incident status
    incident.status = data.status
    # If resolved, set resolved_at
    if data.status == "resolved":
        incident.resolved_at = datetime.utcnow()

    await db.flush()
    await db.refresh(update)
    return {
        "id": update.id,
        "incident_id": update.incident_id,
        "status": update.status,
        "body": update.body,
        "created_at": update.created_at.isoformat() if update.created_at else None,
    }