//! User database operations

use anyhow::Result;
use chrono::{DateTime, Utc};
use sqlx::PgPool;
use uuid::Uuid;

/// User record from database
#[derive(Debug, Clone, sqlx::FromRow)]
pub struct User {
    pub id: Uuid,
    pub username: String,
    pub password_hash: String,
    pub email: Option<String>,
    pub role: String,
    pub enabled: bool,
    pub created_at: DateTime<Utc>,
    #[allow(dead_code)]
    // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
    pub updated_at: DateTime<Utc>,
    pub last_login: Option<DateTime<Utc>>,
    /// Tenancy-ready (Phase 4). Backfilled to the default tenant by migration 004.
    #[allow(dead_code)] // Surfaced by tenant-aware auth in Phase 4; column exists now.
    pub tenant_id: Option<Uuid>,
}

/// User without password hash (for API responses)
#[derive(Debug, Clone, serde::Serialize)]
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
pub struct UserInfo {
    pub id: Uuid,
    pub username: String,
    pub email: Option<String>,
    pub role: String,
    pub enabled: bool,
    pub created_at: DateTime<Utc>,
    pub last_login: Option<DateTime<Utc>>,
    pub permissions: Vec<String>,
}

impl From<User> for UserInfo {
    fn from(u: User) -> Self {
        Self {
            id: u.id,
            username: u.username,
            email: u.email,
            role: u.role,
            enabled: u.enabled,
            created_at: u.created_at,
            last_login: u.last_login,
            permissions: Vec::new(), // Filled in by caller
        }
    }
}

/// Get user by username
pub async fn get_user_by_username(pool: &PgPool, username: &str) -> Result<Option<User>> {
    let user = sqlx::query_as::<_, User>("SELECT * FROM users WHERE username = $1")
        .bind(username)
        .fetch_optional(pool)
        .await?;
    Ok(user)
}

/// Get user by ID
pub async fn get_user_by_id(pool: &PgPool, id: Uuid) -> Result<Option<User>> {
    let user = sqlx::query_as::<_, User>("SELECT * FROM users WHERE id = $1")
        .bind(id)
        .fetch_optional(pool)
        .await?;
    Ok(user)
}

/// Get all users
pub async fn get_all_users(pool: &PgPool) -> Result<Vec<User>> {
    let users = sqlx::query_as::<_, User>("SELECT * FROM users ORDER BY username")
        .fetch_all(pool)
        .await?;
    Ok(users)
}

/// Create a new user
pub async fn create_user(
    pool: &PgPool,
    username: &str,
    password_hash: &str,
    email: Option<&str>,
    role: &str,
) -> Result<User> {
    let user = sqlx::query_as::<_, User>(
        r#"
        INSERT INTO users (username, password_hash, email, role)
        VALUES ($1, $2, $3, $4)
        RETURNING *
        "#,
    )
    .bind(username)
    .bind(password_hash)
    .bind(email)
    .bind(role)
    .fetch_one(pool)
    .await?;
    Ok(user)
}

/// Update user
pub async fn update_user(
    pool: &PgPool,
    id: Uuid,
    email: Option<&str>,
    role: &str,
    enabled: bool,
) -> Result<Option<User>> {
    let user = sqlx::query_as::<_, User>(
        r#"
        UPDATE users
        SET email = $2, role = $3, enabled = $4, updated_at = NOW()
        WHERE id = $1
        RETURNING *
        "#,
    )
    .bind(id)
    .bind(email)
    .bind(role)
    .bind(enabled)
    .fetch_optional(pool)
    .await?;
    Ok(user)
}

/// Update user password
pub async fn update_user_password(pool: &PgPool, id: Uuid, password_hash: &str) -> Result<bool> {
    let result =
        sqlx::query("UPDATE users SET password_hash = $2, updated_at = NOW() WHERE id = $1")
            .bind(id)
            .bind(password_hash)
            .execute(pool)
            .await?;
    Ok(result.rows_affected() > 0)
}

