use axum::{
    extract::{DefaultBodyLimit, Multipart, Path, State},
    response::Json,
    routing::post,
    Router,
};
use serde_json::{json, Value};
use std::path::PathBuf;
use tokio::fs::File;
use tokio::io::AsyncWriteExt;
use uuid::Uuid;

use crate::{error::{ApiError, Result}, utils::urls::UrlBuilder};

pub fn routes() -> Router<crate::AppState> {
    Router::new()
        .route("/bulletins/:id/pdf", post(upload_bulletin_pdf))
        .route("/bulletins/:id/cover", post(upload_bulletin_cover))
        .route("/events/:id/image", post(upload_event_image))
        .route("/pending_events/:id/image", post(upload_pending_event_image))
        .nest_service("/files", tower_http::services::ServeDir::new("uploads"))
        .layer(DefaultBodyLimit::max(50 * 1024 * 1024))
}

async fn upload_bulletin_pdf(
    Path(id): Path<Uuid>,
    State(state): State<crate::AppState>,
    mut multipart: Multipart,
) -> Result<Json<Value>> {
    while let Some(field) = multipart.next_field().await.map_err(|_| ApiError::ValidationError("Invalid multipart".to_string()))? {
        if field.name() == Some("file") {
            let filename = field.file_name()
                .ok_or_else(|| ApiError::ValidationError("No filename provided".to_string()))?
                .to_string();
            
            if !filename.ends_with(".pdf") {
                return Err(ApiError::ValidationError("Only PDF files allowed".to_string()));
            }

            let file_id = Uuid::new_v4();
            let file_path = format!("uploads/bulletins/{}.pdf", file_id);
            let full_path = PathBuf::from(&file_path);

            if let Some(parent) = full_path.parent() {
                tokio::fs::create_dir_all(parent).await.map_err(|_| ApiError::ValidationError("Failed to create directory".to_string()))?;
            }

            let data = field.bytes().await.map_err(|_| ApiError::ValidationError("Failed to read file".to_string()))?;
            let mut file = File::create(&full_path).await.map_err(|_| ApiError::ValidationError("Failed to create file".to_string()))?;
            file.write_all(&data).await.map_err(|_| ApiError::ValidationError("Failed to write file".to_string()))?;

            // Update bulletin record with full URL
            let url_builder = UrlBuilder::new();
            let pdf_path_url = url_builder.build_upload_url(&format!("bulletins/{}.pdf", file_id));
            
            sqlx::query!(
                "UPDATE bulletins SET pdf_path = $1, updated_at = NOW() WHERE id = $2",
                pdf_path_url,
                id
            )
            .execute(&state.pool)
            .await
            .map_err(|_| ApiError::ValidationError("Failed to update bulletin record".to_string()))?;

            return Ok(Json(json!({
                "success": true,
                "file_path": file_path,
                "pdf_path": pdf_path_url,
                "message": "PDF uploaded successfully"
            })));
        }
    }

    Err(ApiError::ValidationError("No file found in request".to_string()))
}

async fn upload_bulletin_cover(
    Path(id): Path<Uuid>,
    State(state): State<crate::AppState>,
    mut multipart: Multipart,
) -> Result<Json<Value>> {
    while let Some(field) = multipart.next_field().await.map_err(|_| ApiError::ValidationError("Invalid multipart".to_string()))? {
        if field.name() == Some("file") {
            let filename = field.file_name()
                .ok_or_else(|| ApiError::ValidationError("No filename provided".to_string()))?
                .to_string();
            
            let ext = filename.split('.').last().unwrap_or("jpg").to_lowercase();
            if !["jpg", "jpeg", "png", "webp", "gif"].contains(&ext.as_str()) {
                return Err(ApiError::ValidationError("Only image files allowed".to_string()));
            }

            let file_id = Uuid::new_v4();
            let file_path = format!("uploads/bulletins/{}.{}", file_id, ext);
            let full_path = PathBuf::from(&file_path);

            if let Some(parent) = full_path.parent() {
                tokio::fs::create_dir_all(parent).await.map_err(|_| ApiError::ValidationError("Failed to create directory".to_string()))?;
            }

            let data = field.bytes().await.map_err(|_| ApiError::ValidationError("Failed to read file".to_string()))?;
            let mut file = File::create(&full_path).await.map_err(|_| ApiError::ValidationError("Failed to create file".to_string()))?;
            file.write_all(&data).await.map_err(|_| ApiError::ValidationError("Failed to write file".to_string()))?;

            // Update bulletin record with full URL
            let url_builder = UrlBuilder::new();
            let cover_image_url = url_builder.build_upload_url(&format!("bulletins/{}.{}", file_id, ext));
            
            sqlx::query!(
                "UPDATE bulletins SET cover_image = $1, updated_at = NOW() WHERE id = $2",
                cover_image_url,
                id
            )
            .execute(&state.pool)
            .await
            .map_err(|_| ApiError::ValidationError("Failed to update bulletin record".to_string()))?;

            return Ok(Json(json!({
                "success": true,
                "file_path": file_path,
                "cover_image_url": cover_image_url,
                "message": "Cover image uploaded successfully"
            })));
        }
    }

    Err(ApiError::ValidationError("No file found in request".to_string()))
}

