//! Avatar API handlers for user UI.
//!
//! Handles avatar data retrieval and slot updates.
//! Note: Emotion switching is handled via WebSocket.

use axum::Json;
use axum::extract::Path;

use chattyness_db::{
    models::{AssignSlotRequest, AvatarWithPaths, ClearSlotRequest, RealmAvatarWithPaths, ServerAvatarWithPaths},
    queries::{avatars, realm_avatars, realms, server_avatars},
};
use chattyness_error::AppError;
use sqlx::PgPool;
use axum::extract::State;
use uuid::Uuid;

use crate::auth::{AuthUser, RlsConn};

/// Get full avatar with all paths resolved.
///
/// GET /api/realms/{slug}/avatar
///
/// Returns the complete avatar data with all inventory UUIDs resolved to asset paths.
/// This enables client-side emotion availability computation and rendering without
/// additional server queries.
pub async fn get_avatar(
    rls_conn: RlsConn,
    AuthUser(user): AuthUser,
    Path(slug): Path<String>,
) -> Result<Json<AvatarWithPaths>, AppError> {
    let mut conn = rls_conn.acquire().await;

    // Get realm
    let realm = realms::get_realm_by_slug(&mut *conn, &slug)
        .await?
        .ok_or_else(|| AppError::NotFound(format!("Realm '{}' not found", slug)))?;

    // Get full avatar with paths
    let avatar = avatars::get_avatar_with_paths_conn(&mut *conn, user.id, realm.id)
        .await?
        .unwrap_or_default();

    Ok(Json(avatar))
}

/// Assign an inventory item to an avatar slot.
///
/// PUT /api/realms/{slug}/avatar/slot
///
/// Assigns the specified inventory item to the given layer and position
/// in the user's active avatar.
pub async fn assign_slot(
    rls_conn: RlsConn,
    AuthUser(user): AuthUser,
    Path(slug): Path<String>,
    Json(req): Json<AssignSlotRequest>,
) -> Result<Json<AvatarWithPaths>, AppError> {
    // Guests cannot customize their avatar
    if user.is_guest() {
        return Err(AppError::Forbidden(
            "Avatar customization is disabled for guests, please register first.".to_string(),
        ));
    }

    req.validate()?;

    let mut conn = rls_conn.acquire().await;

    // Get realm
    let realm = realms::get_realm_by_slug(&mut *conn, &slug)
        .await?
        .ok_or_else(|| AppError::NotFound(format!("Realm '{}' not found", slug)))?;

    // Update the slot
    let column_name = req.column_name();
    avatars::update_avatar_slot(
        &mut *conn,
        user.id,
        realm.id,
        &column_name,
        Some(req.inventory_item_id),
    )
    .await?;

    // Return updated avatar
    let avatar = avatars::get_avatar_with_paths_conn(&mut *conn, user.id, realm.id)
        .await?
        .unwrap_or_default();

    Ok(Json(avatar))
}

/// Clear an avatar slot.
///
/// DELETE /api/realms/{slug}/avatar/slot
///
/// Removes any inventory item from the given layer and position
/// in the user's active avatar.
pub async fn clear_slot(
    rls_conn: RlsConn,
    AuthUser(user): AuthUser,
    Path(slug): Path<String>,
    Json(req): Json<ClearSlotRequest>,
) -> Result<Json<AvatarWithPaths>, AppError> {
    // Guests cannot customize their avatar
    if user.is_guest() {
        return Err(AppError::Forbidden(
            "Avatar customization is disabled for guests, please register first.".to_string(),
        ));
    }

    req.validate()?;

    let mut conn = rls_conn.acquire().await;

    // Get realm
    let realm = realms::get_realm_by_slug(&mut *conn, &slug)
        .await?
        .ok_or_else(|| AppError::NotFound(format!("Realm '{}' not found", slug)))?;

    // Clear the slot
    let column_name = req.column_name();
    avatars::update_avatar_slot(&mut *conn, user.id, realm.id, &column_name, None).await?;

    // Return updated avatar
    let avatar = avatars::get_avatar_with_paths_conn(&mut *conn, user.id, realm.id)
        .await?
        .unwrap_or_default();

    Ok(Json(avatar))
}

// =============================================================================
// Avatar Store Endpoints
// =============================================================================

/// List public server avatars with resolved paths.
///
/// GET /api/server/avatars
///
/// Returns all public, active server avatars that users can select from.
/// Includes resolved asset paths for client-side rendering.
pub async fn list_server_avatars(
    State(pool): State<PgPool>,
) -> Result<Json<Vec<ServerAvatarWithPaths>>, AppError> {
    let avatars = server_avatars::list_public_server_avatars_with_paths(&pool).await?;
    Ok(Json(avatars))
}