/// Update last login timestamp
pub async fn update_last_login(pool: &PgPool, id: Uuid) -> Result<()> {
    sqlx::query("UPDATE users SET last_login = NOW() WHERE id = $1")
        .bind(id)
        .execute(pool)
        .await?;
    Ok(())
}

/// Delete user
pub async fn delete_user(pool: &PgPool, id: Uuid) -> Result<bool> {
    let result = sqlx::query("DELETE FROM users WHERE id = $1")
        .bind(id)
        .execute(pool)
        .await?;
    Ok(result.rows_affected() > 0)
}

/// Count users (for initial admin check)
pub async fn count_users(pool: &PgPool) -> Result<i64> {
    let count: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM users")
        .fetch_one(pool)
        .await?;
    Ok(count.0)
}

/// Get user permissions
pub async fn get_user_permissions(pool: &PgPool, user_id: Uuid) -> Result<Vec<String>> {
    let perms: Vec<(String,)> =
        sqlx::query_as("SELECT permission FROM user_permissions WHERE user_id = $1")
            .bind(user_id)
            .fetch_all(pool)
            .await?;
    Ok(perms.into_iter().map(|p| p.0).collect())
}

/// Set user permissions (replaces all)
pub async fn set_user_permissions(
    pool: &PgPool,
    user_id: Uuid,
    permissions: &[String],
) -> Result<()> {
    // Delete existing
    sqlx::query("DELETE FROM user_permissions WHERE user_id = $1")
        .bind(user_id)
        .execute(pool)
        .await?;

    // Insert new
    for perm in permissions {
        sqlx::query("INSERT INTO user_permissions (user_id, permission) VALUES ($1, $2)")
            .bind(user_id)
            .bind(perm)
            .execute(pool)
            .await?;
    }
    Ok(())
}

/// Get user's accessible client IDs (empty = all access)
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
pub async fn get_user_client_access(pool: &PgPool, user_id: Uuid) -> Result<Vec<Uuid>> {
    let clients: Vec<(Uuid,)> =
        sqlx::query_as("SELECT client_id FROM user_client_access WHERE user_id = $1")
            .bind(user_id)
            .fetch_all(pool)
            .await?;
    Ok(clients.into_iter().map(|c| c.0).collect())
}

/// Set user's client access (replaces all)
pub async fn set_user_client_access(
    pool: &PgPool,
    user_id: Uuid,
    client_ids: &[Uuid],
) -> Result<()> {
    // Delete existing
    sqlx::query("DELETE FROM user_client_access WHERE user_id = $1")
        .bind(user_id)
        .execute(pool)
        .await?;

    // Insert new
    for client_id in client_ids {
        sqlx::query("INSERT INTO user_client_access (user_id, client_id) VALUES ($1, $2)")
            .bind(user_id)
            .bind(client_id)
            .execute(pool)
            .await?;
    }
    Ok(())
}

/// Check if user has access to a specific client
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
pub async fn user_has_client_access(pool: &PgPool, user_id: Uuid, client_id: Uuid) -> Result<bool> {
    // Admins have access to all
    let user = get_user_by_id(pool, user_id).await?;
    if let Some(u) = user {
        if u.role == "admin" {
            return Ok(true);
        }
    }

    // Check explicit access
    let access: Option<(Uuid,)> = sqlx::query_as(
        "SELECT client_id FROM user_client_access WHERE user_id = $1 AND client_id = $2",
    )
    .bind(user_id)
    .bind(client_id)
    .fetch_optional(pool)
    .await?;

    // If no explicit access entries exist, user has access to all (legacy behavior)
    if access.is_some() {
        return Ok(true);
    }

    // Check if user has ANY access restrictions
    let count: (i64,) =
        sqlx::query_as("SELECT COUNT(*) FROM user_client_access WHERE user_id = $1")
            .bind(user_id)
            .fetch_one(pool)
            .await?;

    // No restrictions means access to all
    Ok(count.0 == 0)
}
