//! Props-related database queries.

use sqlx::PgExecutor;
use uuid::Uuid;

use crate::models::{CreateServerPropRequest, ServerProp, ServerPropSummary};
use chattyness_error::AppError;

/// List all server props.
pub async fn list_server_props<'e>(
    executor: impl PgExecutor<'e>,
) -> Result<Vec<ServerPropSummary>, AppError> {
    let props = sqlx::query_as::<_, ServerPropSummary>(
        r#"
        SELECT
            id,
            name,
            slug,
            asset_path,
            default_layer,
            is_active,
            created_at
        FROM server.props
        ORDER BY name ASC
        "#,
    )
    .fetch_all(executor)
    .await?;

    Ok(props)
}

/// Get a server prop by ID.
pub async fn get_server_prop_by_id<'e>(
    executor: impl PgExecutor<'e>,
    prop_id: Uuid,
) -> Result<Option<ServerProp>, AppError> {
    let prop = sqlx::query_as::<_, ServerProp>(
        r#"
        SELECT
            id,
            name,
            slug,
            description,
            tags,
            asset_path,
            thumbnail_path,
            default_layer,
            default_emotion,
            default_position,
            is_unique,
            is_transferable,
            is_portable,
            is_active,
            available_from,
            available_until,
            created_by,
            created_at,
            updated_at
        FROM server.props
        WHERE id = $1
        "#,
    )
    .bind(prop_id)
    .fetch_optional(executor)
    .await?;

    Ok(prop)
}

/// Check if a prop slug is available.
pub async fn is_prop_slug_available<'e>(
    executor: impl PgExecutor<'e>,
    slug: &str,
) -> Result<bool, AppError> {
    let exists: (bool,) =
        sqlx::query_as(r#"SELECT EXISTS(SELECT 1 FROM server.props WHERE slug = $1)"#)
            .bind(slug)
            .fetch_one(executor)
            .await?;

    Ok(!exists.0)
}

/// Create a new server prop.
pub async fn create_server_prop<'e>(
    executor: impl PgExecutor<'e>,
    req: &CreateServerPropRequest,
    asset_path: &str,
    created_by: Option<Uuid>,
) -> Result<ServerProp, AppError> {
    let slug = req.slug_or_generate();

    // Positioning: either content layer OR emotion layer OR neither (all NULL)
    // Database constraint enforces mutual exclusivity
    let (default_layer, default_emotion, default_position) =
        if req.default_layer.is_some() {
            // Content layer prop
            (
                req.default_layer.map(|l| l.to_string()),
                None,
                Some(req.default_position.unwrap_or(4)), // Default to center position
            )
        } else if req.default_emotion.is_some() {
            // Emotion layer prop
            (
                None,
                req.default_emotion.map(|e| e.to_string()),
                Some(req.default_position.unwrap_or(4)), // Default to center position
            )
        } else {
            // Non-avatar prop
            (None, None, None)
        };

    let prop = sqlx::query_as::<_, ServerProp>(
        r#"
        INSERT INTO server.props (
            name, slug, description, tags, asset_path,
            default_layer, default_emotion, default_position,
            created_by
        )
        VALUES (
            $1, $2, $3, $4, $5,
            $6::props.avatar_layer, $7::props.emotion_state, $8,
            $9
        )
        RETURNING
            id,
            name,
            slug,
            description,
            tags,
            asset_path,
            thumbnail_path,
            default_layer,
            default_emotion,
            default_position,
            is_unique,
            is_transferable,
            is_portable,
            is_active,
            available_from,
            available_until,
            created_by,
            created_at,
            updated_at
        "#,
    )
    .bind(&req.name)
    .bind(&slug)
    .bind(&req.description)
    .bind(&req.tags)
    .bind(asset_path)
    .bind(&default_layer)
    .bind(&default_emotion)
    .bind(default_position)
    .bind(created_by)
    .fetch_one(executor)
    .await?;

    Ok(prop)
}

/// Delete a server prop.
pub async fn delete_server_prop<'e>(
    executor: impl PgExecutor<'e>,
    prop_id: Uuid,
) -> Result<(), AppError> {
    let result = sqlx::query(r#"DELETE FROM server.props WHERE id = $1"#)
        .bind(prop_id)
        .execute(executor)
        .await?;

    if result.rows_affected() == 0 {
        return Err(AppError::NotFound("Prop not found".to_string()));
    }

    Ok(())
}
