use sqlx::PgPool;
use uuid::Uuid;
use anyhow::{Context, Result};

use crate::models::public::{PublicServerStatus, PlatformHealth, PublicSiteConfig};

/// Get all servers opted into the public status page with their current stats
pub async fn get_public_servers(pool: &PgPool) -> Result<Vec<PublicServerStatus>> {
    let rows = sqlx::query!(
        r#"
        SELECT
            l.server_name,
            l.subdomain,
            sc.connection_status,
            COALESCE(
                (SELECT player_count FROM server_stats
                 WHERE license_id = l.id
                 ORDER BY recorded_at DESC LIMIT 1),
                0
            ) as current_players,
            COALESCE(s_cfg.max_players, 200) as max_players,
            psc.status_page_description,
            s_cfg.current_seed,
            ws.next_scheduled_run,
            ws.cron_expression,
            ws.timezone
        FROM licenses l
        INNER JOIN public_site_config psc ON psc.license_id = l.id
        LEFT JOIN server_connections sc ON sc.license_id = l.id
        LEFT JOIN server_config s_cfg ON s_cfg.license_id = l.id
        LEFT JOIN LATERAL (
            SELECT next_scheduled_run, cron_expression, timezone
            FROM wipe_schedules
            WHERE license_id = l.id AND is_active = true
            ORDER BY next_scheduled_run ASC
            LIMIT 1
        ) ws ON true
        WHERE psc.show_on_status_page = true
          AND l.status = 'active'
        ORDER BY current_players DESC, l.server_name ASC
        "#
    )
    .fetch_all(pool)
    .await
    .context("Failed to fetch public servers")?;

    // Calculate uptime percentages for each server
    let mut servers = Vec::new();
    for row in rows {
        // Determine status
        let status = match row.connection_status.as_deref() {
            Some("connected") => "online",
            Some("degraded") => "degraded",
            _ => "offline",
        }
        .to_string();

        // Get uptime percentages
        let license_id_opt = sqlx::query!(
            "SELECT id FROM licenses WHERE subdomain = $1 LIMIT 1",
            row.subdomain
        )
        .fetch_optional(pool)
        .await?;

        let (uptime_24h, uptime_7d, uptime_30d) = if let Some(license_row) = license_id_opt {
            let uptime_24h = calculate_uptime_percentage(pool, license_row.id, 24).await.unwrap_or(0.0);
            let uptime_7d = calculate_uptime_percentage(pool, license_row.id, 168).await.unwrap_or(0.0); // 7 days
            let uptime_30d = calculate_uptime_percentage(pool, license_row.id, 720).await.unwrap_or(0.0); // 30 days
            (uptime_24h, uptime_7d, uptime_30d)
        } else {
            (0.0, 0.0, 0.0)
        };

        // Format wipe schedule (cron → human readable)
        let wipe_schedule = row.cron_expression.as_ref().map(|cron| {
            format_cron_expression(cron, row.timezone.as_deref())
        });

        // Map name (procedural or custom)
        let map_name = row.current_seed.map(|seed| {
            format!("Procedural {}", seed)
        });

        servers.push(PublicServerStatus {
            server_name: row.server_name.unwrap_or_else(|| "Unnamed Server".to_string()),
            subdomain: row.subdomain.unwrap_or_default(),
            status,
            player_count: row.current_players.unwrap_or(0),
            max_players: row.max_players.unwrap_or(200),
            uptime_24h_percent: uptime_24h,
            uptime_7d_percent: uptime_7d,
            uptime_30d_percent: uptime_30d,
            map_name,
            wipe_schedule,
            next_wipe: row.next_scheduled_run,
            description: row.status_page_description,
        });
    }

    Ok(servers)
}

/// Calculate platform-wide health metrics
pub async fn get_platform_health(pool: &PgPool) -> Result<PlatformHealth> {
    let stats = sqlx::query!(
        r#"
        SELECT
            COUNT(DISTINCT l.id) as total_servers,
            COUNT(DISTINCT CASE WHEN sc.connection_status = 'connected' THEN l.id END) as online_servers,
            COALESCE(SUM(
                (SELECT player_count FROM server_stats
                 WHERE license_id = l.id
                 ORDER BY recorded_at DESC LIMIT 1)
            ), 0) as total_players
        FROM licenses l
        INNER JOIN public_site_config psc ON psc.license_id = l.id
        LEFT JOIN server_connections sc ON sc.license_id = l.id
        WHERE psc.show_on_status_page = true
          AND l.status = 'active'
        "#
    )
    .fetch_one(pool)
    .await
    .context("Failed to fetch platform health")?;

    let total_servers = stats.total_servers.unwrap_or(0);
    let online_servers = stats.online_servers.unwrap_or(0);

    // Platform uptime = average of all server uptimes (24h)
    let uptime_percent = if total_servers > 0 {
        let uptime_sum: f64 = sqlx::query!(
            r#"
            SELECT AVG(uptime_percentage) as avg_uptime
            FROM server_stats_hourly
            WHERE license_id IN (
                SELECT l.id FROM licenses l
                INNER JOIN public_site_config psc ON psc.license_id = l.id
                WHERE psc.show_on_status_page = true
            )
            AND hour >= NOW() - INTERVAL '24 hours'
            "#
        )
        .fetch_optional(pool)
        .await?
        .and_then(|r| r.avg_uptime)
        .unwrap_or(0.0);

        uptime_sum
    } else {
        100.0
    };

    Ok(PlatformHealth {
        total_servers,
        online_servers,
        total_players: stats.total_players.unwrap_or(0),
        uptime_percent,
    })
}

