use sqlx::PgPool;
use uuid::Uuid;
use anyhow::{Context, Result};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

// TODO: Define WipeProfile struct (id, server_id, name, wipe_type, commands, plugin_actions, created_at)
// TODO: Define WipeSchedule struct (id, profile_id, cron_expression, next_run_at, enabled)

/// Wipe history entry (full schema).
#[derive(Debug, Clone, sqlx::FromRow, Serialize, Deserialize)]
pub struct WipeHistoryRow {
    pub id: Uuid,
    pub license_id: Uuid,
    pub wipe_schedule_id: Option<Uuid>,
    pub wipe_profile_id: Uuid,
    pub wipe_type: String,
    pub trigger_type: String,
    pub status: String,
    pub started_at: Option<DateTime<Utc>>,
    pub completed_at: Option<DateTime<Utc>>,
    pub map_used: Option<String>,
    pub plugins_wiped: Vec<String>,
    pub plugins_preserved: Vec<String>,
    pub backup_reference: Option<String>,
    pub error_message: Option<String>,
    pub created_at: DateTime<Utc>,
}

/// Analytics: Wipe with duration and peak population metadata.
#[derive(Debug, Clone, Serialize)]
pub struct WipeAnalyticsEntry {
    pub date: DateTime<Utc>,
    pub duration_seconds: i64,
    pub peak_population: i32,
    pub hours_to_peak: f64,
    pub success: bool,
}

/// Analytics: Population curve aggregates (average players per day post-wipe).
#[derive(Debug, Clone, Serialize)]
pub struct PopulationCurve {
    pub day_1_avg: f64,
    pub day_2_avg: f64,
    pub day_3_avg: f64,
}

/// Analytics: Optimal wipe timing recommendation.
#[derive(Debug, Clone, Serialize)]
pub struct OptimalWipeTiming {
    pub optimal_wipe_day: String,
    pub optimal_wipe_hour: i32,
}

/// Create a new wipe profile (template for a wipe operation).
pub async fn create_wipe_profile(pool: &PgPool, server_id: Uuid, name: &str, wipe_type: &str) -> Result<Uuid> {
    todo!()
}

/// Get all wipe profiles for a server.
pub async fn get_wipe_profiles(pool: &PgPool, server_id: Uuid) -> Result<()> {
    todo!()
}

/// Create a cron-based schedule for a wipe profile.
pub async fn create_wipe_schedule(pool: &PgPool, profile_id: Uuid, cron_expression: &str) -> Result<Uuid> {
    todo!()
}

/// Get all schedules for a wipe profile.
pub async fn get_wipe_schedules(pool: &PgPool, profile_id: Uuid) -> Result<()> {
    todo!()
}

/// Record the start of a wipe execution.
pub async fn create_wipe_history(pool: &PgPool, profile_id: Uuid) -> Result<Uuid> {
    todo!()
}

/// Update a wipe history entry with completion status and log output.
pub async fn update_wipe_history(pool: &PgPool, history_id: Uuid, status: &str, log: Option<&str>) -> Result<()> {
    todo!()
}

/// Get wipe history for a profile, ordered by most recent first.
pub async fn get_wipe_history(pool: &PgPool, profile_id: Uuid, limit: i64) -> Result<()> {
    todo!()
}

// ========================================================================
// ANALYTICS QUERIES — Phase 2 Wipe Performance Tracking
// ========================================================================

/// Get wipe success rate over a time range (days).
/// Returns (total_wipes, successful_wipes, failed_wipes).
pub async fn get_wipe_success_rate(
    pool: &PgPool,
    license_id: Uuid,
    days: i64,
) -> Result<(i64, i64, i64)> {
    let result: (i64, i64, i64) = sqlx::query_as(
        "SELECT
            COUNT(*) as total_wipes,
            COUNT(*) FILTER (WHERE status = 'success') as successful_wipes,
            COUNT(*) FILTER (WHERE status IN ('failed', 'rolled_back')) as failed_wipes
         FROM wipe_history
         WHERE license_id = $1
           AND created_at >= NOW() - ($2 || ' days')::INTERVAL
           AND status IN ('success', 'failed', 'rolled_back')",
    )
    .bind(license_id)
    .bind(days)
    .fetch_one(pool)
    .await
    .context("Failed to query wipe success rate")?;

    Ok(result)
}

/// Get average wipe duration (in seconds) over a time range (days).
/// Only includes successful wipes with valid start/completion timestamps.
pub async fn get_average_wipe_duration(
    pool: &PgPool,
    license_id: Uuid,
    days: i64,
) -> Result<f64> {
    let result: Option<(Option<f64>,)> = sqlx::query_as(
        "SELECT AVG(EXTRACT(EPOCH FROM (completed_at - started_at))) as avg_duration
         FROM wipe_history
         WHERE license_id = $1
           AND created_at >= NOW() - ($2 || ' days')::INTERVAL
           AND status = 'success'
           AND started_at IS NOT NULL
           AND completed_at IS NOT NULL",
    )
    .bind(license_id)
    .bind(days)
    .fetch_optional(pool)
    .await
    .context("Failed to query average wipe duration")?;

    Ok(result.and_then(|r| r.0).unwrap_or(0.0))
}

