use axum::{
    extract::State,
    response::Json,
};
use serde::{Deserialize, Serialize};
use tracing::{error, info};

use crate::app_state::AppState;
use crate::error::{ApiError, Result};
use crate::utils::backup::{DatabaseBackup, BackupInfo};

#[derive(Serialize)]
pub struct BackupResponse {
    pub success: bool,
    pub message: String,
    pub backup_path: Option<String>,
}

#[derive(Serialize)]
pub struct BackupListResponse {
    pub backups: Vec<BackupInfoResponse>,
}

#[derive(Serialize)]
pub struct BackupInfoResponse {
    pub filename: String,
    pub size: String,
    pub created: String,
    pub is_compressed: bool,
}

impl From<BackupInfo> for BackupInfoResponse {
    fn from(info: BackupInfo) -> Self {
        let size = info.size_human_readable();
        let created = info.created.format("%Y-%m-%d %H:%M:%S UTC").to_string();
        Self {
            filename: info.filename,
            size,
            created,
            is_compressed: info.is_compressed,
        }
    }
}

#[derive(Deserialize)]
pub struct BackupRequest {
    #[serde(default)]
    pub format: BackupFormat,
}

#[derive(Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum BackupFormat {
    #[default]
    Custom,
    Sql,
}

#[derive(Deserialize)]
pub struct CleanupRequest {
    pub keep_count: usize,
}

/// Create a database backup
pub async fn create_backup(
    State(_state): State<AppState>,
    Json(request): Json<BackupRequest>,
) -> Result<Json<BackupResponse>> {
    info!("Creating database backup...");

    let database_url = std::env::var("DATABASE_URL")
        .map_err(|_| ApiError::Internal("DATABASE_URL not found".to_string()))?;

    let backup = DatabaseBackup::new(database_url, "/media/archive");

    let backup_path = match request.format {
        BackupFormat::Custom => backup.create_backup().await,
        BackupFormat::Sql => backup.create_sql_backup().await,
    };

    match backup_path {
        Ok(path) => {
            let response = BackupResponse {
                success: true,
                message: "Database backup created successfully".to_string(),
                backup_path: Some(path.to_string_lossy().to_string()),
            };
            Ok(Json(response))
        }
        Err(e) => {
            error!("Failed to create backup: {}", e);
            Err(ApiError::Internal(format!("Backup failed: {}", e)))
        }
    }
}

/// List all available backups
pub async fn list_backups(
    State(_): State<AppState>,
) -> Result<Json<BackupListResponse>> {
    info!("Listing database backups...");

    let database_url = std::env::var("DATABASE_URL")
        .map_err(|_| ApiError::Internal("DATABASE_URL not found".to_string()))?;

    let backup = DatabaseBackup::new(database_url, "/media/archive");

    match backup.list_backups().await {
        Ok(backups) => {
            let response_backups: Vec<BackupInfoResponse> = backups
                .into_iter()
                .map(BackupInfoResponse::from)
                .collect();

            Ok(Json(BackupListResponse {
                backups: response_backups,
            }))
        }
        Err(e) => {
            error!("Failed to list backups: {}", e);
            Err(ApiError::Internal(format!("Failed to list backups: {}", e)))
        }
    }
}

/// Clean up old backups
pub async fn cleanup_backups(
    State(_): State<AppState>,
    Json(request): Json<CleanupRequest>,
) -> Result<Json<BackupResponse>> {
    info!("Cleaning up old backups, keeping {} most recent", request.keep_count);

    if request.keep_count == 0 {
        return Err(ApiError::BadRequest("keep_count must be greater than 0".to_string()));
    }

    let database_url = std::env::var("DATABASE_URL")
        .map_err(|_| ApiError::Internal("DATABASE_URL not found".to_string()))?;

    let backup = DatabaseBackup::new(database_url, "/media/archive");

    match backup.cleanup_old_backups(request.keep_count).await {
        Ok(()) => {
            let response = BackupResponse {
                success: true,
                message: format!("Successfully cleaned up old backups, kept {} most recent", request.keep_count),
                backup_path: None,
            };
            Ok(Json(response))
        }
        Err(e) => {
            error!("Failed to cleanup backups: {}", e);
            Err(ApiError::Internal(format!("Cleanup failed: {}", e)))
        }
    }
}

/// Manual backup trigger (for immediate backup)
pub async fn backup_now(
    State(state): State<AppState>,
) -> Result<Json<BackupResponse>> {
    let default_request = BackupRequest {
        format: BackupFormat::Custom,
    };
    
    create_backup(State(state), Json(default_request)).await
}