use axum::{
    extract::{State, Path, Query},
    http::{header, HeaderValue, Method, StatusCode, HeaderMap},
    response::{IntoResponse, Json, Response},
    routing::{get, post},
    Router,
};
use std::{collections::HashMap, time::Duration, sync::Arc};
use tower::ServiceBuilder;
use tower_http::{
    compression::CompressionLayer,
    cors::{AllowOrigin, Any, CorsLayer},
    timeout::TimeoutLayer,
};
use crate::{
    AppState,
    middleware::{
        logging::request_logging_middleware, rate_limit::rate_limit_middleware,
        security::security_headers_middleware,
    },
    error::AppError,
    utils::{build_safe_path, serve_partial_file, validate_filename, validate_signature},
    streaming::{
        websocket::{websocket_handler, WebSocketQuery},
        hls::{HLSGenerator, HLSQuality},
    },
    routes::transcode as transcode_routes,
};

pub fn create_routes(
    state: AppState,
    prometheus_handle: Option<metrics_exporter_prometheus::PrometheusHandle>,
) -> Router {
    // SÉCURITÉ: CORS restrictif avec liste d'origines autorisées (pas Any)
    let allowed_origins_str = std::env::var("ALLOWED_ORIGINS")
        .unwrap_or_else(|_| "http://localhost:5176,http://localhost:3000".to_string());
    
    let cors = if allowed_origins_str.trim() == "*" {
        // Mode développement: autoriser toutes les origines
        CorsLayer::new()
            .allow_origin(Any)
            .allow_methods([
                Method::GET,
                Method::POST,
                Method::PUT,
                Method::DELETE,
                Method::OPTIONS,
            ])
            .allow_headers([header::CONTENT_TYPE, header::AUTHORIZATION, header::ACCEPT])
            .expose_headers([
                header::CONTENT_TYPE,
                header::CONTENT_LENGTH,
                header::CONTENT_RANGE,
                header::ACCEPT_RANGES,
            ])
    } else {
        // Mode production: liste d'origines spécifiques
        let allowed_origins = allowed_origins_str
            .split(',')
            .filter_map(|s| s.trim().parse::<HeaderValue>().ok())
            .collect::<Vec<_>>();
        CorsLayer::new()
            .allow_origin(AllowOrigin::list(allowed_origins))
            .allow_methods([
                Method::GET,
                Method::POST,
                Method::PUT,
                Method::DELETE,
                Method::OPTIONS,
            ])
            .allow_headers([header::CONTENT_TYPE, header::AUTHORIZATION, header::ACCEPT])
            .expose_headers([
                header::CONTENT_TYPE,
                header::CONTENT_LENGTH,
                header::CONTENT_RANGE,
                header::ACCEPT_RANGES,
            ])
    };

    // Stack de middlewares
    let middleware_stack = ServiceBuilder::new()
        .layer(TimeoutLayer::new(Duration::from_secs(30)))
        .layer(CompressionLayer::new())
        .layer(cors)
        .layer(axum::middleware::from_fn_with_state(
            state.clone(),
            security_headers_middleware,
        ))
        .layer(axum::middleware::from_fn_with_state(
            state.clone(),
            rate_limit_middleware,
        ))
        .layer(axum::middleware::from_fn_with_state(
            state.clone(),
            request_logging_middleware,
        ));

    // Routes principales
    Router::new()
        .route(
            "/",
            get(|| async { "🎵 Stream Server - Serveur de streaming audio" }),
        )
        .route("/health", get(detailed_health_check))
        .route("/healthz", get(health_check)) // Liveness
        .route("/readyz", get(detailed_health_check)) // Readiness (using detailed check)
        .route(
            "/metrics",
            get(move || async move {
                if let Some(handle) = prometheus_handle {
                    handle.render()
                } else {
                    "Metrics disabled".to_string()
                }
            }),
        )
        .route("/stream/:filename", get(stream_audio))
        .route("/internal/jobs/transcode", post(internal_transcode_handler))
        // Routes de transcodage HLS
        .route("/v1/stream/transcode", post(transcode_routes::transcode_handler))
        .route("/v1/stream/job/:id", get(transcode_routes::get_job_status))
        .route("/api/streams/jobs/:id/status", get(transcode_routes::get_job_status_detailed))
        .route("/v1/stream/hls/:job_id/index.m3u8", get(transcode_routes::serve_hls_manifest))
        .route("/v1/stream/hls/:job_id/:segment", get(transcode_routes::serve_hls_segment))
        // Route WebSocket pour streaming en temps réel
        .route("/ws", get(websocket_handler_wrapper))
        // Ajout des endpoints HLS
        .route(
            "/hls/:track_id/master.m3u8",
            get(hls_master_playlist_wrapper),
        )
        .route(
            "/hls/:track_id/:quality/playlist.m3u8",
            get(hls_quality_playlist_wrapper),
        )
        .route(
            "/hls/:track_id/:quality/:segment",
            get(hls_segment_wrapper),
        )
        .layer(middleware_stack)
        .with_state(state)
}

