//! Membership-related database queries.

use sqlx::PgPool;
use uuid::Uuid;

use crate::models::{Membership, MembershipWithRealm, RealmRole, ServerRole};
use chattyness_error::AppError;

/// Get a user's membership in a specific realm.
pub async fn get_user_membership(
    pool: &PgPool,
    user_id: Uuid,
    realm_id: Uuid,
) -> Result<Option<Membership>, AppError> {
    let membership = sqlx::query_as::<_, Membership>(
        r#"
        SELECT
            id,
            realm_id,
            user_id,
            role,
            nickname,
            created_at AS joined_at,
            last_visited_at
        FROM realm.memberships
        WHERE user_id = $1 AND realm_id = $2
        "#,
    )
    .bind(user_id)
    .bind(realm_id)
    .fetch_optional(pool)
    .await?;

    Ok(membership)
}

/// Create a new membership (join a realm).
pub async fn create_membership(
    pool: &PgPool,
    user_id: Uuid,
    realm_id: Uuid,
    role: RealmRole,
) -> Result<Uuid, AppError> {
    let (membership_id,): (Uuid,) = sqlx::query_as(
        r#"
        INSERT INTO realm.memberships (realm_id, user_id, role)
        VALUES ($1, $2, $3)
        RETURNING id
        "#,
    )
    .bind(realm_id)
    .bind(user_id)
    .bind(role)
    .fetch_one(pool)
    .await?;

    // Update member count on the realm
    sqlx::query("UPDATE realm.realms SET member_count = member_count + 1 WHERE id = $1")
        .bind(realm_id)
        .execute(pool)
        .await?;

    Ok(membership_id)
}

/// Create a new membership using a connection (for RLS support).
pub async fn create_membership_conn(
    conn: &mut sqlx::PgConnection,
    user_id: Uuid,
    realm_id: Uuid,
    role: RealmRole,
) -> Result<Uuid, AppError> {
    let (membership_id,): (Uuid,) = sqlx::query_as(
        r#"
        INSERT INTO realm.memberships (realm_id, user_id, role)
        VALUES ($1, $2, $3)
        RETURNING id
        "#,
    )
    .bind(realm_id)
    .bind(user_id)
    .bind(role)
    .fetch_one(&mut *conn)
    .await?;

    // Update member count on the realm
    sqlx::query("UPDATE realm.realms SET member_count = member_count + 1 WHERE id = $1")
        .bind(realm_id)
        .execute(&mut *conn)
        .await?;

    Ok(membership_id)
}

/// Get all realm memberships for a user.
pub async fn get_user_memberships(
    pool: &PgPool,
    user_id: Uuid,
) -> Result<Vec<MembershipWithRealm>, AppError> {
    let memberships = sqlx::query_as::<_, MembershipWithRealm>(
        r#"
        SELECT
            m.id AS membership_id,
            m.realm_id,
            r.name AS realm_name,
            r.slug AS realm_slug,
            r.privacy AS realm_privacy,
            m.role,
            m.nickname,
            m.last_visited_at
        FROM realm.memberships m
        JOIN realm.realms r ON m.realm_id = r.id
        WHERE m.user_id = $1
        ORDER BY m.last_visited_at DESC NULLS LAST
        "#,
    )
    .bind(user_id)
    .fetch_all(pool)
    .await?;

    Ok(memberships)
}

/// Get a user's server staff role (if any).
pub async fn get_user_staff_role(
    pool: &PgPool,
    user_id: Uuid,
) -> Result<Option<ServerRole>, AppError> {
    let result: Option<(ServerRole,)> = sqlx::query_as(
        r#"
        SELECT role
        FROM server.staff
        WHERE user_id = $1
        "#,
    )
    .bind(user_id)
    .fetch_optional(pool)
    .await?;

    Ok(result.map(|(role,)| role))
}

/// Update last visited timestamp for a membership.
pub async fn update_last_visited(
    pool: &PgPool,
    user_id: Uuid,
    realm_id: Uuid,
) -> Result<(), AppError> {
    sqlx::query(
        r#"
        UPDATE realm.memberships
        SET last_visited_at = now()
        WHERE user_id = $1 AND realm_id = $2
        "#,
    )
    .bind(user_id)
    .bind(realm_id)
    .execute(pool)
    .await?;

    Ok(())
}

/// Update last visited timestamp using a connection (for RLS support).
pub async fn update_last_visited_conn(
    conn: &mut sqlx::PgConnection,
    user_id: Uuid,
    realm_id: Uuid,
) -> Result<(), AppError> {
    sqlx::query(
        r#"
        UPDATE realm.memberships
        SET last_visited_at = now()
        WHERE user_id = $1 AND realm_id = $2
        "#,
    )
    .bind(user_id)
    .bind(realm_id)
    .execute(conn)
    .await?;

    Ok(())
}

/// Check if a user is a member of a realm.
pub async fn is_member(pool: &PgPool, user_id: Uuid, realm_id: Uuid) -> Result<bool, AppError> {
    let exists: (bool,) = sqlx::query_as(
        r#"
        SELECT EXISTS(
            SELECT 1 FROM realm.memberships
            WHERE user_id = $1 AND realm_id = $2
        )
        "#,
    )
    .bind(user_id)
    .bind(realm_id)
    .fetch_one(pool)
    .await?;

    Ok(exists.0)
}
