//! Endpoints API REST pour Radio France
//!
//! Ce module définit les handlers HTTP pour accéder aux stations Radio France,
//! leurs métadonnées live et les flux de streaming.

use crate::playlist::StationGroups;
use crate::pmoserver_ext::RadioFranceState;
use axum::{
    body::Body,
    extract::{Path, State},
    http::{HeaderMap, StatusCode},
    response::{IntoResponse, Response},
    routing::get,
    Json, Router,
};
use futures::StreamExt;
use serde_json;
use tracing::info;

// ============ Gestion des erreurs ============

struct AppError(String);

impl IntoResponse for AppError {
    fn into_response(self) -> Response {
        let (status, message) = match self.0.as_str() {
            "not_found" => (StatusCode::NOT_FOUND, self.0),
            "internal_error" => (StatusCode::INTERNAL_SERVER_ERROR, self.0),
            "bad_gateway" => (StatusCode::BAD_GATEWAY, self.0),
            _ => (StatusCode::INTERNAL_SERVER_ERROR, self.0),
        };

        let body = Json(serde_json::json!({
            "error": message
        }));

        (status, body).into_response()
    }
}

impl From<String> for AppError {
    fn from(err: String) -> Self {
        Self(err)
    }
}

/// Crée le router pour l'API Radio France
pub fn create_router(state: RadioFranceState) -> Router {
    Router::new()
        .route("/stations", get(get_stations))
        .route("/{slug}/metadata", get(get_metadata))
        .route("/{slug}/stream", get(proxy_stream))
        .route("/default-logo", get(get_default_logo))
        .with_state(state)
}

// ============================================================================
// Route Handlers
// ============================================================================

/// GET /api/radiofrance/stations
/// Returns the grouped list of stations
async fn get_stations(State(state): State<RadioFranceState>) -> impl IntoResponse {
    match state.source.get_stations().await {
        Ok(stations) => {
            let groups = StationGroups::from_stations(stations);
            Json(groups).into_response()
        }
        Err(e) => AppError(e.to_string()).into_response(),
    }
}

/// GET /api/radiofrance/{slug}/metadata
/// Returns live metadata for a station
async fn get_metadata(
    State(state): State<RadioFranceState>,
    Path(slug): Path<String>,
) -> impl IntoResponse {
    match state.source.get_live_metadata(&slug).await {
        Ok(metadata) => Json(metadata).into_response(),
        Err(e) => AppError(e.to_string()).into_response(),
    }
}

/// GET /api/radiofrance/{slug}/stream
/// Proxies the AAC stream from Radio France (passthrough, no transcoding)
async fn proxy_stream(
    State(state): State<RadioFranceState>,
    Path(slug): Path<String>,
) -> impl IntoResponse {
    #[cfg(feature = "logging")]
    tracing::info!("Stream proxy accessed for station: {}", slug);

    // Get the stream URL
    let stream_url = match state.source.get_stream_url(&slug).await {
        Ok(url) => url,
        Err(e) => return AppError(format!("Stream not found: {}", e)).into_response(),
    };

    info!(
        "Proxyfying RadioFrance stream for {} ==> {}",
        &slug, &stream_url
    );

    // Connect to the Radio France stream
    let response = match reqwest::get(&stream_url).await {
        Ok(r) => r,
        Err(e) => return AppError(format!("Failed to connect: {}", e)).into_response(),
    };

    if !response.status().is_success() {
        return AppError(format!("Upstream returned {}", response.status())).into_response();
    }

    // Build response headers
    let mut headers = HeaderMap::new();
    headers.insert("content-type", "audio/aac".parse().unwrap());
    headers.insert("cache-control", "no-cache".parse().unwrap());

    // Create streaming body
    let stream = response
        .bytes_stream()
        .map(|chunk| chunk.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e)));

    let body = Body::from_stream(stream);

    (headers, body).into_response()
}

/// GET /api/radiofrance/default-logo
/// Returns the default Radio France logo (embedded in binary)
async fn get_default_logo() -> impl IntoResponse {
    use crate::source::RADIOFRANCE_DEFAULT_IMAGE;

    let mut headers = HeaderMap::new();
    headers.insert("Content-Type", "image/webp".parse().unwrap());
    headers.insert("Cache-Control", "public, max-age=86400".parse().unwrap());

    (headers, RADIOFRANCE_DEFAULT_IMAGE).into_response()
}