// Handlers moved from main.rs

async fn internal_transcode_handler(
    State(state): State<AppState>,
    Json(payload): Json<serde_json::Value>,
) -> Result<Json<serde_json::Value>, (StatusCode, String)> {
    // Extract fields from payload
    let track_id = payload
        .get("track_id")
        .and_then(|v| v.as_str())
        .ok_or((StatusCode::BAD_REQUEST, "Missing track_id".to_string()))?;
    let file_path = payload
        .get("file_path")
        .and_then(|v| v.as_str())
        .ok_or((StatusCode::BAD_REQUEST, "Missing file_path".to_string()))?;

    tracing::info!(
        "Received transcode request for track {} at {}",
        track_id,
        file_path
    );

    let request = crate::audio::compression::CompressionRequest {
        track_id: track_id.to_string(),
        input_file: file_path.to_string(),
        target_quality: "high".to_string(), // Default quality
        preserve_metadata: true,
        async_processing: true,
    };

    match state.compression_engine.compress_audio(request).await {
        Ok(response) => Ok(Json(serde_json::to_value(response).unwrap())),
        Err(e) => {
            tracing::error!("Transcoding failed: {:?}", e);
            Err((
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Transcoding failed: {:?}", e),
            ))
        }
    }
}

async fn health_check() -> Json<serde_json::Value> {
    Json(serde_json::json!({
        "status": "healthy",
        "timestamp": std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_secs(),
        "service": "stream_server",
        "version": "0.2.0",
        "checks": {
            "liveness": "ok",
            "database": "unchecked",
            "audio_directory": "unchecked",
            "services": "unchecked"
        }
    }))
}

async fn detailed_health_check(
    State(state): State<AppState>,
) -> impl IntoResponse {
    let health_status = state.health_monitor.get_health_status().await;
    let mut json_status = serde_json::to_value(health_status).unwrap_or_default();

    // Ajouter le statut de RabbitMQ
    if state.config.rabbit_mq.enable {
        if state.event_bus.is_some() && state.event_bus.as_ref().unwrap().is_enabled {
            json_status["rabbitmq_status"] = serde_json::Value::String("connected".to_string());
        } else {
            json_status["rabbitmq_status"] = serde_json::Value::String("disconnected".to_string());
            // Si RabbitMQ est activé mais non connecté, on passe en mode dégradé mais on reste disponible (200 OK)
            if state.config.rabbit_mq.enable && state.event_bus.is_none() {
                if json_status["status"] == "Healthy" {
                     json_status["status"] = serde_json::Value::String("Degraded".to_string());
                }
                // Ne pas retourner 503, car le mode dégradé est supporté
            }
        }
    } else {
        json_status["rabbitmq_status"] = serde_json::Value::String("disabled".to_string());
    }

    // Si la DB est down, renvoyer 503
    if json_status["database_status"].as_str().unwrap_or_default() == "down" {
        return (StatusCode::SERVICE_UNAVAILABLE, Json(json_status)).into_response();
    }

    (StatusCode::OK, Json(json_status)).into_response()
}

