use axum::extract::{State, Path};
use axum::response::{Json as ResponseJson, Response, IntoResponse};
use axum::http::{HeaderMap, header};
use tokio_util::io::ReaderStream;
use tokio::fs::File;
use crate::error::{Result, ApiError};
use crate::models::media::{MediaItem, MediaItemResponse};
use crate::models::ApiResponse;
// TranscodingJob import removed - never released transcoding nightmare eliminated
use crate::utils::response::success_response;
use crate::AppState;

/// Extract the base URL from request headers
fn get_base_url(headers: &HeaderMap) -> String {
    // Try to get Host header first
    if let Some(host) = headers.get("host").and_then(|h| h.to_str().ok()) {
        // Check if we're behind a reverse proxy (X-Forwarded-Proto)
        let scheme = if headers.get("x-forwarded-proto")
            .and_then(|h| h.to_str().ok())
            .map(|s| s == "https")
            .unwrap_or(false) {
            "https"
        } else {
            "http"
        };
        
        format!("{}://{}", scheme, host)
    } else {
        // Fallback to localhost for development
        "http://localhost:3002".to_string()
    }
}


/// Get all media items from database
pub async fn list_media_items(
    headers: HeaderMap,
    State(state): State<AppState>,
) -> Result<ResponseJson<ApiResponse<Vec<MediaItemResponse>>>> {
    let media_items = sqlx::query_as!(
        MediaItem,
        r#"
        SELECT id, title, speaker, date, description, scripture_reading,
               file_path, file_size, duration_seconds, video_codec, audio_codec,
               resolution, bitrate, thumbnail_path, thumbnail_generated_at,
               nfo_path, last_scanned, created_at, updated_at
        FROM media_items 
        ORDER BY date DESC NULLS LAST, title ASC
        "#
    )
    .fetch_all(&state.pool)
    .await
    .map_err(|e| crate::error::ApiError::Database(e.to_string()))?;

    let base_url = get_base_url(&headers);
    let responses: Vec<MediaItemResponse> = media_items
        .into_iter()
        .map(|item| item.to_response(&base_url))
        .collect();

    Ok(success_response(responses))
}

/// Get a specific media item by ID
pub async fn get_media_item(
    headers: HeaderMap,
    State(state): State<AppState>,
    Path(id): Path<uuid::Uuid>,
) -> Result<ResponseJson<ApiResponse<MediaItemResponse>>> {
    let media_item = sqlx::query_as!(
        MediaItem,
        r#"
        SELECT id, title, speaker, date, description, scripture_reading,
               file_path, file_size, duration_seconds, video_codec, audio_codec,
               resolution, bitrate, thumbnail_path, thumbnail_generated_at,
               nfo_path, last_scanned, created_at, updated_at
        FROM media_items 
        WHERE id = $1
        "#,
        id
    )
    .fetch_optional(&state.pool)
    .await
    .map_err(|e| crate::error::ApiError::Database(e.to_string()))?;

    match media_item {
        Some(mut item) => {
            // If scripture_reading is null and this is a sermon (has a date), 
            // try to get scripture reading from corresponding bulletin
            if item.scripture_reading.is_none() && item.date.is_some() {
                if let Ok(bulletin) = crate::db::bulletins::get_by_date(&state.pool, item.date.unwrap()).await {
                    if let Some(bulletin_data) = bulletin {
                        // Use the processed scripture reading from the bulletin
                        item.scripture_reading = bulletin_data.scripture_reading.clone();
                    }
                }
            }
            
            let base_url = get_base_url(&headers);
            Ok(success_response(item.to_response(&base_url)))
        }
        None => Err(crate::error::ApiError::NotFound("Media item not found".to_string())),
    }
}

/// New sermons endpoint - replaces Jellyfin
pub async fn list_sermons(
    headers: HeaderMap,
    State(state): State<AppState>,
) -> Result<ResponseJson<ApiResponse<Vec<MediaItemResponse>>>> {
    // Get all sermon media items (from sermons directory), ordered by date descending
    let mut media_items = sqlx::query_as!(
        MediaItem,
        r#"
        SELECT id, title, speaker, date, description, scripture_reading,
               file_path, file_size, duration_seconds, video_codec, audio_codec,
               resolution, bitrate, thumbnail_path, thumbnail_generated_at,
               nfo_path, last_scanned, created_at, updated_at
        FROM media_items 
        WHERE video_codec IS NOT NULL 
        AND file_path LIKE '%/sermons/%'
        ORDER BY date DESC NULLS LAST, title ASC
        LIMIT 100
        "#
    )
    .fetch_all(&state.pool)
    .await
    .map_err(|e| crate::error::ApiError::Database(e.to_string()))?;

    // Link sermons to bulletins for scripture readings
    for item in &mut media_items {
        if item.scripture_reading.is_none() && item.date.is_some() {
            if let Ok(bulletin) = crate::db::bulletins::get_by_date(&state.pool, item.date.unwrap()).await {
                if let Some(bulletin_data) = bulletin {
                    // Use the processed scripture reading from the bulletin
                    item.scripture_reading = bulletin_data.scripture_reading.clone();
                }
            }
        }
    }

    let base_url = get_base_url(&headers);
    let responses: Vec<MediaItemResponse> = media_items
        .into_iter()
        .map(|item| item.to_response(&base_url))
        .collect();

    Ok(success_response(responses))
}