/// Get hours from wipe completion to peak population.
/// For each wipe, finds the max player count in the 24 hours post-wipe,
/// then calculates how many hours after wipe completion that peak occurred.
/// Returns average across all wipes in the time range.
pub async fn get_wipe_to_peak_population(
    pool: &PgPool,
    license_id: Uuid,
    days: i64,
) -> Result<f64> {
    // For each successful wipe, find the peak player count within 24 hours post-wipe
    // and calculate hours from wipe completion to that peak.
    let result: Option<(Option<f64>,)> = sqlx::query_as(
        "WITH wipe_peaks AS (
            SELECT
                wh.id as wipe_id,
                wh.completed_at,
                MAX(ssh.max_players) as peak_players,
                (SELECT ssh2.hour
                 FROM server_stats_hourly ssh2
                 WHERE ssh2.license_id = wh.license_id
                   AND ssh2.hour >= wh.completed_at
                   AND ssh2.hour < wh.completed_at + INTERVAL '24 hours'
                   AND ssh2.max_players = MAX(ssh.max_players)
                 ORDER BY ssh2.hour ASC
                 LIMIT 1
                ) as peak_hour
            FROM wipe_history wh
            LEFT JOIN server_stats_hourly ssh
                ON ssh.license_id = wh.license_id
                AND ssh.hour >= wh.completed_at
                AND ssh.hour < wh.completed_at + INTERVAL '24 hours'
            WHERE wh.license_id = $1
              AND wh.created_at >= NOW() - ($2 || ' days')::INTERVAL
              AND wh.status = 'success'
              AND wh.completed_at IS NOT NULL
            GROUP BY wh.id, wh.completed_at
        )
        SELECT AVG(EXTRACT(EPOCH FROM (peak_hour - completed_at)) / 3600.0) as avg_hours_to_peak
        FROM wipe_peaks
        WHERE peak_hour IS NOT NULL",
    )
    .bind(license_id)
    .bind(days)
    .fetch_optional(pool)
    .await
    .context("Failed to query wipe-to-peak population")?;

    Ok(result.and_then(|r| r.0).unwrap_or(0.0))
}

/// Get population curve: average player count on day 1, day 2, day 3 post-wipe.
/// Aggregates all successful wipes in the time range.
pub async fn get_population_curve_by_cycle(
    pool: &PgPool,
    license_id: Uuid,
    days: i64,
) -> Result<PopulationCurve> {
    // For each successful wipe, calculate avg players on day 1 (0-24h), day 2 (24-48h), day 3 (48-72h)
    let result: Vec<(i32, f64)> = sqlx::query_as(
        "WITH wipe_days AS (
            SELECT
                wh.id as wipe_id,
                wh.completed_at,
                CASE
                    WHEN ssh.hour >= wh.completed_at AND ssh.hour < wh.completed_at + INTERVAL '24 hours' THEN 1
                    WHEN ssh.hour >= wh.completed_at + INTERVAL '24 hours' AND ssh.hour < wh.completed_at + INTERVAL '48 hours' THEN 2
                    WHEN ssh.hour >= wh.completed_at + INTERVAL '48 hours' AND ssh.hour < wh.completed_at + INTERVAL '72 hours' THEN 3
                END as day_num,
                ssh.avg_players
            FROM wipe_history wh
            LEFT JOIN server_stats_hourly ssh
                ON ssh.license_id = wh.license_id
                AND ssh.hour >= wh.completed_at
                AND ssh.hour < wh.completed_at + INTERVAL '72 hours'
            WHERE wh.license_id = $1
              AND wh.created_at >= NOW() - ($2 || ' days')::INTERVAL
              AND wh.status = 'success'
              AND wh.completed_at IS NOT NULL
        )
        SELECT day_num, AVG(avg_players) as avg_players_per_day
        FROM wipe_days
        WHERE day_num IS NOT NULL
        GROUP BY day_num
        ORDER BY day_num",
    )
    .bind(license_id)
    .bind(days)
    .fetch_all(pool)
    .await
    .context("Failed to query population curve")?;

    let mut curve = PopulationCurve {
        day_1_avg: 0.0,
        day_2_avg: 0.0,
        day_3_avg: 0.0,
    };

    for (day_num, avg_players) in result {
        match day_num {
            1 => curve.day_1_avg = avg_players,
            2 => curve.day_2_avg = avg_players,
            3 => curve.day_3_avg = avg_players,
            _ => {}
        }
    }

    Ok(curve)
}

