use crate::db::Database;
use crate::scans::Scan;
use axum::{
    extract::{
        ws::{Message, WebSocket, WebSocketUpgrade},
        Path, Query, State,
    },
    http::StatusCode,
    response::IntoResponse,
    Json,
};
use log::error;
use serde::{Deserialize, Serialize};

use super::state::AppState;

/// WebSocket endpoint for streaming task progress
/// GET /ws/tasks/progress
pub async fn scan_progress_ws(
    ws: WebSocketUpgrade,
    State(_state): State<AppState>,
) -> impl IntoResponse {
    ws.on_upgrade(handle_scan_progress)
}

async fn handle_scan_progress(mut socket: WebSocket) {
    use crate::task_manager::TaskManager;

    log::info!("[WS] New WebSocket connection established");

    // Subscribe to scan state broadcasts from TaskManager
    let mut receiver = TaskManager::subscribe();

    // Send initial state to client immediately upon connection
    // This is the handshake that ensures clients always know the current state
    log::info!("[WS] Broadcasting current state to new client");

    // Immediately broadcast status. Terminal status is only sent from
    TaskManager::broadcast_current_state(false);

    // Stream broadcast messages (ActiveScan or NoActiveScan)
    loop {
        tokio::select! {
            result = receiver.recv() => {
                match result {
                    Ok(broadcast_message) => {
                        if let Ok(json) = serde_json::to_string(&broadcast_message) {
                            if socket.send(Message::Text(json.into())).await.is_err() {
                                log::info!("[WS] Client disconnected (send failed)");
                                break; // Client disconnected
                            }
                        }
                    }
                    Err(e) => {
                        // Channel closed or lagged
                        log::info!("[WS] Broadcast channel error: {:?} - closing connection", e);
                        break;
                    }
                }
            }
            msg = socket.recv() => {
                // Handle incoming messages from client (keepalive pings)
                match msg {
                    Some(Ok(Message::Text(_))) => {
                        // Client sent a text ping - ignore it, the act of receiving keeps connection alive
                    }
                    Some(Ok(Message::Ping(_))) => {
                        // Client sent a ping - Axum automatically sends pong
                    }
                    Some(Ok(Message::Close(_))) | None => {
                        // Client closed connection
                        log::info!("[WS] Client initiated close");
                        break;
                    }
                    Some(Err(e)) => {
                        // Error receiving message
                        log::info!("[WS] Error receiving from client: {:?}", e);
                        break;
                    }
                    _ => {}
                }
            }
            _ = tokio::time::sleep(tokio::time::Duration::from_secs(30)) => {
                // Send ping to keep connection alive
                if socket.send(Message::Ping(vec![].into())).await.is_err() {
                    log::info!("[WS] Keepalive ping failed - client disconnected");
                    break;
                }
            }
        }
    }
    log::info!("[WS] WebSocket handler exiting");
    // WebSocket will close automatically when dropped
}

/// POST /api/tasks/{task_id}/stop
/// Request stop of a running task by its task_id
pub async fn stop_task(
    State(_state): State<AppState>,
    Path(task_id): Path<i64>,
) -> Result<StatusCode, StatusCode> {
    use crate::task_manager::TaskManager;

    TaskManager::request_stop(task_id).map_err(|e| {
        error!("Failed to stop task (task_id {}): {}", task_id, e);
        StatusCode::NOT_FOUND
    })?;

    log::info!("Stop requested for task (task_id {})", task_id);
    Ok(StatusCode::ACCEPTED) // 202 Accepted - stop requested, will complete async
}

/// Request structure for setting pause
#[derive(Debug, Deserialize)]
pub struct PauseRequest {
    pub duration_seconds: i64, // -1 for indefinite
}

/// POST /api/pause
/// Set pause with duration - stops current scan and prevents new scans
pub async fn set_pause(
    State(_state): State<AppState>,
    Json(req): Json<PauseRequest>,
) -> Result<StatusCode, StatusCode> {
    use crate::task_manager::TaskManager;

    let conn = Database::get_connection().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    TaskManager::set_pause(&conn, req.duration_seconds).map_err(|e| {
        error!("Failed to set pause: {}", e);
        StatusCode::INTERNAL_SERVER_ERROR
    })?;

    log::info!("Pause set for {} seconds", req.duration_seconds);
    Ok(StatusCode::OK)
}

/// DELETE /api/pause
/// Clear pause - allows scanning to resume
pub async fn clear_pause(State(_state): State<AppState>) -> Result<StatusCode, StatusCode> {
    use crate::task_manager::TaskManager;

    let conn = Database::get_connection().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    TaskManager::clear_pause(&conn).map_err(|e| {
        error!("Failed to clear pause: {}", e);
        StatusCode::BAD_REQUEST // 400 - likely tried to unpause while scan unwinding
    })?;

    log::info!("Pause cleared");
    Ok(StatusCode::OK)
}