/// List public realm avatars with resolved paths.
///
/// GET /api/realms/{slug}/avatars
///
/// Returns all public, active realm avatars for the specified realm.
/// Includes resolved asset paths for client-side rendering.
pub async fn list_realm_avatars(
    State(pool): State<PgPool>,
    Path(slug): Path<String>,
) -> Result<Json<Vec<RealmAvatarWithPaths>>, AppError> {
    // Get realm
    let realm = realms::get_realm_by_slug(&pool, &slug)
        .await?
        .ok_or_else(|| AppError::NotFound(format!("Realm '{}' not found", slug)))?;

    let avatars = realm_avatars::list_public_realm_avatars_with_paths(&pool, realm.id).await?;
    Ok(Json(avatars))
}

/// Request to select an avatar from the store.
#[derive(Debug, serde::Deserialize)]
pub struct SelectAvatarRequest {
    /// The avatar ID to select
    pub avatar_id: Uuid,
    /// Source: "server" or "realm"
    pub source: AvatarSelectionSource,
}

/// Avatar selection source.
#[derive(Debug, serde::Deserialize, Clone, Copy)]
#[serde(rename_all = "snake_case")]
pub enum AvatarSelectionSource {
    Server,
    Realm,
}

/// Response after selecting an avatar.
#[derive(Debug, serde::Serialize)]
pub struct SelectAvatarResponse {
    pub success: bool,
}

/// Select an avatar from the store.
///
/// POST /api/realms/{slug}/avatar/select
///
/// Selects a server or realm avatar to use. This has lower priority than
/// a custom avatar but higher priority than default avatars.
pub async fn select_avatar(
    rls_conn: RlsConn,
    AuthUser(user): AuthUser,
    Path(slug): Path<String>,
    Json(req): Json<SelectAvatarRequest>,
) -> Result<Json<SelectAvatarResponse>, AppError> {
    let mut conn = rls_conn.acquire().await;

    // Get realm
    let realm = realms::get_realm_by_slug(&mut *conn, &slug)
        .await?
        .ok_or_else(|| AppError::NotFound(format!("Realm '{}' not found", slug)))?;

    // Verify the avatar exists and is public
    match req.source {
        AvatarSelectionSource::Server => {
            let avatar = server_avatars::get_server_avatar_by_id(&mut *conn, req.avatar_id)
                .await?
                .ok_or_else(|| AppError::NotFound("Server avatar not found".to_string()))?;
            if !avatar.is_public || !avatar.is_active {
                return Err(AppError::NotFound("Server avatar not available".to_string()));
            }
            avatars::select_server_avatar(&mut *conn, user.id, realm.id, req.avatar_id).await?;
        }
        AvatarSelectionSource::Realm => {
            let avatar = realm_avatars::get_realm_avatar_by_id(&mut *conn, req.avatar_id)
                .await?
                .ok_or_else(|| AppError::NotFound("Realm avatar not found".to_string()))?;
            if avatar.realm_id != realm.id {
                return Err(AppError::NotFound("Realm avatar not found in this realm".to_string()));
            }
            if !avatar.is_public || !avatar.is_active {
                return Err(AppError::NotFound("Realm avatar not available".to_string()));
            }
            avatars::select_realm_avatar(&mut *conn, user.id, realm.id, req.avatar_id).await?;
        }
    }

    Ok(Json(SelectAvatarResponse { success: true }))
}

/// Clear avatar selection response.
#[derive(Debug, serde::Serialize)]
pub struct ClearAvatarSelectionResponse {
    pub success: bool,
}

/// Clear avatar selection.
///
/// DELETE /api/realms/{slug}/avatar/selection
///
/// Clears both server and realm avatar selections, reverting to default
/// avatar behavior (custom avatar if available, otherwise defaults).
pub async fn clear_avatar_selection(
    rls_conn: RlsConn,
    AuthUser(user): AuthUser,
    Path(slug): Path<String>,
) -> Result<Json<ClearAvatarSelectionResponse>, AppError> {
    let mut conn = rls_conn.acquire().await;

    // Get realm
    let realm = realms::get_realm_by_slug(&mut *conn, &slug)
        .await?
        .ok_or_else(|| AppError::NotFound(format!("Realm '{}' not found", slug)))?;

    avatars::clear_avatar_selection(&mut *conn, user.id, realm.id).await?;

    Ok(Json(ClearAvatarSelectionResponse { success: true }))
}
