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

use crate::core::db::ResourceRecord;

pub fn identity_routes() -> Router<crate::api::server::AppState> {
    Router::new()
        .route("/api/v1/people", get(list_people))
        .route("/api/v1/people/search", post(search_people))
        .route("/api/v1/people/candidates", get(list_candidates))
        .route(
            "/api/v1/people/:identity_id/confirm-candidate",
            post(confirm_candidate),
        )
        .route(
            "/api/v1/people/:identity_id/reject-candidate",
            post(reject_candidate),
        )
        .route("/api/v1/files", get(list_files))
        .route("/api/v1/files/:uuid", get(get_file_detail))
        .route("/api/v1/files/:uuid/identities", get(get_file_identities))
        .route("/api/v1/identities/:uuid", get(get_identity_detail))
        .route("/api/v1/identities/:uuid/files", get(get_identity_files))
        .route("/api/v1/identities/:uuid/chunks", get(get_identity_chunks))
        .route("/api/v1/resources/register", post(register_resource))
        .route("/api/v1/resources/heartbeat", post(heartbeat_resource))
        .route("/api/v1/resources", get(list_resources))
}

// ... (Keep existing functions) ...

// --- People / Identity Endpoints ---

#[derive(Debug, Deserialize)]
pub struct PeopleQuery {
    page: Option<usize>,
    page_size: Option<usize>,
}

#[derive(Debug, Serialize)]
pub struct PeopleResponse {
    pub success: bool,
    pub total: i64,
    pub page: usize,
    pub page_size: usize,
    pub data: Vec<PeopleItem>,
}

#[derive(Debug, Serialize)]
pub struct PeopleItem {
    pub identity_id: Uuid,
    pub name: String,
    pub metadata: serde_json::Value,
    pub created_at: Option<chrono::DateTime<chrono::Utc>>,
}

async fn list_people(
    State(state): State<crate::api::server::AppState>,
    Query(params): Query<PeopleQuery>,
) -> Result<Json<PeopleResponse>, (StatusCode, String)> {
    let page = params.page.unwrap_or(1);
    let page_size = params.page_size.unwrap_or(20);
    let offset = ((page - 1) as i64) * (page_size as i64);

    let records = state
        .db
        .list_people(page_size as i32, offset)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    // TODO: Get total count
    let total = 100; // Placeholder

    let data = records
        .into_iter()
        .map(|r| PeopleItem {
            identity_id: r.uuid,
            name: r.name,
            metadata: r.metadata,
            created_at: r.created_at,
        })
        .collect();

    Ok(Json(PeopleResponse {
        success: true,
        total,
        page,
        page_size,
        data,
    }))
}

#[derive(Debug, Deserialize)]
pub struct SearchPeopleRequest {
    pub query: String,
    pub page: Option<usize>,
    pub page_size: Option<usize>,
}

async fn search_people(
    State(state): State<crate::api::server::AppState>,
    Json(req): Json<SearchPeopleRequest>,
) -> Result<Json<PeopleResponse>, (StatusCode, String)> {
    let page = req.page.unwrap_or(1);
    let page_size = req.page_size.unwrap_or(20);
    let offset = ((page - 1) as i64) * (page_size as i64);

    let records = state
        .db
        .search_people(&req.query, page_size as i32, offset)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    let data: Vec<PeopleItem> = records
        .into_iter()
        .map(|r| PeopleItem {
            identity_id: r.uuid,
            name: r.name,
            metadata: r.metadata,
            created_at: r.created_at,
        })
        .collect();

    Ok(Json(PeopleResponse {
        success: true,
        total: data.len() as i64, // Approximation
        page,
        page_size,
        data,
    }))
}

#[derive(Debug, Deserialize)]
pub struct CandidatesQuery {
    page: Option<usize>,
    page_size: Option<usize>,
}

#[derive(Debug, Serialize)]
pub struct CandidatesResponse {
    pub success: bool,
    pub total: i64,
    pub page: usize,
    pub page_size: usize,
    pub data: Vec<CandidateItem>,
}

#[derive(Debug, Serialize)]
pub struct CandidateItem {
    pub pre_chunk_id: i64,
    pub file_uuid: Uuid,
    pub data: serde_json::Value,
}

