//! Realm management API handlers.

use axum::{
    Json,
    extract::{Path, Query, State},
};
use chattyness_db::{
    models::{
        CreateRealmAvatarRequest, OwnerCreateRealmRequest, RealmAvatar, RealmAvatarSummary,
        RealmDetail, RealmListItem, UpdateRealmAvatarRequest, UpdateRealmRequest,
    },
    queries::{owner as queries, realm_avatars},
};
use chattyness_error::{AppError, OptionExt};
use serde::{Deserialize, Serialize};
use sqlx::PgPool;
use uuid::Uuid;

use crate::auth::AdminConn;

/// Create realm response.
#[derive(Debug, Serialize)]
pub struct CreateRealmResponse {
    pub realm_id: Uuid,
    pub slug: String,
    pub owner_id: Uuid,
    pub owner_temporary_password: Option<String>,
}

/// Transfer ownership request.
#[derive(Debug, Deserialize)]
pub struct TransferOwnershipRequest {
    pub new_owner_id: Uuid,
}

/// List query params.
#[derive(Debug, Deserialize)]
pub struct ListRealmsQuery {
    pub q: Option<String>,
    pub limit: Option<i64>,
    pub offset: Option<i64>,
}

/// List realms with optional search.
pub async fn list_realms(
    State(pool): State<PgPool>,
    Query(query): Query<ListRealmsQuery>,
) -> Result<Json<Vec<RealmListItem>>, AppError> {
    let limit = query.limit.unwrap_or(25).min(100);
    let offset = query.offset.unwrap_or(0);

    let realms = if let Some(ref q) = query.q {
        queries::search_realms(&pool, q, limit).await?
    } else {
        queries::list_realms_with_owner(&pool, limit, offset).await?
    };

    Ok(Json(realms))
}

/// Get a realm by slug.
pub async fn get_realm(
    State(pool): State<PgPool>,
    Path(slug): Path<String>,
) -> Result<Json<RealmDetail>, AppError> {
    let realm = queries::get_realm_by_slug(&pool, &slug).await?;
    Ok(Json(realm))
}

/// Create a new realm.
///
/// Note: Uses State(pool) instead of AdminConn because create_realm_with_new_owner
/// uses transactions internally that require pool access.
pub async fn create_realm(
    State(pool): State<PgPool>,
    Json(req): Json<OwnerCreateRealmRequest>,
) -> Result<Json<CreateRealmResponse>, AppError> {
    req.validate()?;

    // If owner_id is provided, create realm with existing user
    if let Some(owner_id) = req.owner_id {
        let realm_id = queries::create_realm(
            &pool,
            owner_id,
            &req.name,
            &req.slug,
            req.description.as_deref(),
            req.tagline.as_deref(),
            req.privacy,
            req.is_nsfw,
            req.max_users,
            req.allow_guest_access,
            req.theme_color.as_deref(),
        )
        .await?;

        Ok(Json(CreateRealmResponse {
            realm_id,
            slug: req.slug,
            owner_id,
            owner_temporary_password: None,
        }))
    } else {
        // Create realm with new user as owner
        let (realm_id, user_id, temporary_password) =
            queries::create_realm_with_new_owner(&pool, &req).await?;

        Ok(Json(CreateRealmResponse {
            realm_id,
            slug: req.slug,
            owner_id: user_id,
            owner_temporary_password: Some(temporary_password),
        }))
    }
}

/// Update a realm.
///
/// Note: Uses State(pool) because owner queries use &PgPool directly.
pub async fn update_realm(
    State(pool): State<PgPool>,
    Path(slug): Path<String>,
    Json(req): Json<UpdateRealmRequest>,
) -> Result<Json<RealmDetail>, AppError> {
    req.validate()?;

    // First get the realm to find its ID
    let existing = queries::get_realm_by_slug(&pool, &slug).await?;
    let realm = queries::update_realm(&pool, existing.id, &req).await?;
    Ok(Json(realm))
}

/// Transfer realm ownership.
///
/// Note: Uses State(pool) instead of AdminConn because transfer_realm_ownership
/// uses transactions internally that require pool access.
pub async fn transfer_ownership(
    State(pool): State<PgPool>,
    Path(slug): Path<String>,
    Json(req): Json<TransferOwnershipRequest>,
) -> Result<Json<()>, AppError> {
    // First get the realm to find its ID
    let existing = queries::get_realm_by_slug(&pool, &slug).await?;
    queries::transfer_realm_ownership(&pool, existing.id, req.new_owner_id).await?;
    Ok(Json(()))
}

// =============================================================================
// Realm Avatar Handlers
// =============================================================================

