//! Spot management API handlers for admin UI.

use axum::{
    Json,
    extract::{Path, State},
};
use chattyness_db::{
    models::{CreateSpotRequest, Spot, SpotSummary, UpdateSpotRequest},
    queries::spots,
};
use chattyness_error::{AppError, OptionExt};
use serde::Serialize;
use sqlx::PgPool;
use uuid::Uuid;

use crate::auth::AdminConn;

/// List all spots for a scene.
pub async fn list_spots(
    State(pool): State<PgPool>,
    Path(scene_id): Path<Uuid>,
) -> Result<Json<Vec<SpotSummary>>, AppError> {
    let spot_list = spots::list_spots_for_scene(&pool, scene_id).await?;
    Ok(Json(spot_list))
}

/// Get a spot by ID.
pub async fn get_spot(
    State(pool): State<PgPool>,
    Path(spot_id): Path<Uuid>,
) -> Result<Json<Spot>, AppError> {
    let spot = spots::get_spot_by_id(&pool, spot_id)
        .await?
        .or_not_found("Spot")?;
    Ok(Json(spot))
}

/// Create spot response.
#[derive(Debug, Serialize)]
pub struct CreateSpotResponse {
    pub id: Uuid,
}

/// Create a new spot in a scene.
pub async fn create_spot(
    admin_conn: AdminConn,
    Path(scene_id): Path<Uuid>,
    Json(req): Json<CreateSpotRequest>,
) -> Result<Json<CreateSpotResponse>, AppError> {
    let conn = admin_conn.0;
    let mut guard = conn.acquire().await;

    // Check if slug is available (if provided)
    if let Some(ref slug) = req.slug {
        let available = spots::is_spot_slug_available(&mut *guard, scene_id, slug).await?;
        if !available {
            return Err(AppError::Conflict(format!(
                "Spot slug '{}' is already taken in this scene",
                slug
            )));
        }
    }

    let spot = spots::create_spot(&mut *guard, scene_id, &req).await?;
    Ok(Json(CreateSpotResponse { id: spot.id }))
}

/// Update a spot.
pub async fn update_spot(
    admin_conn: AdminConn,
    Path(spot_id): Path<Uuid>,
    Json(req): Json<UpdateSpotRequest>,
) -> Result<Json<Spot>, AppError> {
    let conn = admin_conn.0;
    let mut guard = conn.acquire().await;

    // If updating slug, check availability
    if let Some(ref new_slug) = req.slug {
        let existing = spots::get_spot_by_id(&mut *guard, spot_id)
            .await?
            .or_not_found("Spot")?;

        if Some(new_slug.clone()) != existing.slug {
            let available =
                spots::is_spot_slug_available(&mut *guard, existing.scene_id, new_slug).await?;
            if !available {
                return Err(AppError::Conflict(format!(
                    "Spot slug '{}' is already taken in this scene",
                    new_slug
                )));
            }
        }
    }

    let spot = spots::update_spot(&mut *guard, spot_id, &req).await?;
    Ok(Json(spot))
}

/// Delete a spot.
pub async fn delete_spot(
    admin_conn: AdminConn,
    Path(spot_id): Path<Uuid>,
) -> Result<Json<()>, AppError> {
    let conn = admin_conn.0;
    let mut guard = conn.acquire().await;
    spots::delete_spot(&mut *guard, spot_id).await?;
    Ok(Json(()))
}