async fn stream_audio(
    Path(filename): Path<String>,
    Query(params): Query<HashMap<String, String>>,
    State(state): State<AppState>,
    headers: HeaderMap,
) -> std::result::Result<Response, (StatusCode, String)> {
    
    // Validation des paramètres
    let expires = params.get("expires").ok_or((
        StatusCode::BAD_REQUEST,
        "Missing expires parameter".to_string(),
    ))?;

    let sig = params.get("sig").ok_or((
        StatusCode::BAD_REQUEST,
        "Missing signature parameter".to_string(),
    ))?;

    // Validation du nom de fichier
    let validated_filename = validate_filename(&filename).map_err(|_| {
        (
            StatusCode::BAD_REQUEST,
            "Invalid filename".to_string(),
        )
    })?;

    // Validation de la signature
    if !validate_signature(&state.config, &validated_filename, expires, sig) {
        return Err((
            StatusCode::FORBIDDEN,
            "Invalid signature".to_string(),
        ));
    }

    // Construction du chemin sécurisé
    let file_path = build_safe_path(&state.config, &format!("{}.mp3", validated_filename))
        .map_err(|_| {
            (
                StatusCode::NOT_FOUND,
                "File not found".to_string(),
            )
        })?;

    // Streaming du fichier
    serve_partial_file(&state.config, file_path, headers)
        .await
        .map_err(|e| match e {
            AppError::NotFound { .. } => (
                StatusCode::NOT_FOUND,
                "File not found".to_string(),
            ),
            AppError::InvalidData { .. } => (
                StatusCode::RANGE_NOT_SATISFIABLE,
                "Invalid range".to_string(),
            ),
            _ => (
                StatusCode::INTERNAL_SERVER_ERROR,
                "Internal error".to_string(),
            ),
        })
}

// Handler WebSocket wrapper pour utiliser avec AppState
async fn websocket_handler_wrapper(
    ws: axum::extract::ws::WebSocketUpgrade,
    query: Query<WebSocketQuery>,
    headers: HeaderMap,
    State(state): State<AppState>,
) -> Response {
    websocket_handler(
        ws,
        query,
        headers,
        State(state.websocket_manager.clone()),
    )
    .await
}

// Handler pour la master playlist HLS
async fn hls_master_playlist_wrapper(
    Path(track_id): Path<String>,
    State(state): State<AppState>,
) -> impl IntoResponse {
    
    // Générer la master playlist avec toutes les qualités supportées
    let generator = HLSGenerator::new(track_id, state.config.backend_url.clone())
        .with_quality(HLSQuality::high())
        .with_quality(HLSQuality::medium())
        .with_quality(HLSQuality::low())
        .with_quality(HLSQuality::mobile());

    let playlist = generator.generate_master_playlist();

    (
        [(header::CONTENT_TYPE, "application/vnd.apple.mpegurl")],
        playlist,
    )
}

// Handler pour les playlists de qualité
async fn hls_quality_playlist_wrapper(
    Path((track_id, quality)): Path<(String, String)>,
    State(state): State<AppState>,
) -> impl IntoResponse {
    use std::path::Path;

    let mut segment_count = 0;
    let output_dir = Path::new(&state.config.compression.output_dir);
    
    // Simulation / Vérification de l'existence des segments
    if let Ok(mut entries) = tokio::fs::read_dir(output_dir).await {
        while let Ok(Some(entry)) = entries.next_entry().await {
            let filename = entry.file_name().to_string_lossy().to_string();
            if filename.starts_with(&format!("{}_{}_", track_id, quality)) && filename.ends_with(".ts") {
                segment_count += 1;
            }
        }
    }

    // Si aucun segment trouvé, comportement de test fallback
    if segment_count == 0 {
            if state.config.is_development() {
                segment_count = 5;
            } else {
                return (StatusCode::NOT_FOUND, "No segments found").into_response();
            }
    }

    let generator = HLSGenerator::new(track_id, state.config.backend_url.clone());
    
    match generator.generate_quality_playlist(&quality, segment_count) {
        Ok(playlist) => (
            [(header::CONTENT_TYPE, "application/vnd.apple.mpegurl")],
            playlist,
        ).into_response(),
        Err(_) => (StatusCode::NOT_FOUND, "Quality not found").into_response(),
    }
}

// Handler pour les segments HLS (.ts)
async fn hls_segment_wrapper(
    Path((track_id, quality, segment)): Path<(String, String, String)>,
    State(state): State<AppState>,
) -> impl IntoResponse {
    
    let index_part = segment.strip_prefix("segment_").unwrap_or("00000.ts");
    let real_filename = format!("{}_{}_{}", track_id, quality, index_part);
    // Use std::path::PathBuf via join directly
    let file_path = std::path::Path::new(&state.config.compression.output_dir).join(real_filename);

    if !file_path.exists() {
            // Fallback pour tests
            if state.config.is_development() {
                return (StatusCode::OK, "Fake TS content").into_response();
            }
            return (StatusCode::NOT_FOUND, "Segment not found").into_response();
    }

    let headers = HeaderMap::new();
    match serve_partial_file(&state.config, file_path, headers).await {
        Ok(response) => response,
        Err(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Error serving file").into_response(),
    }
}
