use axum::{
    extract::{Path, State},
    http::StatusCode,
    response::Json,
    routing::{get, post},
    Router,
};
use serde::{Deserialize, Serialize};

use crate::core::processor::snapshot_agent::SnapshotAgent;
use crate::core::storage::snapshot_manager::SnapshotManager;

pub fn snapshot_routes() -> Router<crate::api::server::AppState> {
    Router::new()
        .route(
            "/api/v1/files/:uuid/snapshots",
            get(get_file_snapshots).post(generate_file_snapshots),
        )
        .route(
            "/api/v1/files/:uuid/snapshots/status",
            get(get_file_snapshot_status),
        )
        .route(
            "/api/v1/files/:uuid/snapshots/migrate",
            post(migrate_file_snapshots),
        )
        .route(
            "/api/v1/files/:uuid/snapshots/teardown",
            post(teardown_file_snapshots),
        )
        .route(
            "/api/v1/identities/:uuid/snapshots",
            get(get_identity_snapshots).post(generate_identity_snapshots),
        )
}

// --- File Snapshot Endpoints ---

#[derive(Debug, Serialize)]
pub struct FileSnapshotsResponse {
    pub success: bool,
    pub file_uuid: String,
    pub tier: String,
    pub hits: u64,
    pub types: Vec<String>,
}

async fn get_file_snapshots(
    State(state): State<crate::api::server::AppState>,
    Path(uuid): Path<String>,
) -> Result<Json<FileSnapshotsResponse>, (StatusCode, String)> {
    let output_dir = crate::core::config::OUTPUT_DIR.as_str();
    let manager = SnapshotManager::new(output_dir);

    let hits = state
        .redis_cache
        .get_snapshot_hits(&uuid)
        .await
        .unwrap_or(0);

    let tier = SnapshotManager::compute_tier(hits);
    let types = manager.list_snapshot_types(&uuid);

    state.redis_cache.update_last_access(&uuid).await.ok();

    Ok(Json(FileSnapshotsResponse {
        success: true,
        file_uuid: uuid,
        tier: tier.to_string(),
        hits,
        types,
    }))
}

#[derive(Debug, Serialize)]
pub struct SnapshotStatusResponse {
    pub success: bool,
    pub file_uuid: String,
    pub status: String,
    pub progress: Option<f32>,
    pub tier: String,
}

async fn get_file_snapshot_status(
    State(state): State<crate::api::server::AppState>,
    Path(uuid): Path<String>,
) -> Result<Json<SnapshotStatusResponse>, (StatusCode, String)> {
    let status_json = state
        .redis_cache
        .get_snapshot_status(&uuid)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    let status: String = status_json
        .get("status")
        .and_then(|v| v.as_str())
        .unwrap_or("cold")
        .to_string();

    let progress: Option<f32> = status_json
        .get("progress")
        .and_then(|v| v.as_f64())
        .map(|f| f as f32);

    let hits = state
        .redis_cache
        .get_snapshot_hits(&uuid)
        .await
        .unwrap_or(0);

    let tier = SnapshotManager::compute_tier(hits);

    Ok(Json(SnapshotStatusResponse {
        success: true,
        file_uuid: uuid,
        status,
        progress,
        tier: tier.to_string(),
    }))
}

#[derive(Debug, Deserialize)]
pub struct GenerateSnapshotRequest {
    #[serde(rename = "type")]
    pub snapshot_type: Option<String>,
}

#[derive(Debug, Serialize)]
pub struct GenerateSnapshotResponse {
    pub success: bool,
    pub message: String,
    pub file_uuid: String,
}