async fn list_candidates(
    State(state): State<crate::api::server::AppState>,
    Query(params): Query<CandidatesQuery>,
) -> Result<Json<CandidatesResponse>, (StatusCode, String)> {
    let page = params.page.unwrap_or(1);
    let page_size = params.page_size.unwrap_or(20);
    let offset = ((page - 1) as i64) * (page_size as i64);

    let records = state
        .db
        .get_people_candidates(page_size as i32, offset)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    let data = records
        .into_iter()
        .map(|r| CandidateItem {
            pre_chunk_id: r.id,
            file_uuid: r.file_uuid,
            data: r.data,
        })
        .collect();

    Ok(Json(CandidatesResponse {
        success: true,
        total: 0, // TODO
        page,
        page_size,
        data,
    }))
}

// --- Candidate Workflow Endpoints ---

#[derive(Debug, Deserialize)]
pub struct ConfirmCandidateRequest {
    pub pre_chunk_id: i64,
}

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

async fn confirm_candidate(
    State(state): State<crate::api::server::AppState>,
    Path(identity_id_str): Path<String>,
    Json(req): Json<ConfirmCandidateRequest>,
) -> Result<Json<ConfirmCandidateResponse>, (StatusCode, String)> {
    let identity_id = Uuid::parse_str(&identity_id_str)
        .map_err(|e| (StatusCode::BAD_REQUEST, format!("Invalid UUID: {}", e)))?;

    state
        .db
        .confirm_candidate(req.pre_chunk_id, identity_id)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    Ok(Json(ConfirmCandidateResponse {
        success: true,
        message: "Candidate confirmed and linked to identity".to_string(),
    }))
}

async fn reject_candidate(
    State(state): State<crate::api::server::AppState>,
    Path(_identity_id_str): Path<String>, // Unused, but consistent with route
    Json(req): Json<ConfirmCandidateRequest>,
) -> Result<Json<ConfirmCandidateResponse>, (StatusCode, String)> {
    state
        .db
        .reject_candidate(req.pre_chunk_id)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    Ok(Json(ConfirmCandidateResponse {
        success: true,
        message: "Candidate rejected".to_string(),
    }))
}

// --- Files Endpoints ---

#[derive(Debug, Deserialize)]
pub struct FilesQuery {
    page: Option<usize>,
    page_size: Option<usize>,
}

#[derive(Debug, Serialize)]
pub struct FilesResponse {
    pub success: bool,
    pub total: i64,
    pub page: usize,
    pub page_size: usize,
    pub data: Vec<FileItem>,
}

#[derive(Debug, Serialize)]
pub struct FileItem {
    pub file_uuid: String,
    pub file_name: String,
    pub file_path: String,
    pub status: String, // From probe or processing status
}

async fn list_files(
    State(state): State<crate::api::server::AppState>,
    Query(params): Query<FilesQuery>,
) -> Result<Json<FilesResponse>, (StatusCode, String)> {
    let page = params.page.unwrap_or(1);
    let page_size = params.page_size.unwrap_or(20);
    let offset = ((page - 1) as i64) * (page_size as i64);

    let records = state
        .db
        .list_files(page_size as i32, offset)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    let data = records
        .into_iter()
        .map(|r| FileItem {
            file_uuid: r.file_uuid,
            file_name: r.file_name,
            file_path: r.file_path,
            status: "ready".to_string(),
        })
        .collect();

    Ok(Json(FilesResponse {
        success: true,
        total: 0, // TODO
        page,
        page_size,
        data,
    }))
}

#[derive(Debug, Serialize)]
pub struct FileDetailResponse {
    pub success: bool,
    pub file_uuid: String,
    pub file_name: String,
    pub file_path: String,
    pub metadata: Option<serde_json::Value>,
    pub created_at: Option<chrono::DateTime<chrono::Utc>>,
}

