use crate::AppState;
use axum::{
    extract::{Path, Query, State},
    http::StatusCode,
    response::IntoResponse,
    routing::{get, post},
    Json, Router,
};
use contracts::auth_middleware::{require_admin, AuthUser};
use db::models::verification::{VerificationRepository};
use serde::Deserialize;
use uuid::Uuid;

use super::role_meta::{role_key_to_display, role_to_table};

/// Creates an entry in approval_requests after verification is approved.
/// This is the bridge between Verification Management and Approval Management.
async fn create_approval_request_from_verification(
    pool: &sqlx::PgPool,
    verification: &db::models::verification::Verification,
) -> Result<(), sqlx::Error> {
    // Determine entity_type and entity_id from the verification payload
    let payload = &verification.payload;
    let entity_type = match verification.case_type.as_str() {
        "JOB_APPROVAL" => "JOB",
        "REQUIREMENT_APPROVAL" => "REQUIREMENT",
        "PORTFOLIO_APPROVAL" => "PORTFOLIO",
        _ => "PROFILE",
    };

    // Extract entity_id from payload (could be entity_id, job_id, requirement_id, etc.)
    let entity_id = payload
        .get("entity_id")
        .or_else(|| payload.get("job_id"))
        .or_else(|| payload.get("requirement_id"))
        .and_then(|v| v.as_str())
        .and_then(|s| Uuid::parse_str(s).ok())
        .unwrap_or(verification.user_id); // Fall back to user_id if no entity_id found

    let approval_type = match verification.case_type.as_str() {
        "JOB_APPROVAL" => "JOB",
        "REQUIREMENT_APPROVAL" => "REQUIREMENT",
        "PORTFOLIO_APPROVAL" => "PORTFOLIO",
        "COMPANY_APPROVAL" => "BUSINESS",
        _ => "PROFILE",
    };

    sqlx::query(
        r#"
        INSERT INTO approval_requests (entity_type, entity_id, approval_type, status, submitted_by_user_id)
        VALUES ($1, $2, $3, 'PENDING', $4)
        ON CONFLICT (entity_type, entity_id) DO UPDATE
        SET status = 'PENDING', updated_at = NOW()
        "#,
    )
    .bind(entity_type)
    .bind(entity_id)
    .bind(approval_type)
    .bind(verification.user_id)
    .execute(pool)
    .await?;

    Ok(())
}

pub fn router() -> Router<AppState> {
    Router::new()
        .route("/", get(list_verifications))
        .route("/presign", post(presign_document))
        .route("/{id}", get(get_verification))
        .route("/{id}/approve", post(approve_verification))
        .route("/{id}/reject", post(reject_verification))
        .route("/{id}/notes", post(add_notes))
        .route("/{id}/request-documents", post(request_documents))
        .route("/{id}/request-revision", post(request_revision))
}

#[derive(Deserialize)]
pub struct PresignPayload {
    pub url: String,
}

/// POST /api/admin/verifications/presign
/// Mints a short-lived signed URL for a document/image previously submitted in a
/// verification's profile_data, so the admin panel never has to render or link to
/// the permanent storage URL directly.
async fn presign_document(
    auth: AuthUser,
    State(state): State<AppState>,
    Json(payload): Json<PresignPayload>,
) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    match state.storage.presign(&payload.url, std::time::Duration::from_secs(300)).await {
        Ok(url) => (StatusCode::OK, Json(serde_json::json!({ "url": url, "expires_in": 300 }))).into_response(),
        Err(e) => {
            tracing::error!("Failed to presign document URL: {:?}", e);
            (StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ "error": "Failed to generate view URL" }))).into_response()
        }
    }
}

#[derive(Deserialize)]
pub struct ListQuery {
    pub status: Option<String>,
    pub case_type: Option<String>,
    pub page: Option<i64>,
    pub limit: Option<i64>,
}

async fn list_verifications(
    auth: AuthUser,
    State(state): State<AppState>,
    Query(q): Query<ListQuery>,
) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    let page = q.page.unwrap_or(1);
    let limit = q.limit.unwrap_or(20);

    match VerificationRepository::list(
        &state.pool,
        q.status.as_deref(),
        q.case_type.as_deref(),
        page,
        limit,
    )
    .await
    {
        Ok(items) => (
            StatusCode::OK,
            Json(serde_json::json!({ "items": items, "page": page, "limit": limit })),
        )
            .into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}

