use axum::{
    body::Body,
    http::{header, StatusCode},
    response::Html,
    routing::{delete, get, patch, post, put},
    Router,
};

// These imports are needed for static file handlers
#[cfg(debug_assertions)]
use axum::{
    http::Uri,
    response::{IntoResponse, Response},
};

#[cfg(not(debug_assertions))]
use axum::{
    http::Uri,
    response::{IntoResponse, Response},
};
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use tokio::net::TcpListener;
use tokio::sync::{broadcast, Notify};

use crate::api;
use crate::db::{self, Database};
use crate::db::migration::{maintenance, MigrationProgress, ReadyFlag};
use crate::error::FsPulseError;
use crate::mcp::FsPulseMcp;
use crate::task_manager::TaskManager;

use rmcp::transport::streamable_http_server::{
    StreamableHttpService, StreamableHttpServerConfig,
    session::local::LocalSessionManager,
};
use tokio_util::sync::CancellationToken;

// Embed static files in release builds
#[cfg(not(debug_assertions))]
use rust_embed::RustEmbed;

#[cfg(not(debug_assertions))]
#[derive(RustEmbed)]
#[folder = "frontend/dist/"]
struct Asset;

pub struct WebServer {
    host: String,
    port: u16,
}

impl WebServer {
    pub fn new(host: String, port: u16) -> Self {
        Self { host, port }
    }

    pub async fn start(&self) -> Result<(), FsPulseError> {
        let migration_needed = Database::migration_needed();

        let addr: SocketAddr = format!("{}:{}", self.host, self.port)
            .parse()
            .map_err(|e| FsPulseError::Error(format!("Invalid address: {}", e)))?;

        // Create shutdown channel for background tasks
        let (shutdown_tx, _) = broadcast::channel::<()>(1);
        let shutdown_tx_for_services = shutdown_tx.clone();

        // Create shutdown notify for API-initiated shutdown
        let shutdown_notify = Arc::new(Notify::new());

        // Create MCP cancellation token for clean shutdown
        let mcp_ct = CancellationToken::new();
        let mcp_ct_for_shutdown = mcp_ct.clone();

        // Build the appropriate router
        let (app, ready_flag) = if migration_needed {
            let ready_flag = ReadyFlag::new(false);
            let app = self.create_maintenance_router(ready_flag.clone(), shutdown_notify.clone(), mcp_ct)?;
            (app, Some(ready_flag))
        } else {
            let app = self.create_router(shutdown_notify.clone(), mcp_ct)?;
            (app, None)
        };

        println!("fsPulse server starting on http://{}", addr);

        #[cfg(debug_assertions)]
        println!("   Running in DEVELOPMENT mode - serving assets from frontend/dist/");

        #[cfg(not(debug_assertions))]
        println!("   Running in PRODUCTION mode - serving embedded assets");

        // Bind the listener early so the port is available during migration
        let listener = TcpListener::bind(addr)
            .await
            .map_err(|e| FsPulseError::Error(format!("Failed to bind to {}: {}", addr, e)))?;

        if migration_needed {
            let ready_flag = ready_flag.unwrap();

            // Initialize the migration broadcast channel
            MigrationProgress::init();

            println!("   Database migration required - serving maintenance page");

            // Spawn migrations in a blocking task
            let ready_flag_clone = ready_flag.clone();
            let shutdown_tx_clone = shutdown_tx_for_services;
            tokio::spawn(async move {
                let result = tokio::task::spawn_blocking(|| {
                    db::ensure_schema_current()
                })
                .await;

                match result {
                    Ok(Ok(())) => {
                        log::info!("Migrations completed successfully");
                        MigrationProgress::send_complete();

                        // Brief delay so SSE clients receive the "complete" event
                        tokio::time::sleep(Duration::from_millis(500)).await;

                        // Initialize TaskManager and background services
                        if let Err(e) = start_app_services(shutdown_tx_clone) {
                            log::error!("Failed to initialize app services: {}", e);
                        }

                        // Flip the ready flag — middleware now passes through
                        ready_flag_clone.set_ready();
                        log::info!("Application ready - maintenance mode disabled");
                        println!("   Database migration complete - application ready");
                    }
                    Ok(Err(e)) => {
                        log::error!("Migration failed: {}", e);
                        MigrationProgress::send_failed(&e.to_string());

                        // Give SSE clients time to see the error before exiting
                        tokio::time::sleep(Duration::from_secs(10)).await;
                        std::process::exit(1);
                    }
                    Err(join_error) => {
                        log::error!("Migration task panicked: {}", join_error);
                        MigrationProgress::send_failed(&format!("Migration panicked: {}", join_error));
                        tokio::time::sleep(Duration::from_secs(10)).await;
                        std::process::exit(1);
                    }
                }
            });
        } else {
            // Normal startup — initialize services immediately
            start_app_services(shutdown_tx_for_services)?;
        }

        log::info!("Server ready to handle requests");

        // Start the server with graceful shutdown
        axum::serve(listener, app)
            .with_graceful_shutdown(async move {
                // Wait for either an OS signal or an API-initiated shutdown
                tokio::select! {
                    _ = shutdown_signal() => {
                        log::info!("Shutdown via OS signal");
                    }
                    _ = shutdown_notify.notified() => {
                        log::info!("Shutdown via API request");
                    }
                }

                log::info!("Shutdown signal received, stopping background tasks...");
                println!("\nShutdown signal received - stopping server gracefully...");

                // Cancel MCP sessions so connections close
                mcp_ct_for_shutdown.cancel();

                // Signal background tasks to stop
                let _ = shutdown_tx.send(());

                // Give background tasks a moment to finish
                tokio::time::sleep(Duration::from_secs(2)).await;

                log::info!("Server shutdown complete");
                println!("   Server stopped");
            })
            .await
            .map_err(|e| FsPulseError::Error(format!("Server error: {}", e)))?;

        Ok(())
    }