async fn get_file_detail(
    State(state): State<crate::api::server::AppState>,
    Path(uuid): Path<String>,
) -> Result<Json<FileDetailResponse>, (StatusCode, String)> {
    let file = state
        .db
        .get_file_by_uuid(&uuid)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    match file {
        Some(f) => Ok(Json(FileDetailResponse {
            success: true,
            file_uuid: f.file_uuid,
            file_name: f.file_name,
            file_path: f.file_path,
            metadata: f.probe_json,
            created_at: f.created_at,
        })),
        None => Err((StatusCode::NOT_FOUND, format!("File not found: {}", uuid))),
    }
}

#[derive(Debug, Serialize)]
pub struct FileIdentitiesResponse {
    pub success: bool,
    pub file_uuid: String,
    pub total: i64,
    pub page: usize,
    pub page_size: usize,
    pub data: Vec<FileIdentityItem>,
}

#[derive(Debug, Serialize)]
pub struct FileIdentityItem {
    pub identity_id: i32,
    pub name: String,
    pub metadata: serde_json::Value,
    pub face_count: Option<i32>,
    pub speaker_count: Option<i32>,
    pub first_appearance: Option<f64>,
    pub last_appearance: Option<f64>,
    pub confidence: Option<f64>,
}

async fn get_file_identities(
    State(state): State<crate::api::server::AppState>,
    Path(uuid): Path<String>,
    Query(params): Query<FilesQuery>,
) -> Result<Json<FileIdentitiesResponse>, (StatusCode, String)> {
    let page = params.page.unwrap_or(1);
    let page_size = params.page_size.unwrap_or(20);
    let offset = ((page - 1) as i64) * (page_size as i64);

    let records = state
        .db
        .get_file_identities(&uuid, page_size as i32, offset)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    let data: Vec<FileIdentityItem> = records
        .into_iter()
        .map(|r| FileIdentityItem {
            identity_id: r.identity_id,
            name: r.name,
            metadata: r.metadata,
            face_count: r.face_count,
            speaker_count: r.speaker_count,
            first_appearance: r.first_appearance,
            last_appearance: r.last_appearance,
            confidence: r.confidence,
        })
        .collect();

    Ok(Json(FileIdentitiesResponse {
        success: true,
        file_uuid: uuid,
        total: data.len() as i64,
        page,
        page_size,
        data,
    }))
}

#[derive(Debug, Serialize)]
pub struct IdentityDetailResponse {
    pub success: bool,
    pub uuid: Uuid,
    pub name: String,
    pub identity_type: Option<String>,
    pub source: Option<String>,
    pub status: Option<String>,
    pub metadata: serde_json::Value,
    pub reference_data: serde_json::Value,
    pub tmdb_id: Option<i32>,
    pub tmdb_profile: Option<String>,
    pub created_at: Option<chrono::DateTime<chrono::Utc>>,
    pub updated_at: Option<chrono::DateTime<chrono::Utc>>,
}

async fn get_identity_detail(
    State(state): State<crate::api::server::AppState>,
    Path(uuid_str): Path<String>,
) -> Result<Json<IdentityDetailResponse>, (StatusCode, String)> {
    let uuid = Uuid::parse_str(&uuid_str)
        .map_err(|e| (StatusCode::BAD_REQUEST, format!("Invalid UUID: {}", e)))?;

    let identity = state
        .db
        .get_identity_by_uuid(&uuid)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    match identity {
        Some(i) => Ok(Json(IdentityDetailResponse {
            success: true,
            uuid: i.uuid,
            name: i.name,
            identity_type: i.identity_type,
            source: i.source,
            status: i.status,
            metadata: i.metadata,
            reference_data: i.reference_data,
            tmdb_id: i.tmdb_id,
            tmdb_profile: i.tmdb_profile,
            created_at: i.created_at,
            updated_at: i.updated_at,
        })),
        None => Err((
            StatusCode::NOT_FOUND,
            format!("Identity not found: {}", uuid),
        )),
    }
}

#[derive(Debug, Serialize)]
pub struct IdentityFilesResponse {
    pub success: bool,
    pub identity_uuid: Uuid,
    pub total: i64,
    pub page: usize,
    pub page_size: usize,
    pub data: Vec<IdentityFileItem>,
}