async fn get_verification(
    auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    match VerificationRepository::get_by_id(&state.pool, id).await {
        Ok(Some(v)) => (StatusCode::OK, Json(v)).into_response(),
        Ok(None) => (StatusCode::NOT_FOUND, "Verification not found").into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}

#[derive(Deserialize)]
pub struct ActionPayload {
    pub notes: Option<String>,
    pub reason: Option<String>,
}

async fn trigger_rejection(
    state: &AppState,
    user_id: Uuid,
    role_key: &str,
    case_type: &str,
    reason: Option<&str>,
) -> Result<(), sqlx::Error> {
    let role_key = role_key.to_uppercase();
    let reason_str = reason.unwrap_or("Verification rejected");

    if case_type == "PROFILE_VERIFICATION" {
        let table = match role_to_table(&role_key) {
            Some(t) => t,
            None => return Ok(()),
        };

        let user_role_profile_id = match sqlx::query_scalar::<_, Uuid>(
            "SELECT id FROM user_role_profiles WHERE user_id = $1 AND role_key = $2",
        )
        .bind(user_id)
        .bind(&role_key)
        .fetch_optional(&state.pool)
        .await?
        {
            Some(id) => id,
            None => return Ok(()),
        };

        let query = format!(
            "UPDATE {} SET status = 'REJECTED', updated_at = NOW() WHERE id = $1",
            table
        );
        sqlx::query(&query).bind(user_role_profile_id).execute(&state.pool).await?;

        // Send Email
    if let Ok(user) = db::models::user::UserRepository::get_by_id(&state.pool, user_id).await {
        let display = role_key_to_display(&role_key);
        let user_name = format!("{} {}", user.first_name.unwrap_or_default(), user.last_name.unwrap_or_default());
        if let Err(e) = state.mail.send_approval_rejected_email(&user.email, &user_name, &display, reason_str).await {
            tracing::error!("Failed to send approval-rejected email to {}: {:?}", user.email, e);
        }
    }

    // Send in-app notification
    sqlx::query(
        r#"INSERT INTO notifications (user_id, title, body, type, reference_id)
           VALUES ($1, $2, $3, $4, $5)"#,
    )
    .bind(user_id)
    .bind("Profile Verification Update")
    .bind(format!("Your {} profile was not approved. Reason: {}", role_key_to_display(&role_key), reason_str))
    .bind("VERIFICATION")
    .bind(user_id)
    .execute(&state.pool)
    .await
    .ok();
    }

    Ok(())
}

async fn approve_verification(
    auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
    Json(payload): Json<ActionPayload>,
) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    match VerificationRepository::update_status(
        &state.pool,
        id,
        "APPROVED",
        Some(auth.user_id),
        payload.notes.as_deref(),
        None,
    )
    .await
    {
        Ok(v) => {
            // Create an entry in approval_requests so it appears in Approval Management
            // for the second-level review (final approval/rejection)
            if let Err(e) = create_approval_request_from_verification(&state.pool, &v).await {
                eprintln!("Failed to create approval request: {}", e);
            }

            // Send notification that verification passed first stage
            // (Approval Management will handle final approval email)
            if let Ok(user) = db::models::user::UserRepository::get_by_id(&state.pool, v.user_id).await {
                let display = role_key_to_display(&v.role_key);
                let user_name = format!("{} {}", user.first_name.unwrap_or_default(), user.last_name.unwrap_or_default());
                // Use a "verification passed" notification instead of final approval
                if let Err(e) = state.mail.send_approval_approved_email(&user.email, &user_name, &display).await {
                    tracing::error!("Failed to send approval-approved email to {}: {:?}", user.email, e);
                }
            }

            // Send in-app notification - profile verified, pending final approval
            sqlx::query(
                r#"INSERT INTO notifications (user_id, title, body, type, reference_id)
                   VALUES ($1, $2, $3, $4, $5)"#,
            )
            .bind(v.user_id)
            .bind("Profile Verified — Pending Final Approval")
            .bind(format!("Your {} profile has been verified and is now pending final approval. You'll be notified once approved.", role_key_to_display(&v.role_key)))
            .bind("VERIFICATION")
            .bind(v.id)
            .execute(&state.pool)
            .await
            .ok();

            (StatusCode::OK, Json(v)).into_response()
        }
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}

async fn reject_verification(
    auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
    Json(payload): Json<ActionPayload>,
) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    match VerificationRepository::update_status(
        &state.pool,
        id,
        "REJECTED",
        Some(auth.user_id),
        payload.notes.as_deref(),
        payload.reason.as_deref(),
    )
    .await
    {
        Ok(v) => {
            let _ = trigger_rejection(&state, v.user_id, &v.role_key, &v.case_type, payload.reason.as_deref()).await;
            (StatusCode::OK, Json(v)).into_response()
        }
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}

/// POST /api/admin/verifications/:id/notes
/// Adds internal notes without changing status (for reviewer comments).
async fn add_notes(
    auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
    Json(payload): Json<ActionPayload>,
) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    let notes = payload.notes.unwrap_or_default();
    match VerificationRepository::update_status(
        &state.pool,
        id,
        "UNDER_REVIEW",
        Some(auth.user_id),
        Some(&notes),
        None,
    )
    .await
    {
        Ok(v) => (StatusCode::OK, Json(v)).into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}

#[derive(serde::Deserialize)]
pub struct RequestDocumentsPayload {
    /// Human-readable message to the user describing what's needed.
    pub message: String,
}

/// POST /api/admin/verifications/:id/request-documents
/// Sets status to DOCUMENTS_REQUESTED and notifies the user.
async fn request_documents(
    auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
    Json(payload): Json<RequestDocumentsPayload>,
) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    match VerificationRepository::update_status(
        &state.pool,
        id,
        "DOCUMENTS_REQUESTED",
        Some(auth.user_id),
        Some(&payload.message),
        None,
    )
    .await
    {
        Ok(v) => {
            // Notify the user via in-app notification
            sqlx::query(
                r#"INSERT INTO notifications (user_id, title, body, type, reference_id)
                   VALUES ($1, $2, $3, $4, $5)"#,
            )
            .bind(v.user_id)
            .bind("Action Required — Documents Needed")
            .bind(format!("Please resubmit your documents: {}", payload.message))
            .bind("DOCUMENT_REQUEST")
            .bind(v.id)
            .execute(&state.pool)
            .await
            .ok();

            // Send email notification
            if let Ok(user) = db::models::user::UserRepository::get_by_id(&state.pool, v.user_id).await {
                let display = role_key_to_display(&v.role_key);
                let user_name = format!("{} {}", user.first_name.unwrap_or_default(), user.last_name.unwrap_or_default());
                let _ = state.mail.send_documents_requested_email(&user.email, &user_name, &display, &payload.message).await;
            }

            (StatusCode::OK, Json(v)).into_response()
        }
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}

/// POST /api/admin/verifications/:id/request-revision
/// Sets status to REVISION_REQUESTED and notifies the user.
async fn request_revision(
    auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
    Json(payload): Json<RequestDocumentsPayload>,
) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    match VerificationRepository::update_status(
        &state.pool,
        id,
        "REVISION_REQUESTED",
        Some(auth.user_id),
        Some(&payload.message),
        None,
    )
    .await
    {
        Ok(v) => {
            sqlx::query(
                r#"INSERT INTO notifications (user_id, title, body, type, reference_id)
                   VALUES ($1, $2, $3, $4, $5)"#,
            )
            .bind(v.user_id)
            .bind("Action Required — Revision Requested")
            .bind(format!("Please revise your submission: {}", payload.message))
            .bind("REVISION_REQUEST")
            .bind(v.id)
            .execute(&state.pool)
            .await
            .ok();

            // Send email notification
            if let Ok(user) = db::models::user::UserRepository::get_by_id(&state.pool, v.user_id).await {
                let display = role_key_to_display(&v.role_key);
                let user_name = format!("{} {}", user.first_name.unwrap_or_default(), user.last_name.unwrap_or_default());
                let _ = state.mail.send_revision_requested_email(&user.email, &user_name, &display, &payload.message).await;
            }

            (StatusCode::OK, Json(v)).into_response()
        }
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}