    /// Build a router wrapped with maintenance middleware for migration mode.
    /// Includes the SSE endpoint and intercepts all requests until ready.
    fn create_maintenance_router(&self, ready_flag: ReadyFlag, shutdown_notify: Arc<Notify>, mcp_ct: CancellationToken) -> Result<Router, FsPulseError> {
        let app = self
            .create_router(shutdown_notify, mcp_ct)?
            .route(
                "/maintenance/events",
                get(maintenance::migration_events_handler),
            )
            .layer(axum::middleware::from_fn_with_state(
                ready_flag,
                maintenance::maintenance_middleware,
            ));

        Ok(app)
    }

    fn create_router(&self, shutdown_notify: Arc<Notify>, mcp_ct: CancellationToken) -> Result<Router, FsPulseError> {
        let app_state = api::state::AppState::new(shutdown_notify);

        let app = Router::new()
            // Health check
            .route("/health", get(health_check))
            // Server management
            .route("/api/server/shutdown", post(api::server::shutdown))
            // App info
            .route("/api/app-info", get(api::app::get_app_info))
            // Query endpoints
            .route(
                "/api/query/fetch_override",
                post(api::query::fetch_override_query),
            )
            .route("/api/query/count_raw", post(api::query::count_raw_query))
            .route(
                "/api/query/{domain}/metadata",
                get(api::query::get_metadata),
            )
            .route("/api/query/{domain}/count", post(api::query::count_query))
            .route("/api/query/{domain}/fetch", post(api::query::fetch_query))
            .route("/api/validate-filter", post(api::query::validate_filter))
            // Integrity endpoints
            .route("/api/integrity/count", get(api::integrity::count))
            .route("/api/integrity/items", get(api::integrity::get_items))
            .route("/api/integrity/items/{item_id}/versions", get(api::integrity::get_versions))
            .route("/api/integrity/review", post(api::integrity::review))
            .route("/api/integrity/bulk-review", post(api::integrity::bulk_review))
            .route("/api/integrity/do-not-validate", post(api::integrity::set_do_not_validate))
            // Task scheduling endpoints
            .route("/api/tasks/scan", post(api::tasks::schedule_scan))
            .route("/api/tasks/compact-database", post(api::tasks::schedule_compact_database))
            .route("/api/tasks/{task_id}/stop", post(api::scans::stop_task))
            // Pause endpoints
            .route("/api/pause", post(api::scans::set_pause))
            .route("/api/pause", delete(api::scans::clear_pause))
            // Root endpoints
            .route("/api/roots", post(api::roots::create_root))
            .route(
                "/api/roots/with-scans",
                get(api::roots::get_roots_with_scans),
            )
            .route("/api/roots/{root_id}", delete(api::roots::delete_root))
            .route(
                "/api/roots/{root_id}/schedule-count",
                get(api::roots::get_schedule_count),
            )
            // Schedule endpoints
            .route("/api/schedules", get(api::schedules::list_schedules))
            .route("/api/schedules", post(api::schedules::create_schedule))
            .route("/api/schedules/{id}", put(api::schedules::update_schedule))
            .route(
                "/api/schedules/{id}",
                delete(api::schedules::delete_schedule),
            )
            .route(
                "/api/schedules/{id}/toggle",
                patch(api::schedules::toggle_schedule),
            )
            .route(
                "/api/tasks/upcoming",
                get(api::schedules::get_upcoming_tasks),
            )
            // Task history endpoints
            .route(
                "/api/tasks/history/count",
                get(api::tasks::get_task_history_count),
            )
            .route(
                "/api/tasks/history/fetch",
                get(api::tasks::get_task_history_fetch),
            )
            // Scan resolution endpoint (temporal versioning)
            .route(
                "/api/scans/resolve",
                get(api::scans::resolve_scan),
            )
            .route(
                "/api/scans/scan_dates",
                get(api::scans::get_scan_dates),
            )
            .route(
                "/api/scans/by_date",
                get(api::scans::get_scans_by_date),
            )
            // Item endpoints
            .route(
                "/api/items/{item_id}/size-history",
                get(api::items::get_item_size_history),
            )
            .route(
                "/api/items/{item_id}/version-at-scan",
                get(api::items::get_version_at_scan),
            )
            .route(
                "/api/items/{item_id}/version-count",
                get(api::items::get_version_count),
            )
            .route(
                "/api/items/{item_id}/versions",
                get(api::items::get_versions),
            )
            .route(
                "/api/items/{item_id}/children-counts",
                get(api::items::get_children_counts),
            )
            .route(
                "/api/items/{item_id}/integrity-state",
                get(api::items::get_integrity_state),
            )
            .route(
                "/api/items/{item_id}/versions/{item_version}/hashes",
                get(api::items::get_hash_history),
            )
            // Temporal model item endpoints
            .route(
                "/api/items/immediate-children",
                get(api::items::get_immediate_children),
            )
            .route(
                "/api/items/search",
                get(api::items::search_items),
            )
            // Database endpoints
            .route(
                "/api/database/stats",
                get(api::database::get_database_stats),
            )
            // Settings endpoints
            .route("/api/settings", get(api::settings::get_settings))
            .route("/api/settings", put(api::settings::update_settings))
            .route("/api/settings", delete(api::settings::delete_settings))
            // WebSocket routes
            .route("/ws/tasks/progress", get(api::scans::scan_progress_ws))
            // Add state for handlers
            .with_state(app_state);

        // MCP server endpoint (conditionally enabled)
        let app = if crate::config::Config::get_mcp_enabled() {
            let mut mcp_config = StreamableHttpServerConfig::default();
            mcp_config.stateful_mode = false;
            mcp_config.json_response = true;
            mcp_config.cancellation_token = mcp_ct;
            let mcp_service = StreamableHttpService::new(
                || Ok(FsPulseMcp::new()),
                LocalSessionManager::default().into(),
                mcp_config,
            );

            log::info!("MCP server enabled at /mcp (stateless mode)");
            println!("   MCP server enabled at /mcp");
            app.nest_service("/mcp", mcp_service)
        } else {
            log::info!("MCP server disabled");
            println!("   MCP server disabled");
            app
        };

        // Serve static files differently based on build type
        #[cfg(debug_assertions)]
        {
            // Development: serve from filesystem with SPA fallback
            let app = app.fallback(dev_static_handler);
            Ok(app)
        }

        #[cfg(not(debug_assertions))]
        {
            // Production: serve embedded files
            let app = app.fallback(static_handler);
            Ok(app)
        }
    }
}