/// Query parameters for scan dates within a month
#[derive(Debug, Deserialize)]
pub struct ScanDatesParams {
    pub root_id: i64,
    pub year: i32,
    pub month: u32,
}

/// Response structure for scan dates
#[derive(Debug, Serialize)]
pub struct ScanDatesResponse {
    pub dates: Vec<String>,
}

/// GET /api/scans/scan_dates?root_id=X&year=YYYY&month=MM
/// Returns distinct local dates within a month that have completed scans for the root.
pub async fn get_scan_dates(
    Query(params): Query<ScanDatesParams>,
) -> Result<Json<ScanDatesResponse>, (StatusCode, String)> {
    let conn = Database::get_connection().map_err(|e| {
        error!("Database error: {}", e);
        (StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
    })?;

    let dates =
        crate::scans::get_scan_dates_for_month(&conn, params.root_id, params.year, params.month)
            .map_err(|e| {
                error!(
                    "Failed to get scan dates for root_id={}, {}-{}: {}",
                    params.root_id, params.year, params.month, e
                );
                (StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
            })?;

    Ok(Json(ScanDatesResponse { dates }))
}

/// Query parameters for scans on a specific date
#[derive(Debug, Deserialize)]
pub struct ScansByDateParams {
    pub root_id: i64,
    pub date: String,
}

/// Response structure for scans by date
#[derive(Debug, Serialize)]
pub struct ScansByDateResponse {
    pub scans: Vec<crate::scans::ScanSummary>,
}

/// GET /api/scans/by_date?root_id=X&date=YYYY-MM-DD
/// Returns all completed scans for a root on a specific date, most recent first.
pub async fn get_scans_by_date(
    Query(params): Query<ScansByDateParams>,
) -> Result<Json<ScansByDateResponse>, (StatusCode, String)> {
    let conn = Database::get_connection().map_err(|e| {
        error!("Database error: {}", e);
        (StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
    })?;

    let scans = crate::scans::get_scans_for_date(&conn, params.root_id, &params.date).map_err(
        |e| {
            error!(
                "Failed to get scans for root_id={}, date={}: {}",
                params.root_id, params.date, e
            );
            (StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
        },
    )?;

    Ok(Json(ScansByDateResponse { scans }))
}

/// Query parameters for scan resolution
#[derive(Debug, Deserialize)]
pub struct ResolveScanParams {
    pub root_id: Option<i64>,
    pub date: Option<String>,    // "YYYY-MM-DD", omit for latest
    pub scan_id: Option<i64>,    // Look up a specific scan by ID
}

/// Response structure for resolved scan
#[derive(Debug, Serialize)]
pub struct ResolvedScanResponse {
    pub scan_id: i64,
    pub started_at: i64,
}

/// GET /api/scans/resolve?root_id=X&date=YYYY-MM-DD
/// GET /api/scans/resolve?scan_id=Y
/// Resolves to a scan. If scan_id is provided, looks up that scan directly.
/// Otherwise requires root_id and optionally date to find the most recent completed scan.
pub async fn resolve_scan(
    Query(params): Query<ResolveScanParams>,
) -> Result<Json<ResolvedScanResponse>, (StatusCode, String)> {
    // If scan_id is provided, look up that specific scan
    if let Some(scan_id) = params.scan_id {
        let conn = Database::get_connection().map_err(|e| {
            error!("Database error: {}", e);
            (StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
        })?;

        return match Scan::get_by_id_or_latest(&conn, Some(scan_id), None) {
            Ok(Some(scan)) => Ok(Json(ResolvedScanResponse {
                scan_id: scan.scan_id(),
                started_at: scan.started_at(),
            })),
            Ok(None) => Err((
                StatusCode::NOT_FOUND,
                format!("Scan {} not found", scan_id),
            )),
            Err(e) => {
                error!("Failed to resolve scan_id={}: {}", scan_id, e);
                Err((
                    StatusCode::INTERNAL_SERVER_ERROR,
                    format!("Failed to resolve scan: {}", e),
                ))
            }
        };
    }

    // Otherwise, resolve by root_id (required) and optional date
    let root_id = params.root_id.ok_or_else(|| {
        (StatusCode::BAD_REQUEST, "root_id or scan_id is required".to_string())
    })?;

    match Scan::resolve_scan_for_date(root_id, params.date.as_deref()) {
        Ok(Some((scan_id, started_at))) => Ok(Json(ResolvedScanResponse {
            scan_id,
            started_at,
        })),
        Ok(None) => Err((
            StatusCode::NOT_FOUND,
            "No completed scan found for the specified root and date".to_string(),
        )),
        Err(e) => {
            error!(
                "Failed to resolve scan for root_id={}, date={:?}: {}",
                root_id, params.date, e
            );
            Err((
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Failed to resolve scan: {}", e),
            ))
        }
    }
}