#[derive(Debug, Serialize)]
pub struct IdentityFileItem {
    pub file_uuid: String,
    pub file_name: String,
    pub file_path: String,
    pub status: String,
    pub face_count: Option<i32>,
    pub speaker_count: Option<i32>,
    pub first_appearance: Option<f64>,
    pub last_appearance: Option<f64>,
    pub confidence: Option<f64>,
}

async fn get_identity_files(
    State(state): State<crate::api::server::AppState>,
    Path(uuid_str): Path<String>,
    Query(params): Query<FilesQuery>,
) -> Result<Json<IdentityFilesResponse>, (StatusCode, String)> {
    let uuid = Uuid::parse_str(&uuid_str)
        .map_err(|e| (StatusCode::BAD_REQUEST, format!("Invalid UUID: {}", e)))?;

    let page = params.page.unwrap_or(1);
    let page_size = params.page_size.unwrap_or(20);
    let offset = ((page - 1) as i64) * (page_size as i64);

    let records = state
        .db
        .get_identity_files(&uuid, page_size as i32, offset)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    let data: Vec<IdentityFileItem> = records
        .into_iter()
        .map(|r| IdentityFileItem {
            file_uuid: r.file_uuid,
            file_name: r.file_name,
            file_path: r.file_path,
            status: r.status,
            face_count: r.face_count,
            speaker_count: r.speaker_count,
            first_appearance: r.first_appearance,
            last_appearance: r.last_appearance,
            confidence: r.confidence,
        })
        .collect();

    Ok(Json(IdentityFilesResponse {
        success: true,
        identity_uuid: uuid,
        total: data.len() as i64,
        page,
        page_size,
        data,
    }))
}

#[derive(Debug, Serialize)]
pub struct IdentityFacesResponse {
    pub success: bool,
    pub identity_uuid: Uuid,
    pub total: i64,
    pub page: usize,
    pub page_size: usize,
    pub data: Vec<IdentityFaceItem>,
}

#[derive(Debug, Serialize)]
pub struct IdentityFaceItem {
    pub id: i64,
    pub file_uuid: String,
    pub frame_number: i64,
    pub timestamp_secs: f64,
    pub face_id: Option<String>,
    pub bbox: BBox,
    pub confidence: f64,
}

#[derive(Debug, Serialize)]
pub struct BBox {
    pub x: f64,
    pub y: f64,
    pub width: f64,
    pub height: f64,
}

async fn get_identity_faces(
    State(state): State<crate::api::server::AppState>,
    Path(uuid_str): Path<String>,
    Query(params): Query<FilesQuery>,
) -> Result<Json<IdentityFacesResponse>, (StatusCode, String)> {
    let uuid = Uuid::parse_str(&uuid_str)
        .map_err(|e| (StatusCode::BAD_REQUEST, format!("Invalid UUID: {}", e)))?;

    let page = params.page.unwrap_or(1);
    let page_size = params.page_size.unwrap_or(50);
    let offset = ((page - 1) as i64) * (page_size as i64);

    let records = state
        .db
        .get_identity_faces(&uuid, page_size as i32, offset)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    let data: Vec<IdentityFaceItem> = records
        .into_iter()
        .map(|r| IdentityFaceItem {
            id: r.id,
            file_uuid: r.file_uuid,
            frame_number: r.frame_number,
            timestamp_secs: r.timestamp_secs,
            face_id: r.face_id,
            bbox: BBox {
                x: r.x,
                y: r.y,
                width: r.width,
                height: r.height,
            },
            confidence: r.confidence,
        })
        .collect();

    Ok(Json(IdentityFacesResponse {
        success: true,
        identity_uuid: uuid,
        total: data.len() as i64,
        page,
        page_size,
        data,
    }))
}

#[derive(Debug, Serialize)]
pub struct IdentityChunksResponse {
    pub success: bool,
    pub identity_uuid: Uuid,
    pub total: i64,
    pub page: usize,
    pub page_size: usize,
    pub data: Vec<IdentityChunkItem>,
}

#[derive(Debug, Serialize)]
pub struct IdentityChunkItem {
    pub id: i64,
    pub file_uuid: String,
    pub chunk_id: String,
    pub chunk_type: String,
    pub start_time: Option<f64>,
    pub end_time: Option<f64>,
    pub text_content: Option<String>,
}

