"""Reference data: readable by any authenticated user, writable by admins."""
from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy import select
from sqlalchemy.orm import Session, joinedload

from app.api.deps import get_current_user, require_admin
from app.db.session import get_db
from app.models.reference import Area, EquipmentType, Location, Zone
from app.models.user import User
from app.schemas.reference import (
    AreaCreate,
    AreaOut,
    AreaWithZoneOut,
    EquipmentTypeCreate,
    EquipmentTypeOut,
    LocationCreate,
    LocationOut,
    ZoneCreate,
    ZoneOut,
)

router = APIRouter(prefix="/reference", tags=["reference"])


def _conflict(field: str) -> HTTPException:
    return HTTPException(
        status_code=status.HTTP_409_CONFLICT, detail=f"{field} is already in use"
    )


# --- Zones ---


@router.get("/zones", response_model=list[ZoneOut])
def list_zones(db: Session = Depends(get_db), _: User = Depends(get_current_user)):
    return db.scalars(
        select(Zone).where(Zone.is_deleted.is_(False)).order_by(Zone.name)
    ).all()


@router.post("/zones", response_model=ZoneOut, status_code=status.HTTP_201_CREATED)
def create_zone(
    payload: ZoneCreate, db: Session = Depends(get_db), _: User = Depends(require_admin)
):
    if db.scalar(select(Zone).where(Zone.code == payload.code, Zone.is_deleted.is_(False))):
        raise _conflict("Zone code")
    zone = Zone(**payload.model_dump())
    db.add(zone)
    db.commit()
    db.refresh(zone)
    return zone


# --- Areas ---


@router.get("/areas", response_model=list[AreaWithZoneOut])
def list_areas(
    db: Session = Depends(get_db),
    _: User = Depends(get_current_user),
    zone_id: int | None = Query(None),
):
    stmt = (
        select(Area)
        .options(joinedload(Area.zone))
        .where(Area.is_deleted.is_(False))
        .order_by(Area.name)
    )
    if zone_id is not None:
        stmt = stmt.where(Area.zone_id == zone_id)
    return db.scalars(stmt).unique().all()


@router.post("/areas", response_model=AreaOut, status_code=status.HTTP_201_CREATED)
def create_area(
    payload: AreaCreate, db: Session = Depends(get_db), _: User = Depends(require_admin)
):
    if not db.scalar(select(Zone).where(Zone.id == payload.zone_id, Zone.is_deleted.is_(False))):
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Zone not found")
    if db.scalar(select(Area).where(Area.code == payload.code, Area.is_deleted.is_(False))):
        raise _conflict("Area code")
    area = Area(**payload.model_dump())
    db.add(area)
    db.commit()
    db.refresh(area)
    return area


# --- Locations ---


@router.get("/locations", response_model=list[LocationOut])
def list_locations(db: Session = Depends(get_db), _: User = Depends(get_current_user)):
    return db.scalars(
        select(Location).where(Location.is_deleted.is_(False)).order_by(Location.name)
    ).all()


@router.post("/locations", response_model=LocationOut, status_code=status.HTTP_201_CREATED)
def create_location(
    payload: LocationCreate, db: Session = Depends(get_db), _: User = Depends(require_admin)
):
    if db.scalar(
        select(Location).where(Location.code == payload.code, Location.is_deleted.is_(False))
    ):
        raise _conflict("Location code")
    location = Location(**payload.model_dump())
    if location.is_default:
        for other in db.scalars(select(Location).where(Location.is_default.is_(True))).all():
            other.is_default = False
    db.add(location)
    db.commit()
    db.refresh(location)
    return location


# --- Equipment types ---


@router.get("/equipment-types", response_model=list[EquipmentTypeOut])
def list_equipment_types(db: Session = Depends(get_db), _: User = Depends(get_current_user)):
    return db.scalars(
        select(EquipmentType)
        .where(EquipmentType.is_deleted.is_(False))
        .order_by(EquipmentType.name)
    ).all()


@router.post(
    "/equipment-types", response_model=EquipmentTypeOut, status_code=status.HTTP_201_CREATED
)
def create_equipment_type(
    payload: EquipmentTypeCreate,
    db: Session = Depends(get_db),
    _: User = Depends(require_admin),
):
    if db.scalar(
        select(EquipmentType).where(
            EquipmentType.code == payload.code, EquipmentType.is_deleted.is_(False)
        )
    ):
        raise _conflict("Equipment type code")
    eq_type = EquipmentType(**payload.model_dump())
    db.add(eq_type)
    db.commit()
    db.refresh(eq_type)
    return eq_type