async fn generate_file_snapshots(
    State(state): State<crate::api::server::AppState>,
    Path(uuid): Path<String>,
    Json(req): Json<GenerateSnapshotRequest>,
) -> Result<Json<GenerateSnapshotResponse>, (StatusCode, String)> {
    let output_dir = crate::core::config::OUTPUT_DIR.as_str();
    let manager = SnapshotManager::new(output_dir);
    let agent = SnapshotAgent::default();

    manager
        .ensure_file_dirs(&uuid)
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    state
        .redis_cache
        .set_snapshot_status(&uuid, "generating", Some(0.0))
        .await
        .map_err(|e: anyhow::Error| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    let snapshot_type = req.snapshot_type.as_deref().unwrap_or("faces");

    tracing::info!(
        "Starting snapshot generation for file_uuid={}, type={}",
        uuid,
        snapshot_type
    );

    match agent.generate_file_snapshots(&uuid, snapshot_type).await {
        Ok(_) => {
            state
                .redis_cache
                .set_snapshot_status(&uuid, "ready", Some(1.0))
                .await
                .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

            tracing::info!("Snapshot generation completed for file_uuid={}", uuid);
            Ok(Json(GenerateSnapshotResponse {
                success: true,
                message: format!("Snapshot generation completed for type: {}", snapshot_type),
                file_uuid: uuid,
            }))
        }
        Err(e) => {
            state
                .redis_cache
                .set_snapshot_status(&uuid, "failed", None)
                .await
                .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

            tracing::error!("Snapshot generation failed for file_uuid={}: {}", uuid, e);
            Err((
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Snapshot generation failed: {}", e),
            ))
        }
    }
}

#[derive(Debug, Deserialize)]
pub struct MigrateSnapshotRequest {
    pub parent_uuid: String,
}

#[derive(Debug, Serialize)]
pub struct MigrateSnapshotResponse {
    pub success: bool,
    pub message: String,
    pub file_uuid: String,
    pub migrated_types: Vec<String>,
}

async fn migrate_file_snapshots(
    State(state): State<crate::api::server::AppState>,
    Path(uuid): Path<String>,
    Json(req): Json<MigrateSnapshotRequest>,
) -> Result<Json<MigrateSnapshotResponse>, (StatusCode, String)> {
    let agent = SnapshotAgent::default();

    tracing::info!(
        "Starting snapshot migration from parent_uuid={} to file_uuid={}",
        req.parent_uuid,
        uuid
    );

    match agent.migrate_snapshots(&uuid, &req.parent_uuid).await {
        Ok(migrated) => {
            state
                .redis_cache
                .set_migrate_hint(&uuid, &req.parent_uuid, migrated.len() as u64)
                .await
                .map_err(|e: anyhow::Error| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

            Ok(Json(MigrateSnapshotResponse {
                success: true,
                message: format!("Migrated {} snapshot types", migrated.len()),
                file_uuid: uuid,
                migrated_types: migrated,
            }))
        }
        Err(e) => {
            tracing::error!("Snapshot migration failed: {}", e);
            Err((
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Migration failed: {}", e),
            ))
        }
    }
}

// --- Identity Snapshot Endpoints ---

#[derive(Debug, Serialize)]
pub struct IdentitySnapshotsResponse {
    pub success: bool,
    pub identity_uuid: String,
    pub has_reference: bool,
    pub face_count: usize,
}

async fn get_identity_snapshots(
    State(_state): State<crate::api::server::AppState>,
    Path(uuid): Path<String>,
) -> Result<Json<IdentitySnapshotsResponse>, (StatusCode, String)> {
    let output_dir = crate::core::config::OUTPUT_DIR.as_str();
    let manager = SnapshotManager::new(output_dir);

    let identity_dir = manager.identity_snapshot_dir(&uuid);
    let has_reference = identity_dir.join("reference.jpg").exists();

    let face_count = if identity_dir.join("faces").exists() {
        std::fs::read_dir(identity_dir.join("faces"))
            .map(|entries| entries.flatten().count())
            .unwrap_or(0)
    } else {
        0
    };

    Ok(Json(IdentitySnapshotsResponse {
        success: true,
        identity_uuid: uuid,
        has_reference,
        face_count,
    }))
}

#[derive(Debug, Serialize)]
pub struct GenerateIdentitySnapshotResponse {
    pub success: bool,
    pub message: String,
    pub identity_uuid: String,
}

async fn generate_identity_snapshots(
    State(_state): State<crate::api::server::AppState>,
    Path(uuid): Path<String>,
) -> Result<Json<GenerateIdentitySnapshotResponse>, (StatusCode, String)> {
    let output_dir = crate::core::config::OUTPUT_DIR.as_str();
    let manager = SnapshotManager::new(output_dir);

    manager
        .ensure_identity_dirs(&uuid)
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    tracing::info!("Snapshot generation requested for identity_uuid={}", uuid);

    Ok(Json(GenerateIdentitySnapshotResponse {
        success: true,
        message: "Identity snapshot directories created".to_string(),
        identity_uuid: uuid,
    }))
}

#[derive(Debug, Serialize)]
pub struct TeardownSnapshotResponse {
    pub success: bool,
    pub message: String,
    pub file_uuid: String,
}

async fn teardown_file_snapshots(
    Path(uuid): Path<String>,
) -> Result<Json<TeardownSnapshotResponse>, (StatusCode, String)> {
    let agent = SnapshotAgent::default();

    tracing::info!("Manual teardown requested for file_uuid={}", uuid);

    match agent.auto_tear_down(&uuid).await {
        Ok(_) => Ok(Json(TeardownSnapshotResponse {
            success: true,
            message: "Snapshot teardown completed".to_string(),
            file_uuid: uuid,
        })),
        Err(e) => {
            tracing::error!("Snapshot teardown failed for file_uuid={}: {}", uuid, e);
            Err((
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Teardown failed: {}", e),
            ))
        }
    }
}