/// Initialize TaskManager pause state and start the background queue processor.
/// Called after migrations complete (or immediately if no migration needed).
fn start_app_services(shutdown_tx: broadcast::Sender<()>) -> Result<(), FsPulseError> {
    // Initialize pause state from database
    let conn = Database::get_connection()?;
    TaskManager::init_pause_state(&conn)?;

    // Start background queue processor with shutdown handling
    let shutdown_rx = shutdown_tx.subscribe();
    tokio::spawn(async move {
        println!("   Starting background queue processor (polling every 20 seconds)");
        let mut interval = tokio::time::interval(Duration::from_secs(20));
        let mut shutdown_rx = shutdown_rx;

        loop {
            tokio::select! {
                _ = interval.tick() => {
                    let conn = match Database::get_connection() {
                        Ok(conn) => conn,
                        Err(e) => {
                            log::error!("Queue processor: Failed to get database connection: {}", e);
                            continue;
                        }
                    };

                    if let Err(e) = TaskManager::poll_queue(&conn) {
                        log::error!("Queue processor error: {}", e);
                    }
                }
                _ = shutdown_rx.recv() => {
                    log::info!("Background queue processor shutting down gracefully");
                    TaskManager::do_shutdown().await;
                    println!("   Background queue processor stopped");
                    break;
                }
            }
        }
    });

    Ok(())
}