/// Response for realm avatar creation.
#[derive(Debug, Serialize)]
pub struct CreateRealmAvatarResponse {
    pub id: Uuid,
    pub slug: String,
    pub name: String,
    pub is_public: bool,
    pub created_at: chrono::DateTime<chrono::Utc>,
}

impl From<RealmAvatar> for CreateRealmAvatarResponse {
    fn from(avatar: RealmAvatar) -> Self {
        Self {
            id: avatar.id,
            slug: avatar.slug,
            name: avatar.name,
            is_public: avatar.is_public,
            created_at: avatar.created_at,
        }
    }
}

/// List all avatars for a realm.
pub async fn list_realm_avatars(
    State(pool): State<PgPool>,
    Path(slug): Path<String>,
) -> Result<Json<Vec<RealmAvatarSummary>>, AppError> {
    let realm = queries::get_realm_by_slug(&pool, &slug).await?;
    let avatars = realm_avatars::list_all_realm_avatars(&pool, realm.id).await?;
    Ok(Json(avatars))
}

/// Create a new realm avatar.
pub async fn create_realm_avatar(
    State(pool): State<PgPool>,
    admin_conn: AdminConn,
    Path(slug): Path<String>,
    Json(req): Json<CreateRealmAvatarRequest>,
) -> Result<Json<CreateRealmAvatarResponse>, AppError> {
    let conn = admin_conn.0;
    let mut guard = conn.acquire().await;

    // Validate the request
    req.validate()?;

    // Get realm ID (uses pool because owner queries require it)
    let realm = queries::get_realm_by_slug(&pool, &slug).await?;

    // Check slug availability
    let avatar_slug = req.slug_or_generate();
    let available =
        realm_avatars::is_avatar_slug_available(&mut *guard, realm.id, &avatar_slug).await?;
    if !available {
        return Err(AppError::Conflict(format!(
            "Avatar slug '{}' is already taken in this realm",
            avatar_slug
        )));
    }

    // Create the avatar
    let avatar = realm_avatars::create_realm_avatar(&mut *guard, realm.id, &req, None).await?;

    tracing::info!(
        "Created realm avatar: {} ({}) in realm {}",
        avatar.name,
        avatar.id,
        slug
    );

    Ok(Json(CreateRealmAvatarResponse::from(avatar)))
}

/// Get a realm avatar by ID.
pub async fn get_realm_avatar(
    State(pool): State<PgPool>,
    Path((slug, avatar_id)): Path<(String, Uuid)>,
) -> Result<Json<RealmAvatar>, AppError> {
    // Verify realm exists
    let _realm = queries::get_realm_by_slug(&pool, &slug).await?;

    let avatar = realm_avatars::get_realm_avatar_by_id(&pool, avatar_id)
        .await?
        .or_not_found("Avatar")?;
    Ok(Json(avatar))
}

/// Update a realm avatar.
pub async fn update_realm_avatar(
    State(pool): State<PgPool>,
    admin_conn: AdminConn,
    Path((slug, avatar_id)): Path<(String, Uuid)>,
    Json(req): Json<UpdateRealmAvatarRequest>,
) -> Result<Json<RealmAvatar>, AppError> {
    let conn = admin_conn.0;
    let mut guard = conn.acquire().await;

    // Validate the request
    req.validate()?;

    // Verify realm exists (uses pool because owner queries require it)
    let _realm = queries::get_realm_by_slug(&pool, &slug).await?;

    // Check avatar exists
    let existing = realm_avatars::get_realm_avatar_by_id(&mut *guard, avatar_id)
        .await?
        .or_not_found("Avatar")?;

    // Update the avatar
    let avatar = realm_avatars::update_realm_avatar(&mut *guard, avatar_id, &req).await?;

    tracing::info!(
        "Updated realm avatar: {} ({}) in realm {}",
        existing.name,
        avatar_id,
        slug
    );

    Ok(Json(avatar))
}

/// Delete a realm avatar.
pub async fn delete_realm_avatar(
    State(pool): State<PgPool>,
    admin_conn: AdminConn,
    Path((slug, avatar_id)): Path<(String, Uuid)>,
) -> Result<Json<()>, AppError> {
    let conn = admin_conn.0;
    let mut guard = conn.acquire().await;

    // Verify realm exists (uses pool because owner queries require it)
    let _realm = queries::get_realm_by_slug(&pool, &slug).await?;

    // Get the avatar first to log its name
    let avatar = realm_avatars::get_realm_avatar_by_id(&mut *guard, avatar_id)
        .await?
        .or_not_found("Avatar")?;

    // Delete from database
    realm_avatars::delete_realm_avatar(&mut *guard, avatar_id).await?;

    tracing::info!(
        "Deleted realm avatar: {} ({}) from realm {}",
        avatar.name,
        avatar_id,
        slug
    );

    Ok(Json(()))
}