/// Get optimal wipe timing: best day of week and hour based on historical peak populations.
/// Analyzes successful wipes and their subsequent player peaks.
pub async fn get_optimal_wipe_timing(
    pool: &PgPool,
    license_id: Uuid,
    days: i64,
) -> Result<OptimalWipeTiming> {
    // Find the day of week and hour with highest average peak population within 24h post-wipe
    let result: Option<(String, f64, f64)> = sqlx::query_as(
        "WITH wipe_performance AS (
            SELECT
                wh.id as wipe_id,
                wh.completed_at,
                EXTRACT(DOW FROM wh.completed_at) as day_of_week,
                EXTRACT(HOUR FROM wh.completed_at) as hour_of_day,
                MAX(ssh.max_players) as peak_players
            FROM wipe_history wh
            LEFT JOIN server_stats_hourly ssh
                ON ssh.license_id = wh.license_id
                AND ssh.hour >= wh.completed_at
                AND ssh.hour < wh.completed_at + INTERVAL '24 hours'
            WHERE wh.license_id = $1
              AND wh.created_at >= NOW() - ($2 || ' days')::INTERVAL
              AND wh.status = 'success'
              AND wh.completed_at IS NOT NULL
            GROUP BY wh.id, wh.completed_at
        ),
        best_timing AS (
            SELECT
                day_of_week,
                hour_of_day,
                AVG(peak_players) as avg_peak_players
            FROM wipe_performance
            GROUP BY day_of_week, hour_of_day
            ORDER BY avg_peak_players DESC
            LIMIT 1
        )
        SELECT
            CASE day_of_week::INTEGER
                WHEN 0 THEN 'Sunday'
                WHEN 1 THEN 'Monday'
                WHEN 2 THEN 'Tuesday'
                WHEN 3 THEN 'Wednesday'
                WHEN 4 THEN 'Thursday'
                WHEN 5 THEN 'Friday'
                WHEN 6 THEN 'Saturday'
            END as day_name,
            hour_of_day,
            avg_peak_players
        FROM best_timing",
    )
    .bind(license_id)
    .bind(days)
    .fetch_optional(pool)
    .await
    .context("Failed to query optimal wipe timing")?;

    let (day_name, hour_of_day, _) = result.unwrap_or(("Thursday".to_string(), 18.0, 0.0));

    Ok(OptimalWipeTiming {
        optimal_wipe_day: day_name,
        optimal_wipe_hour: hour_of_day as i32,
    })
}

/// Get detailed wipe analytics entries (for charting).
/// Returns individual wipe records with duration, peak population, hours to peak.
pub async fn get_wipe_analytics_entries(
    pool: &PgPool,
    license_id: Uuid,
    days: i64,
) -> Result<Vec<WipeAnalyticsEntry>> {
    let rows: Vec<(Uuid, DateTime<Utc>, Option<DateTime<Utc>>, Option<DateTime<Utc>>, String)> = sqlx::query_as(
        "SELECT id, created_at, started_at, completed_at, status
         FROM wipe_history
         WHERE license_id = $1
           AND created_at >= NOW() - ($2 || ' days')::INTERVAL
           AND status IN ('success', 'failed', 'rolled_back')
         ORDER BY created_at ASC",
    )
    .bind(license_id)
    .bind(days)
    .fetch_all(pool)
    .await
    .context("Failed to query wipe analytics entries")?;

    let mut entries = Vec::new();

    for (wipe_id, created_at, started_at, completed_at, status) in rows {
        let success = status == "success";
        let duration_seconds = if let (Some(start), Some(end)) = (started_at, completed_at) {
            (end - start).num_seconds()
        } else {
            0
        };

        // Find peak population and hours to peak for successful wipes
        let (peak_population, hours_to_peak) = if success && completed_at.is_some() {
            let peak_result: Option<(i32, DateTime<Utc>)> = sqlx::query_as(
                "SELECT max_players, hour
                 FROM server_stats_hourly
                 WHERE license_id = $1
                   AND hour >= $2
                   AND hour < $2 + INTERVAL '24 hours'
                 ORDER BY max_players DESC, hour ASC
                 LIMIT 1",
            )
            .bind(license_id)
            .bind(completed_at.unwrap())
            .fetch_optional(pool)
            .await
            .context("Failed to query peak population for wipe")?;

            if let Some((peak, peak_hour)) = peak_result {
                let hours = (peak_hour - completed_at.unwrap()).num_seconds() as f64 / 3600.0;
                (peak, hours)
            } else {
                (0, 0.0)
            }
        } else {
            (0, 0.0)
        };

        entries.push(WipeAnalyticsEntry {
            date: created_at,
            duration_seconds,
            peak_population,
            hours_to_peak,
            success,
        });
    }

    Ok(entries)
}