/// Calculate uptime percentage for a specific license over N hours
async fn calculate_uptime_percentage(pool: &PgPool, license_id: Uuid, hours: i64) -> Result<f64> {
    // Use hourly stats for efficiency
    let result = sqlx::query!(
        r#"
        SELECT AVG(uptime_percentage) as avg_uptime
        FROM server_stats_hourly
        WHERE license_id = $1
          AND hour >= NOW() - ($2 || ' hours')::INTERVAL
        "#,
        license_id,
        hours
    )
    .fetch_optional(pool)
    .await?;

    let uptime = result
        .and_then(|r| r.avg_uptime)
        .unwrap_or(0.0);

    Ok(uptime.max(0.0).min(100.0))
}

/// Format cron expression to human-readable string
fn format_cron_expression(cron: &str, timezone: Option<&str>) -> String {
    // Basic cron parsing (can be enhanced)
    // Format: "0 18 * * 4" → "Thursdays 6 PM EST"

    let parts: Vec<&str> = cron.split_whitespace().collect();
    if parts.len() < 5 {
        return cron.to_string();
    }

    let minute = parts[0];
    let hour = parts[1];
    let day_of_week = parts.get(4).unwrap_or(&"*");

    let day_str = match *day_of_week {
        "0" | "7" => "Sundays",
        "1" => "Mondays",
        "2" => "Tuesdays",
        "3" => "Wednesdays",
        "4" => "Thursdays",
        "5" => "Fridays",
        "6" => "Saturdays",
        _ => "Daily",
    };

    let hour_num = hour.parse::<u32>().unwrap_or(0);
    let minute_num = minute.parse::<u32>().unwrap_or(0);

    let (display_hour, period) = if hour_num == 0 {
        (12, "AM")
    } else if hour_num < 12 {
        (hour_num, "AM")
    } else if hour_num == 12 {
        (12, "PM")
    } else {
        (hour_num - 12, "PM")
    };

    let tz_suffix = timezone.unwrap_or("UTC");

    if minute_num == 0 {
        format!("{} {} {} {}", day_str, display_hour, period, tz_suffix)
    } else {
        format!("{} {}:{:02} {} {}", day_str, display_hour, minute_num, period, tz_suffix)
    }
}

/// Get public site config for a license
pub async fn get_public_site_config(pool: &PgPool, license_id: Uuid) -> Result<Option<PublicSiteConfig>> {
    let config = sqlx::query_as!(
        PublicSiteConfig,
        r#"
        SELECT
            id, license_id, site_enabled, show_on_status_page, steam_connect_url,
            motd, public_mods, header_image_url, theme_color, custom_css,
            discord_invite_url, show_player_count, show_wipe_schedule,
            show_wipe_countdown, show_mod_list, status_page_description, created_at
        FROM public_site_config
        WHERE license_id = $1
        "#,
        license_id
    )
    .fetch_optional(pool)
    .await
    .context("Failed to fetch public site config")?;

    Ok(config)
}

/// Create default public site config for a license
pub async fn create_public_site_config(pool: &PgPool, license_id: Uuid) -> Result<Uuid> {
    let id = Uuid::new_v4();

    sqlx::query!(
        "INSERT INTO public_site_config (id, license_id) VALUES ($1, $2)",
        id,
        license_id
    )
    .execute(pool)
    .await
    .context("Failed to create public site config")?;

    Ok(id)
}

/// Update public site config
pub async fn update_public_site_config(
    pool: &PgPool,
    license_id: Uuid,
    show_on_status_page: Option<bool>,
    status_page_description: Option<String>,
    site_enabled: Option<bool>,
) -> Result<()> {
    // Build dynamic update query
    if let Some(show) = show_on_status_page {
        sqlx::query!(
            "UPDATE public_site_config SET show_on_status_page = $1 WHERE license_id = $2",
            show,
            license_id
        )
        .execute(pool)
        .await?;
    }

    if let Some(desc) = status_page_description {
        sqlx::query!(
            "UPDATE public_site_config SET status_page_description = $1 WHERE license_id = $2",
            desc,
            license_id
        )
        .execute(pool)
        .await?;
    }

    if let Some(enabled) = site_enabled {
        sqlx::query!(
            "UPDATE public_site_config SET site_enabled = $1 WHERE license_id = $2",
            enabled,
            license_id
        )
        .execute(pool)
        .await?;
    }

    Ok(())
}