async fn health_check() -> Result<(StatusCode, Html<String>), StatusCode> {
    Ok((
        StatusCode::OK,
        Html("<h1>fsPulse Server</h1><p>✅ Server is running</p>".to_string()),
    ))
}

// Handler for filesystem static files (development builds only)
#[cfg(debug_assertions)]
async fn dev_static_handler(uri: Uri) -> impl IntoResponse {
    let path = uri.path().trim_start_matches('/');
    let file_path = if path.is_empty() {
        "frontend/dist/index.html"
    } else {
        &format!("frontend/dist/{}", path)
    };

    // Try to serve the requested file
    if let Ok(content) = std::fs::read(file_path) {
        let mime = mime_guess::from_path(file_path).first_or_octet_stream();

        return Response::builder()
            .status(StatusCode::OK)
            .header(header::CONTENT_TYPE, mime.as_ref())
            .body(Body::from(content))
            .unwrap();
    }

    // For SPA routing: if file not found, serve index.html
    if let Ok(content) = std::fs::read("frontend/dist/index.html") {
        return Response::builder()
            .status(StatusCode::OK)
            .header(header::CONTENT_TYPE, "text/html")
            .body(Body::from(content))
            .unwrap();
    }

    // If even index.html is missing, return 404
    Response::builder()
        .status(StatusCode::NOT_FOUND)
        .body(Body::from("404 Not Found"))
        .unwrap()
}

// Handler for embedded static files (production builds only)
#[cfg(not(debug_assertions))]
async fn static_handler(uri: Uri) -> impl IntoResponse {
    let path = uri.path().trim_start_matches('/');

    // Try to serve the requested file
    if let Some(content) = Asset::get(path) {
        let mime = mime_guess::from_path(path).first_or_octet_stream();

        return Response::builder()
            .status(StatusCode::OK)
            .header(header::CONTENT_TYPE, mime.as_ref())
            .body(Body::from(content.data))
            .unwrap();
    }

    // For SPA routing: if file not found, serve index.html
    // This allows React Router to handle the route
    if let Some(content) = Asset::get("index.html") {
        return Response::builder()
            .status(StatusCode::OK)
            .header(header::CONTENT_TYPE, "text/html")
            .body(Body::from(content.data))
            .unwrap();
    }

    // If even index.html is missing, return 404
    Response::builder()
        .status(StatusCode::NOT_FOUND)
        .body(Body::from("404 Not Found"))
        .unwrap()
}

/// Waits for a shutdown signal (SIGTERM or SIGINT)
async fn shutdown_signal() {
    use tokio::signal;

    let ctrl_c = async {
        signal::ctrl_c()
            .await
            .expect("Failed to install Ctrl+C handler");
    };

    #[cfg(unix)]
    let terminate = async {
        signal::unix::signal(signal::unix::SignalKind::terminate())
            .expect("Failed to install SIGTERM handler")
            .recv()
            .await;
    };

    #[cfg(not(unix))]
    let terminate = std::future::pending::<()>();

    tokio::select! {
        _ = ctrl_c => {
            log::info!("Received SIGINT (Ctrl+C)");
        },
        _ = terminate => {
            log::info!("Received SIGTERM");
        },
    }
}
