use sqlx::PgPool;
use uuid::Uuid;
use anyhow::Result;

use crate::models::server::{ServerConnection, ServerConfig, GameAdmin};

/// Get the server connection for a license.
pub async fn get_server_connection(pool: &PgPool, license_id: Uuid) -> Result<Option<ServerConnection>> {
    let conn = sqlx::query_as::<_, ServerConnection>(
        "SELECT id, license_id, connection_type, panel_api_endpoint, panel_api_key_encrypted, \
         panel_server_identifier, companion_agent_token, companion_last_seen, plugin_last_seen, \
         server_ip, server_port, game_port, connection_status, created_at, updated_at \
         FROM server_connections WHERE license_id = $1",
    )
    .bind(license_id)
    .fetch_optional(pool)
    .await?;

    Ok(conn)
}

/// Create a new server connection.
pub async fn create_server_connection(
    pool: &PgPool,
    license_id: Uuid,
    connection_type: &str,
    server_ip: Option<&str>,
    server_port: Option<i32>,
    game_port: Option<i32>,
) -> Result<Uuid> {
    let row: (Uuid,) = sqlx::query_as(
        "INSERT INTO server_connections (license_id, connection_type, server_ip, server_port, game_port) \
         VALUES ($1, $2, $3, $4, $5) RETURNING id",
    )
    .bind(license_id)
    .bind(connection_type)
    .bind(server_ip)
    .bind(server_port)
    .bind(game_port)
    .fetch_one(pool)
    .await?;

    Ok(row.0)
}

/// Update server connection status.
pub async fn update_connection_status(pool: &PgPool, license_id: Uuid, status: &str) -> Result<()> {
    sqlx::query(
        "UPDATE server_connections SET connection_status = $1, updated_at = NOW() WHERE license_id = $2",
    )
    .bind(status)
    .bind(license_id)
    .execute(pool)
    .await?;
    Ok(())
}

/// Get the server config for a license.
pub async fn get_server_config(pool: &PgPool, license_id: Uuid) -> Result<Option<ServerConfig>> {
    let config = sqlx::query_as::<_, ServerConfig>(
        "SELECT id, license_id, server_name, max_players, world_size, current_seed, \
         current_map_id, server_description, server_url, server_header_image, tags, \
         auto_restart_enabled, auto_restart_cron, auto_restart_timezone, \
         crash_recovery_enabled, crash_recovery_max_attempts, crash_recovery_cooldown_minutes, \
         force_wipe_eligible, auto_update_on_force_wipe, config_overrides, \
         created_at, updated_at \
         FROM server_config WHERE license_id = $1",
    )
    .bind(license_id)
    .fetch_optional(pool)
    .await?;

    Ok(config)
}

/// Create a default server config for a license.
pub async fn create_server_config(
    pool: &PgPool,
    license_id: Uuid,
    server_name: &str,
) -> Result<Uuid> {
    let row: (Uuid,) = sqlx::query_as(
        "INSERT INTO server_config (license_id, server_name) VALUES ($1, $2) RETURNING id",
    )
    .bind(license_id)
    .bind(server_name)
    .fetch_one(pool)
    .await?;

    Ok(row.0)
}

/// Update server config fields.
pub async fn update_server_config(
    pool: &PgPool,
    license_id: Uuid,
    server_name: Option<&str>,
    max_players: Option<i32>,
    world_size: Option<i32>,
    current_seed: Option<i32>,
) -> Result<()> {
    // Dynamic update — only modify provided fields
    if let Some(name) = server_name {
        sqlx::query("UPDATE server_config SET server_name = $1, updated_at = NOW() WHERE license_id = $2")
            .bind(name)
            .bind(license_id)
            .execute(pool)
            .await?;
    }
    if let Some(mp) = max_players {
        sqlx::query("UPDATE server_config SET max_players = $1, updated_at = NOW() WHERE license_id = $2")
            .bind(mp)
            .bind(license_id)
            .execute(pool)
            .await?;
    }
    if let Some(ws) = world_size {
        sqlx::query("UPDATE server_config SET world_size = $1, updated_at = NOW() WHERE license_id = $2")
            .bind(ws)
            .bind(license_id)
            .execute(pool)
            .await?;
    }
    if let Some(seed) = current_seed {
        sqlx::query("UPDATE server_config SET current_seed = $1, updated_at = NOW() WHERE license_id = $2")
            .bind(seed)
            .bind(license_id)
            .execute(pool)
            .await?;
    }
    Ok(())
}

/// Get all game admins for a license.
pub async fn get_game_admins(pool: &PgPool, license_id: Uuid) -> Result<Vec<GameAdmin>> {
    let admins = sqlx::query_as::<_, GameAdmin>(
        "SELECT id, license_id, steam_id, display_name, admin_level, permissions, added_by, created_at \
         FROM game_admins WHERE license_id = $1 ORDER BY created_at",
    )
    .bind(license_id)
    .fetch_all(pool)
    .await?;

    Ok(admins)
}

/// Add a game admin.
pub async fn create_game_admin(
    pool: &PgPool,
    license_id: Uuid,
    steam_id: &str,
    display_name: &str,
    admin_level: &str,
    added_by: Uuid,
) -> Result<Uuid> {
    let row: (Uuid,) = sqlx::query_as(
        "INSERT INTO game_admins (license_id, steam_id, display_name, admin_level, added_by) \
         VALUES ($1, $2, $3, $4, $5) RETURNING id",
    )
    .bind(license_id)
    .bind(steam_id)
    .bind(display_name)
    .bind(admin_level)
    .bind(added_by)
    .fetch_one(pool)
    .await?;

    Ok(row.0)
}

/// Remove a game admin.
pub async fn delete_game_admin(pool: &PgPool, admin_id: Uuid) -> Result<()> {
    sqlx::query("DELETE FROM game_admins WHERE id = $1")
        .bind(admin_id)
        .execute(pool)
        .await?;
    Ok(())
}
