//! Inventory-related database queries.

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

use crate::models::{InventoryItem, PublicProp};
use chattyness_error::AppError;

/// List all inventory items for a user.
pub async fn list_user_inventory<'e>(
    executor: impl PgExecutor<'e>,
    user_id: Uuid,
) -> Result<Vec<InventoryItem>, AppError> {
    let items = sqlx::query_as::<_, InventoryItem>(
        r#"
        SELECT
            id,
            prop_name,
            prop_asset_path,
            layer,
            is_transferable,
            is_portable,
            is_droppable,
            origin,
            acquired_at
        FROM auth.inventory
        WHERE user_id = $1
        ORDER BY acquired_at DESC
        "#,
    )
    .bind(user_id)
    .fetch_all(executor)
    .await?;

    Ok(items)
}

/// Drop a prop (remove from inventory).
/// Returns an error if the prop is non-droppable (essential prop).
pub async fn drop_inventory_item<'e>(
    executor: impl PgExecutor<'e>,
    user_id: Uuid,
    item_id: Uuid,
) -> Result<(), AppError> {
    // Use a CTE to check existence/droppability and delete in a single query
    // Returns: (existed, was_droppable, was_deleted)
    let result: Option<(bool, bool, bool)> = sqlx::query_as(
        r#"
        WITH item_info AS (
            SELECT id, is_droppable
            FROM auth.inventory
            WHERE id = $1 AND user_id = $2
        ),
        deleted AS (
            DELETE FROM auth.inventory
            WHERE id = $1 AND user_id = $2 AND is_droppable = true
            RETURNING id
        )
        SELECT
            EXISTS(SELECT 1 FROM item_info) AS existed,
            COALESCE((SELECT is_droppable FROM item_info), false) AS was_droppable,
            EXISTS(SELECT 1 FROM deleted) AS was_deleted
        "#,
    )
    .bind(item_id)
    .bind(user_id)
    .fetch_optional(executor)
    .await?;

    match result {
        Some((false, _, _)) | None => {
            return Err(AppError::NotFound(
                "Inventory item not found or not owned by user".to_string(),
            ));
        }
        Some((true, false, _)) => {
            return Err(AppError::Forbidden(
                "This prop cannot be dropped - it is an essential prop".to_string(),
            ));
        }
        Some((true, true, true)) => {
            // Successfully deleted
        }
        Some((true, true, false)) => {
            // Should not happen - item existed, was droppable, but wasn't deleted
            return Err(AppError::Internal(
                "Unexpected error dropping inventory item".to_string(),
            ));
        }
    }

    Ok(())
}

/// List all public server props.
///
/// Returns props that are:
/// - Active (`is_active = true`)
/// - Public (`is_public = true`)
/// - Currently available (within availability window if set)
pub async fn list_public_server_props<'e>(
    executor: impl PgExecutor<'e>,
) -> Result<Vec<PublicProp>, AppError> {
    let props = sqlx::query_as::<_, PublicProp>(
        r#"
        SELECT
            id,
            name,
            asset_path,
            description
        FROM server.props
        WHERE is_active = true
          AND is_public = true
          AND (available_from IS NULL OR available_from <= now())
          AND (available_until IS NULL OR available_until > now())
        ORDER BY name ASC
        "#,
    )
    .fetch_all(executor)
    .await?;

    Ok(props)
}

/// List all public realm props for a specific realm.
///
/// Returns props that are:
/// - In the specified realm
/// - Active (`is_active = true`)
/// - Public (`is_public = true`)
/// - Currently available (within availability window if set)
pub async fn list_public_realm_props<'e>(
    executor: impl PgExecutor<'e>,
    realm_id: Uuid,
) -> Result<Vec<PublicProp>, AppError> {
    let props = sqlx::query_as::<_, PublicProp>(
        r#"
        SELECT
            id,
            name,
            asset_path,
            description
        FROM realm.props
        WHERE realm_id = $1
          AND is_active = true
          AND is_public = true
          AND (available_from IS NULL OR available_from <= now())
          AND (available_until IS NULL OR available_until > now())
        ORDER BY name ASC
        "#,
    )
    .bind(realm_id)
    .fetch_all(executor)
    .await?;

    Ok(props)
}
