//! Realm-related database queries.

use sqlx::{PgExecutor, PgPool};
use uuid::Uuid;

use crate::models::{CreateRealmRequest, Realm, RealmSummary};
use chattyness_error::AppError;

/// Create a new realm.
pub async fn create_realm(
    pool: &PgPool,
    owner_id: Uuid,
    req: &CreateRealmRequest,
) -> Result<Realm, AppError> {
    let privacy_str = req.privacy.as_str();
    let realm = sqlx::query_as::<_, Realm>(
        r#"
        INSERT INTO realm.realms (
            name, slug, description, tagline, owner_id,
            privacy, is_nsfw, max_users, allow_guest_access, theme_color
        )
        VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
        RETURNING
            id,
            name,
            slug,
            description,
            tagline,
            owner_id,
            privacy,
            is_nsfw,
            min_reputation_tier,
            theme_color,
            banner_image_path,
            thumbnail_path,
            max_users,
            allow_guest_access,
            default_scene_id,
            member_count,
            current_user_count,
            created_at,
            updated_at
        "#,
    )
    .bind(&req.name)
    .bind(&req.slug)
    .bind(&req.description)
    .bind(&req.tagline)
    .bind(owner_id)
    .bind(privacy_str)
    .bind(req.is_nsfw)
    .bind(req.max_users)
    .bind(req.allow_guest_access)
    .bind(&req.theme_color)
    .fetch_one(pool)
    .await?;

    Ok(realm)
}

/// Check if a realm slug is available.
pub async fn is_slug_available(pool: &PgPool, slug: &str) -> Result<bool, AppError> {
    let exists: (bool,) = sqlx::query_as(
        r#"SELECT EXISTS(SELECT 1 FROM realm.realms WHERE slug = $1)"#,
    )
    .bind(slug)
    .fetch_one(pool)
    .await?;

    Ok(!exists.0)
}

/// Get a realm by its slug.
pub async fn get_realm_by_slug<'e>(
    executor: impl PgExecutor<'e>,
    slug: &str,
) -> Result<Option<Realm>, AppError> {
    let realm = sqlx::query_as::<_, Realm>(
        r#"
        SELECT
            id,
            name,
            slug,
            description,
            tagline,
            owner_id,
            privacy,
            is_nsfw,
            min_reputation_tier,
            theme_color,
            banner_image_path,
            thumbnail_path,
            max_users,
            allow_guest_access,
            default_scene_id,
            member_count,
            current_user_count,
            created_at,
            updated_at
        FROM realm.realms
        WHERE slug = $1
        "#,
    )
    .bind(slug)
    .fetch_optional(executor)
    .await?;

    Ok(realm)
}

/// Get a realm by its ID.
pub async fn get_realm_by_id(pool: &PgPool, id: Uuid) -> Result<Option<Realm>, AppError> {
    let realm = sqlx::query_as::<_, Realm>(
        r#"
        SELECT
            id,
            name,
            slug,
            description,
            tagline,
            owner_id,
            privacy,
            is_nsfw,
            min_reputation_tier,
            theme_color,
            banner_image_path,
            thumbnail_path,
            max_users,
            allow_guest_access,
            default_scene_id,
            member_count,
            current_user_count,
            created_at,
            updated_at
        FROM realm.realms
        WHERE id = $1
        "#,
    )
    .bind(id)
    .fetch_optional(pool)
    .await?;

    Ok(realm)
}

/// List public realms.
pub async fn list_public_realms(
    pool: &PgPool,
    include_nsfw: bool,
    limit: i64,
    offset: i64,
) -> Result<Vec<RealmSummary>, AppError> {
    let realms = if include_nsfw {
        sqlx::query_as::<_, RealmSummary>(
            r#"
            SELECT
                id,
                name,
                slug,
                tagline,
                privacy,
                is_nsfw,
                thumbnail_path,
                member_count,
                current_user_count
            FROM realm.realms
            WHERE privacy = 'public'
            ORDER BY current_user_count DESC, member_count DESC
            LIMIT $1 OFFSET $2
            "#,
        )
        .bind(limit)
        .bind(offset)
        .fetch_all(pool)
        .await?
    } else {
        sqlx::query_as::<_, RealmSummary>(
            r#"
            SELECT
                id,
                name,
                slug,
                tagline,
                privacy,
                is_nsfw,
                thumbnail_path,
                member_count,
                current_user_count
            FROM realm.realms
            WHERE privacy = 'public' AND is_nsfw = false
            ORDER BY current_user_count DESC, member_count DESC
            LIMIT $1 OFFSET $2
            "#,
        )
        .bind(limit)
        .bind(offset)
        .fetch_all(pool)
        .await?
    };

    Ok(realms)
}

/// Get realms owned by a user.
pub async fn get_user_realms(pool: &PgPool, user_id: Uuid) -> Result<Vec<RealmSummary>, AppError> {
    let realms = sqlx::query_as::<_, RealmSummary>(
        r#"
        SELECT
            id,
            name,
            slug,
            tagline,
            privacy,
            is_nsfw,
            thumbnail_path,
            member_count,
            current_user_count
        FROM realm.realms
        WHERE owner_id = $1
        ORDER BY created_at DESC
        "#,
    )
    .bind(user_id)
    .fetch_all(pool)
    .await?;

    Ok(realms)
}