async fn upload_event_image(
    Path(id): Path<Uuid>,
    State(state): State<crate::AppState>,
    mut multipart: Multipart,
) -> Result<Json<Value>> {
    while let Some(field) = multipart.next_field().await.map_err(|_| ApiError::ValidationError("Invalid multipart".to_string()))? {
        if field.name() == Some("file") {
            let filename = field.file_name()
                .ok_or_else(|| ApiError::ValidationError("No filename provided".to_string()))?
                .to_string();
            
            let ext = filename.split('.').last().unwrap_or("jpg").to_lowercase();
            if !["jpg", "jpeg", "png", "webp", "gif"].contains(&ext.as_str()) {
                return Err(ApiError::ValidationError("Only image files allowed".to_string()));
            }

            // Get current event to check for existing image
            let current_event = sqlx::query!(
                "SELECT image FROM events WHERE id = $1",
                id
            )
            .fetch_optional(&state.pool)
            .await
            .map_err(|_| ApiError::ValidationError("Failed to fetch event".to_string()))?
            .ok_or_else(|| ApiError::NotFound("Event not found".to_string()))?;

            let file_id = Uuid::new_v4();
            let file_path = format!("uploads/events/{}.{}", file_id, ext);
            let full_path = PathBuf::from(&file_path);

            if let Some(parent) = full_path.parent() {
                tokio::fs::create_dir_all(parent).await.map_err(|_| ApiError::ValidationError("Failed to create directory".to_string()))?;
            }

            let data = field.bytes().await.map_err(|_| ApiError::ValidationError("Failed to read file".to_string()))?;
            let mut file = File::create(&full_path).await.map_err(|_| ApiError::ValidationError("Failed to create file".to_string()))?;
            file.write_all(&data).await.map_err(|_| ApiError::ValidationError("Failed to write file".to_string()))?;

            // Update event record with full URL
            let url_builder = UrlBuilder::new();
            let image_url = url_builder.build_upload_url(&format!("events/{}.{}", file_id, ext));
            
            sqlx::query!(
                "UPDATE events SET image = $1, updated_at = NOW() WHERE id = $2",
                image_url,
                id
            )
            .execute(&state.pool)
            .await
            .map_err(|_| ApiError::ValidationError("Failed to update event record".to_string()))?;

            // Delete old image file if it exists
            if let Some(old_image_url) = current_event.image {
                // Extract file path from URL (remove base URL if present)
                if let Some(relative_path) = old_image_url.strip_prefix("https://").and_then(|s| s.split_once('/')).map(|(_, path)| path)
                    .or_else(|| old_image_url.strip_prefix("http://").and_then(|s| s.split_once('/')).map(|(_, path)| path))
                    .or_else(|| Some(old_image_url.as_str())) {
                    
                    let old_file_path = PathBuf::from(relative_path);
                    if old_file_path.exists() {
                        if let Err(_) = tokio::fs::remove_file(&old_file_path).await {
                            tracing::warn!("Failed to delete old event image: {:?}", old_file_path);
                        }
                    }
                }
            }

            return Ok(Json(json!({
                "success": true,
                "file_path": file_path,
                "image_url": image_url,
                "message": "Event image uploaded successfully"
            })));
        }
    }

    Err(ApiError::ValidationError("No file found in request".to_string()))
}

async fn upload_pending_event_image(
    Path(_id): Path<Uuid>,
    State(_state): State<crate::AppState>,
    mut multipart: Multipart,
) -> Result<Json<Value>> {
    while let Some(field) = multipart.next_field().await.map_err(|_| ApiError::ValidationError("Invalid multipart".to_string()))? {
        if field.name() == Some("file") {
            let filename = field.file_name()
                .ok_or_else(|| ApiError::ValidationError("No filename provided".to_string()))?
                .to_string();
            
            let ext = filename.split('.').last().unwrap_or("jpg").to_lowercase();
            if !["jpg", "jpeg", "png", "webp", "gif"].contains(&ext.as_str()) {
                return Err(ApiError::ValidationError("Only image files allowed".to_string()));
            }

            let file_id = Uuid::new_v4();
            let file_path = format!("uploads/events/{}.{}", file_id, ext);
            let full_path = PathBuf::from(&file_path);

            if let Some(parent) = full_path.parent() {
                tokio::fs::create_dir_all(parent).await.map_err(|_| ApiError::ValidationError("Failed to create directory".to_string()))?;
            }

            let data = field.bytes().await.map_err(|_| ApiError::ValidationError("Failed to read file".to_string()))?;
            let mut file = File::create(&full_path).await.map_err(|_| ApiError::ValidationError("Failed to create file".to_string()))?;
            file.write_all(&data).await.map_err(|_| ApiError::ValidationError("Failed to write file".to_string()))?;


            return Ok(Json(json!({
                "success": true,
                "file_path": file_path,
                "message": "Pending event image uploaded successfully"
            })));
        }
    }

    Err(ApiError::ValidationError("No file found in request".to_string()))
}