/// List livestreams - replaces Jellyfin livestreams endpoint
pub async fn list_livestreams(
    headers: HeaderMap,
    State(state): State<AppState>,
) -> Result<ResponseJson<ApiResponse<Vec<MediaItemResponse>>>> {
    // Get all livestream media items (from livestreams directory), ordered by date descending
    let media_items = sqlx::query_as!(
        MediaItem,
        r#"
        SELECT id, title, speaker, date, description, scripture_reading,
               file_path, file_size, duration_seconds, video_codec, audio_codec,
               resolution, bitrate, thumbnail_path, thumbnail_generated_at,
               nfo_path, last_scanned, created_at, updated_at
        FROM media_items 
        WHERE video_codec IS NOT NULL 
        AND file_path LIKE '%/livestreams/%'
        ORDER BY date DESC NULLS LAST, title ASC
        LIMIT 100
        "#
    )
    .fetch_all(&state.pool)
    .await
    .map_err(|e| crate::error::ApiError::Database(e.to_string()))?;

    let base_url = get_base_url(&headers);
    let responses: Vec<MediaItemResponse> = media_items
        .into_iter()
        .map(|item| item.to_response(&base_url))
        .collect();

    Ok(success_response(responses))
}


/// Legacy streaming function removed - replaced by smart_streaming system
/*
pub async fn stream_media(
    State(state): State<AppState>,
    Path(media_id): Path<uuid::Uuid>,
    Query(params): Query<HashMap<String, String>>,
    headers: HeaderMap,
) -> Result<impl IntoResponse> {
    // Get the media item
    let media_item = sqlx::query_as!(
        MediaItem,
        r#"
        SELECT id, title, speaker, date, description, scripture_reading,
               file_path, file_size, duration_seconds, video_codec, audio_codec,
               resolution, bitrate, thumbnail_path, thumbnail_generated_at,
               nfo_path, last_scanned, created_at, updated_at
        FROM media_items 
        WHERE id = $1
        "#,
        media_id
    )
    .fetch_optional(&state.pool)
    .await
    .map_err(|e| ApiError::Database(e.to_string()))?
    .ok_or_else(|| ApiError::NotFound("Media item not found".to_string()))?;

    // Detect client capabilities
    let client_caps = ClientCapabilities::detect_from_headers(&headers);
    let source_codec = media_item.video_codec.as_deref().unwrap_or("h264");
    
    // Use the unified transcoding service from app state
    let transcoding_service = &state.transcoding_service;
    
    // Check if transcoding is needed
    let needs_transcoding = transcoding_service.needs_transcoding(source_codec, &client_caps);
    
    let file_path = if needs_transcoding {
        tracing::info!("Client requires transcoding from {} for device {}", source_codec, client_caps.device_type);
        
        // Create transcoding job
        let job = TranscodingJob {
            media_item_id: media_item.id,
            source_path: media_item.file_path.clone(),
            target_codec: "h264".to_string(),
            target_resolution: params.get("max_width").map(|w| {
                let width = w.parse::<u32>().unwrap_or(1920);
                let height = width * 9 / 16; // 16:9 aspect ratio
                format!("{}x{}", width, height)
            }),
            target_bitrate: params.get("video_bit_rate").and_then(|b| b.parse::<i32>().ok()),
            client_capabilities: client_caps.clone(),
        };
        
        // Check if transcoded version already exists
        let transcoded = transcoding_service.get_or_create_transcoded(job).await?;
        
        match transcoded.status {
            crate::models::media::TranscodingStatus::Completed => {
                tracing::info!("Serving transcoded file");
                transcoded.file_path
            },
            crate::models::media::TranscodingStatus::Processing | crate::models::media::TranscodingStatus::Pending => {
                // For incompatible codecs like AV1, return 202 to indicate transcoding in progress
                if source_codec == "av1" || source_codec == "hevc" || source_codec == "h265" {
                    tracing::info!("Transcoding {} file for {} - returning 202", source_codec, client_caps.device_type);
                    
                    let response = Response::builder()
                        .status(StatusCode::ACCEPTED)
                        .header(header::CONTENT_TYPE, "application/json")
                        .header(header::RETRY_AFTER, "30") // Suggest retry in 30 seconds
                        .body(axum::body::Body::from(r#"{"message":"Transcoding in progress","retry_after":30}"#))
                        .map_err(|e| ApiError::Internal(format!("Failed to build response: {}", e)))?;
                    
                    return Ok(response);
                } else {
                    // For other codecs, serve original while transcoding
                    tracing::info!("Transcoding in progress, serving original file");
                    media_item.file_path
                }
            },
            crate::models::media::TranscodingStatus::Failed => {
                tracing::warn!("Transcoding failed, serving original file");
                media_item.file_path
            }
        }
    } else {
        tracing::info!("No transcoding needed, serving original file");
        media_item.file_path
    };

    // Check if file exists
    if !std::path::Path::new(&file_path).exists() {
        return Err(ApiError::NotFound(format!("Media file not found: {}", file_path)));
    }

    // Open the file
    let file = File::open(&file_path).await
        .map_err(|e| ApiError::Internal(format!("Failed to open media file: {}", e)))?;
    
    // Get file metadata
    let metadata = file.metadata().await
        .map_err(|e| ApiError::Internal(format!("Failed to get file metadata: {}", e)))?;
    
    let file_size = metadata.len();
    
    // Handle range requests for video streaming
    let (start, end, content_length) = if let Some(range) = headers.get("range") {
        let range_str = range.to_str().unwrap_or("");
        if let Some(range_bytes) = range_str.strip_prefix("bytes=") {
            let parts: Vec<&str> = range_bytes.split('-').collect();
            let start = parts[0].parse::<u64>().unwrap_or(0);
            let end = if parts.len() > 1 && !parts[1].is_empty() {
                parts[1].parse::<u64>().unwrap_or(file_size - 1)
            } else {
                file_size - 1
            };
            let content_length = end - start + 1;
            (start, end, content_length)
        } else {
            (0, file_size - 1, file_size)
        }
    } else {
        // For video files > 10MB, serve only first 1MB initially to encourage range requests
        if file_size > 10 * 1024 * 1024 {
            let chunk_size = 1024 * 1024; // 1MB
            let end = std::cmp::min(chunk_size - 1, file_size - 1);
            (0, end, end + 1)
        } else {
            (0, file_size - 1, file_size)
        }
    };

    // Create the response
    let mut response_builder = Response::builder();
    
    // Set content type based on file extension
    let content_type = if file_path.ends_with(".mp4") {
        "video/mp4"
    } else if file_path.ends_with(".mkv") {
        "video/x-matroska"
    } else if file_path.ends_with(".webm") {
        "video/webm"
    } else {
        "application/octet-stream"
    };
    
    response_builder = response_builder
        .header(header::CONTENT_TYPE, content_type)
        .header(header::ACCEPT_RANGES, "bytes")
        .header(header::CONTENT_LENGTH, content_length.to_string())
        .header(header::CACHE_CONTROL, "public, max-age=3600");

    // Set status code based on range request
    let status = if start > 0 || end < file_size - 1 {
        response_builder = response_builder
            .header(header::CONTENT_RANGE, format!("bytes {}-{}/{}", start, end, file_size));
        StatusCode::PARTIAL_CONTENT
    } else {
        StatusCode::OK
    };

    // Seek to start position if needed
    let reader = if start > 0 {
        use tokio::io::{AsyncSeekExt, AsyncReadExt};
        let mut file = file;
        file.seek(std::io::SeekFrom::Start(start)).await
            .map_err(|e| ApiError::Internal(format!("Failed to seek in file: {}", e)))?;
        
        // Take only the requested range
        file.take(content_length)
    } else {
        file.take(content_length)
    };

    let stream = ReaderStream::new(reader);
    let body = axum::body::Body::from_stream(stream);

    let response = response_builder
        .status(status)
        .body(body)
        .map_err(|e| ApiError::Internal(format!("Failed to build response: {}", e)))?;

    Ok(response)
}
*/