async fn get_identity_chunks(
    State(state): State<crate::api::server::AppState>,
    Path(uuid_str): Path<String>,
    Query(params): Query<FilesQuery>,
) -> Result<Json<IdentityChunksResponse>, (StatusCode, String)> {
    let uuid = Uuid::parse_str(&uuid_str)
        .map_err(|e| (StatusCode::BAD_REQUEST, format!("Invalid UUID: {}", e)))?;

    let page = params.page.unwrap_or(1);
    let page_size = params.page_size.unwrap_or(20);
    let offset = ((page - 1) as i64) * (page_size as i64);

    let records = state
        .db
        .get_identity_chunks(&uuid, page_size as i32, offset)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    let data: Vec<IdentityChunkItem> = records
        .into_iter()
        .map(|r| IdentityChunkItem {
            id: r.id,
            file_uuid: r.file_uuid,
            chunk_id: r.chunk_id,
            chunk_type: r.chunk_type,
            start_time: r.start_time,
            end_time: r.end_time,
            text_content: r.text_content,
        })
        .collect();

    Ok(Json(IdentityChunksResponse {
        success: true,
        identity_uuid: uuid,
        total: data.len() as i64,
        page,
        page_size,
        data,
    }))
}

// --- Resource Registry Endpoints (Phase 5) ---

#[derive(Debug, Deserialize)]
pub struct RegisterResourceRequest {
    pub resource_id: String,
    pub resource_type: String,
    pub category: String,
    pub capabilities: Option<serde_json::Value>,
    pub config: Option<serde_json::Value>,
    pub metadata: Option<serde_json::Value>,
}

#[derive(Debug, Serialize)]
pub struct ResourceResponse {
    pub success: bool,
    pub message: String,
    pub data: Option<ResourceItem>,
}

#[derive(Debug, Serialize)]
pub struct ResourceItem {
    pub resource_id: String,
    pub resource_type: String,
    pub category: String,
    pub capabilities: Option<serde_json::Value>,
    pub status: String,
    pub last_heartbeat: Option<chrono::DateTime<chrono::Utc>>,
}

async fn register_resource(
    State(state): State<crate::api::server::AppState>,
    Json(req): Json<RegisterResourceRequest>,
) -> Result<Json<ResourceResponse>, (StatusCode, String)> {
    let resource = ResourceRecord {
        resource_id: req.resource_id.clone(),
        resource_type: req.resource_type.clone(),
        category: req.category.clone(),
        capabilities: req.capabilities,
        config: req.config,
        metadata: req.metadata,
        status: "online".to_string(),
        last_heartbeat: None,
        created_at: None,
    };

    state
        .db
        .register_resource(resource)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    Ok(Json(ResourceResponse {
        success: true,
        message: "Resource registered successfully".to_string(),
        data: None, // We could return the full record, but simplified for now
    }))
}

#[derive(Debug, Deserialize)]
pub struct HeartbeatRequest {
    pub resource_id: String,
    pub status: Option<String>,
}

async fn heartbeat_resource(
    State(state): State<crate::api::server::AppState>,
    Json(req): Json<HeartbeatRequest>,
) -> Result<Json<ResourceResponse>, (StatusCode, String)> {
    let status = req.status.unwrap_or("online".to_string());
    state
        .db
        .heartbeat_resource(&req.resource_id, &status)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    Ok(Json(ResourceResponse {
        success: true,
        message: "Heartbeat received".to_string(),
        data: None,
    }))
}

async fn list_resources(
    State(state): State<crate::api::server::AppState>,
) -> Result<Json<ResourceResponse>, (StatusCode, String)> {
    let records = state
        .db
        .list_resources()
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    let data: Vec<ResourceItem> = records
        .into_iter()
        .map(|r| ResourceItem {
            resource_id: r.resource_id,
            resource_type: r.resource_type,
            category: r.category,
            capabilities: r.capabilities,
            status: r.status,
            last_heartbeat: r.last_heartbeat,
        })
        .collect();

    Ok(Json(ResourceResponse {
        success: true,
        message: "Resources listed".to_string(),
        data: None,
    }))
}