/// Get thumbnail for media item
pub async fn get_thumbnail(
    State(state): State<AppState>,
    Path(media_id): Path<uuid::Uuid>,
) -> Result<impl IntoResponse> {
    // Get the media item
    let media_item = sqlx::query!(
        "SELECT thumbnail_path FROM media_items WHERE id = $1",
        media_id
    )
    .fetch_optional(&state.pool)
    .await
    .map_err(|e| ApiError::Database(e.to_string()))?
    .ok_or_else(|| ApiError::NotFound("Media item not found".to_string()))?;

    let thumbnail_path = media_item.thumbnail_path
        .ok_or_else(|| ApiError::NotFound("Thumbnail not available".to_string()))?;

    // Check if thumbnail file exists
    if !std::path::Path::new(&thumbnail_path).exists() {
        return Err(ApiError::NotFound("Thumbnail file not found".to_string()));
    }

    // Open the thumbnail file
    let file = File::open(&thumbnail_path).await
        .map_err(|e| ApiError::Internal(format!("Failed to open thumbnail: {}", e)))?;

    let stream = ReaderStream::new(file);
    let body = axum::body::Body::from_stream(stream);

    let response = Response::builder()
        .header(header::CONTENT_TYPE, "image/webp")
        .header(header::CACHE_CONTROL, "public, max-age=86400") // Cache for 24 hours
        .body(body)
        .map_err(|e| ApiError::Internal(format!("Failed to build response: {}", e)))?;

    Ok(response)
}