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::activity_log::ActivityLogRepository;
use db::models::job::JobRepository;
use db::models::requirement::RequirementRepository;
use db::models::role::RoleRepository;
use db::models::user::UserRepository;
use db::models::verification::Verification;
use db::models::verification::VerificationRepository;
use serde::Deserialize;
use uuid::Uuid;

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

#[derive(Default)]
struct RoleProfileSnapshot {
    display_name: Option<String>,
    email: Option<String>,
    phone: Option<String>,
    location: Option<String>,
}

pub fn router() -> Router<AppState> {
    Router::new()
        .route("/", get(list_pending))
        .route("/submission/{user_id}", get(get_submission))
        .route("/profiles/{id}/approve", post(approve_profile))
        .route("/profiles/{id}/reject", post(reject_profile))
        .route("/jobs/{id}/approve", post(approve_job))
        .route("/jobs/{id}/reject", post(reject_job))
        .route("/requirements/{id}/approve", post(approve_requirement))
        .route("/requirements/{id}/reject", post(reject_requirement))
}

#[derive(Deserialize)]
pub struct RoleKeyQuery {
    #[serde(rename = "roleKey", alias = "role_key")]
    pub role_key: Option<String>,
}

/// GET /api/admin/approvals/submission/{user_id}?roleKey=PHOTOGRAPHER
async fn get_submission(
    auth: AuthUser,
    State(state): State<AppState>,
    Path(user_id): Path<Uuid>,
    Query(q): Query<RoleKeyQuery>,
) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    let user = match UserRepository::get_by_id(&state.pool, user_id).await {
        Ok(u) => u,
        Err(_) => return (StatusCode::NOT_FOUND, "User not found").into_response(),
    };

    let role_filter = q.role_key.as_deref().map(|s| s.to_uppercase());
    let latest_verification = sqlx::query_as::<_, Verification>(
        r#"
        SELECT *
        FROM verifications
        WHERE user_id = $1
          AND ($2::TEXT IS NULL OR UPPER(role_key) = UPPER($2))
        ORDER BY created_at DESC
        LIMIT 1
        "#,
    )
    .bind(user_id)
    .bind(role_filter.as_deref())
    .fetch_optional(&state.pool)
    .await
    .ok()
    .flatten();

    let submission = latest_verification.as_ref().map(|v| {
        let completed_at = match v.status.as_str() {
            "APPROVED" | "REJECTED" => Some(v.updated_at),
            _ => None,
        };
        serde_json::json!({
            "id": v.id,
            "status": v.status,
            "case_type": v.case_type,
            "priority": v.priority,
            "payload": v.payload,
            "documents": v.documents,
            "notes": v.notes,
            "rejection_reason": v.rejection_reason,
            "created_at": v.created_at,
            "updated_at": v.updated_at,
            "completed_at": completed_at,
        })
    });

    (
        StatusCode::OK,
        Json(serde_json::json!({
            "user": {
                "id": user.id,
                "name": format!("{} {}", user.first_name.unwrap_or_default(), user.last_name.unwrap_or_default()),
                "email": user.email,
                "phone": null,
                "status": user.status,
                "email_verified": user.email_verified,
                "created_at": user.created_at,
            },
            "role_key": q.role_key,
            "submission": submission,
            // Legacy compatibility for older admin UI code paths.
            "onboarding": submission,
            "message": "Submission details are sourced from Verification records.",
        })),
    )
        .into_response()
}

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

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

async fn finalize_verification_case_for_entity(
    pool: &sqlx::PgPool,
    entity_id: Uuid,
    case_type: &str,
    final_status: &str,
) {
    let _ = sqlx::query(
        r#"
        UPDATE verifications
        SET status = $1, updated_at = NOW()
        WHERE case_type = $2
          AND status = 'APPROVED'
          AND payload->>'entity_id' = $3
        "#,
    )
    .bind(final_status)
    .bind(case_type)
    .bind(entity_id.to_string())
    .execute(pool)
    .await;
}

/// Deprecated: Use /api/admin/verifications instead.
async fn list_pending(
    auth: AuthUser,
) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    (
        StatusCode::OK,
        Json(serde_json::json!({
            "message": "This endpoint is deprecated. Please use /api/admin/verifications for profile and job approvals.",
            "jobs": [],
            "requirements": [],
            "profiles_summary": {}
        })),
    )
        .into_response()
}

async fn activate_profile_after_final_approval(
    state: &AppState,
    user_id: Uuid,
    role_key: &str,
) -> Result<(), sqlx::Error> {
    let role_key = role_key.to_uppercase();
    let table = match role_to_table(&role_key) {
        Some(t) => t,
        None => return Ok(()),
    };

    // 'APPROVED' is the canonical verified-profile status checked everywhere
    // downstream (create_job, dashboard counts, company/customer/professional
    // models) — writing 'ACTIVE' here left newly-approved companies unable to
    // post jobs since 'ACTIVE' is also company_profiles' pre-verification default.
    //
    // company_profiles/job_seeker_profiles are keyed by user_id directly and
    // have no user_role_profile_id column, so they must be matched on
    // user_id. Every other role table (photographer_profiles, etc.) is keyed
    // by its own id with a separate user_role_profile_id FK column —
    // matching those against `id` (as this previously did) never hit any
    // row, silently no-opping the approval.
    if role_key == "COMPANY" || role_key == "JOB_SEEKER" || role_key == "CUSTOMER" {
        let query = format!(
            "UPDATE {} SET status = 'APPROVED', updated_at = NOW() WHERE user_id = $1",
            table
        );
        sqlx::query(&query).bind(user_id).execute(&state.pool).await?;
    } else {
        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 = 'APPROVED', updated_at = NOW() WHERE user_role_profile_id = $1",
            table
        );
        sqlx::query(&query).bind(user_role_profile_id).execute(&state.pool).await?;
    }

    // Update user's role to match the approved role_key and set status to ACTIVE.
    // `users` has no `role` text column — role is tracked via `role_id` FK into `roles`.
    if let Ok(role) = RoleRepository::get_by_key(&state.pool, &role_key).await {
        sqlx::query(
            "UPDATE users SET role_id = $1, status = 'ACTIVE', updated_at = NOW() WHERE id = $2",
        )
        .bind(role.id)
        .bind(user_id)
        .execute(&state.pool)
        .await?;

        sqlx::query(
            "INSERT INTO user_role_assignments (user_id, role_id, status, approved_at) VALUES ($1, $2, 'APPROVED', NOW()) ON CONFLICT (user_id, role_id) DO UPDATE SET status = 'APPROVED', approved_at = NOW()",
        )
        .bind(user_id)
        .bind(role.id)
        .execute(&state.pool)
        .await
        .ok();
    }

    if let Ok(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());
        let _ = state
            .mail
            .send_approval_approved_email(&user.email, &user_name, &display)
            .await;
    }

    // Send in-app notification for final approval
    sqlx::query(
        r#"INSERT INTO notifications (user_id, title, body, type, reference_id)
           VALUES ($1, $2, $3, $4, $5)"#,
    )
    .bind(user_id)
    .bind("Congratulations! Your Profile is Now Active")
    .bind(format!("Your {} profile has been fully approved and is now active on Nxtgauge.", role_key_to_display(&role_key)))
    .bind("PROFILE")
    .bind(user_id)
    .execute(&state.pool)
    .await
    .ok();

    sync_user_role_profile_after_final_decision(state, user_id, &role_key, "APPROVED", None).await?;

    Ok(())
}

async fn reject_profile_after_final_approval(
    state: &AppState,
    user_id: Uuid,
    role_key: &str,
    reason: Option<&str>,
) -> Result<(), sqlx::Error> {
    let role_key = role_key.to_uppercase();
    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(()),
    };

    if role_key == "COMPANY" || role_key == "JOB_SEEKER" || role_key == "CUSTOMER" {
        let query = format!(
            "UPDATE {} SET status = 'REJECTED', updated_at = NOW() WHERE user_id = $1",
            table
        );
        sqlx::query(&query).bind(user_id).execute(&state.pool).await?;
    } else {
        let query = format!(
            "UPDATE {} SET status = 'REJECTED', updated_at = NOW() WHERE user_role_profile_id = $1",
            table
        );
        sqlx::query(&query).bind(user_role_profile_id).execute(&state.pool).await?;
    }

    if let Ok(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());
        let _ = state
            .mail
            .send_approval_rejected_email(
                &user.email,
                &user_name,
                &display,
                reason.unwrap_or("Rejected by final approval"),
            )
            .await;
    }

    // Send in-app notification for final rejection
    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.unwrap_or("Rejected by final approval")))
    .bind("PROFILE")
    .bind(user_id)
    .execute(&state.pool)
    .await
    .ok();

    sync_user_role_profile_after_final_decision(
        state,
        user_id,
        &role_key,
        "REJECTED",
        reason,
    )
    .await?;

    Ok(())
}

async fn load_role_profile_snapshot(
    state: &AppState,
    user_id: Uuid,
    role_key: &str,
) -> Result<RoleProfileSnapshot, sqlx::Error> {
    use sqlx::Row;

    let user = UserRepository::get_by_id(&state.pool, user_id).await.ok();
    let fallback_name = user.as_ref().map(|u| {
        let full = format!(
            "{} {}",
            u.first_name.clone().unwrap_or_default(),
            u.last_name.clone().unwrap_or_default()
        )
        .trim()
        .to_string();
        if full.is_empty() { None } else { Some(full) }
    }).flatten();
    let fallback_email = user.as_ref().map(|u| u.email.clone());

    if role_key == "COMPANY" {
        let row = sqlx::query(
            r#"
            SELECT company_name, contact_email, contact_phone, city
            FROM company_profiles
            WHERE user_id = $1
            "#,
        )
        .bind(user_id)
        .fetch_optional(&state.pool)
        .await?;

        if let Some(row) = row {
            return Ok(RoleProfileSnapshot {
                display_name: row.try_get("company_name").ok().flatten().or(fallback_name),
                email: row.try_get("contact_email").ok().flatten().or(fallback_email),
                phone: row.try_get("contact_phone").ok().flatten(),
                location: row.try_get("city").ok().flatten(),
            });
        }
    }

    let mut custom_data = serde_json::Value::Null;

    if role_key == "JOB_SEEKER" || role_key == "CUSTOMER" {
        let table = if role_key == "JOB_SEEKER" {
            "job_seeker_profiles"
        } else {
            "customer_profiles"
        };
        let row = sqlx::query(&format!(
            r#"SELECT custom_data FROM {} WHERE user_id = $1"#,
            table
        ))
        .bind(user_id)
        .fetch_optional(&state.pool)
        .await?;

        if let Some(row) = row {
            custom_data = row
                .try_get::<serde_json::Value, _>("custom_data")
                .unwrap_or(serde_json::Value::Null)
                .get("basic_info")
                .cloned()
                .unwrap_or(serde_json::Value::Null);
        }
    } else if let Some(table) = role_to_table(role_key) {
        let urp_id = 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?;

        if let Some(urp_id) = urp_id {
            let row = sqlx::query(&format!(
                r#"SELECT custom_data FROM {} WHERE user_role_profile_id = $1"#,
                table
            ))
            .bind(urp_id)
            .fetch_optional(&state.pool)
            .await?;

            if let Some(row) = row {
                custom_data = row
                    .try_get::<serde_json::Value, _>("custom_data")
                    .unwrap_or(serde_json::Value::Null);
            }
        }
    }

    let first_name = custom_data.get("first_name").and_then(|v| v.as_str()).map(str::trim).filter(|v| !v.is_empty());
    let last_name = custom_data.get("last_name").and_then(|v| v.as_str()).map(str::trim).filter(|v| !v.is_empty());
    let display_name = match (first_name, last_name) {
        (Some(first), Some(last)) => Some(format!("{} {}", first, last)),
        (Some(first), None) => Some(first.to_string()),
        (None, Some(last)) => Some(last.to_string()),
        (None, None) => custom_data
            .get("business_name")
            .or_else(|| custom_data.get("owner_name"))
            .or_else(|| custom_data.get("company_name"))
            .and_then(|v| v.as_str())
            .map(str::trim)
            .filter(|v| !v.is_empty())
            .map(String::from)
            .or(fallback_name),
    };

    Ok(RoleProfileSnapshot {
        display_name,
        email: custom_data
            .get("email")
            .or_else(|| custom_data.get("company_email"))
            .and_then(|v| v.as_str())
            .map(str::trim)
            .filter(|v| !v.is_empty())
            .map(String::from)
            .or(fallback_email),
        phone: custom_data
            .get("phone")
            .or_else(|| custom_data.get("company_phone"))
            .and_then(|v| v.as_str())
            .map(str::trim)
            .filter(|v| !v.is_empty())
            .map(String::from),
        location: custom_data
            .get("location")
            .or_else(|| custom_data.get("city"))
            .and_then(|v| v.as_str())
            .map(str::trim)
            .filter(|v| !v.is_empty())
            .map(String::from),
    })
}

async fn sync_user_role_profile_after_final_decision(
    state: &AppState,
    user_id: Uuid,
    role_key: &str,
    final_status: &str,
    rejection_reason: Option<&str>,
) -> Result<(), sqlx::Error> {
    let snapshot = load_role_profile_snapshot(state, user_id, role_key).await.unwrap_or_default();
    let approval_status = if final_status == "APPROVED" { "APPROVED" } else { "REJECTED" };

    sqlx::query(
        r#"
        INSERT INTO user_role_profiles (
            user_id, role_key, display_name, email, phone, location, status,
            verification_status, approval_status, rejection_reason, approved_at, verified_at
        )
        VALUES (
            $1, $2, $3, $4, $5, $6, $7,
            $8, $9, $10,
            CASE WHEN $9 = 'APPROVED' THEN NOW() ELSE NULL END,
            CASE WHEN $8 = 'APPROVED' THEN NOW() ELSE NULL END
        )
        ON CONFLICT (user_id, role_key) DO UPDATE SET
            display_name = COALESCE(EXCLUDED.display_name, user_role_profiles.display_name),
            email = COALESCE(EXCLUDED.email, user_role_profiles.email),
            phone = COALESCE(EXCLUDED.phone, user_role_profiles.phone),
            location = COALESCE(EXCLUDED.location, user_role_profiles.location),
            status = EXCLUDED.status,
            verification_status = EXCLUDED.verification_status,
            approval_status = EXCLUDED.approval_status,
            rejection_reason = EXCLUDED.rejection_reason,
            approved_at = CASE
                WHEN EXCLUDED.approval_status = 'APPROVED' THEN NOW()
                ELSE user_role_profiles.approved_at
            END,
            verified_at = CASE
                WHEN EXCLUDED.verification_status = 'APPROVED' THEN NOW()
                ELSE user_role_profiles.verified_at
            END,
            updated_at = NOW()
        "#,
    )
    .bind(user_id)
    .bind(role_key)
    .bind(snapshot.display_name)
    .bind(snapshot.email)
    .bind(snapshot.phone)
    .bind(snapshot.location)
    .bind(final_status)
    .bind(final_status)
    .bind(approval_status)
    .bind(rejection_reason)
    .execute(&state.pool)
    .await?;

    Ok(())
}

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

    let verification = match VerificationRepository::get_by_id(&state.pool, id).await {
        Ok(Some(v)) => v,
        Ok(None) => return (StatusCode::NOT_FOUND, "Verification not found").into_response(),
        Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", e)).into_response(),
    };

    if verification.status != "APPROVED" {
        return (
            StatusCode::BAD_REQUEST,
            "Verification must be APPROVED in Verification Management before final approval",
        )
            .into_response();
    }

    match activate_profile_after_final_approval(&state, verification.user_id, &verification.role_key).await {
        Ok(_) => {}
        Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", e)).into_response(),
    }

    match VerificationRepository::update_status(
        &state.pool,
        id,
        "COMPLETED",
        Some(auth.user_id),
        Some("Final approval completed"),
        None,
    )
    .await
    {
        Ok(v) => (StatusCode::OK, Json(v)).into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", e)).into_response(),
    }
}

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

    let verification = match VerificationRepository::get_by_id(&state.pool, id).await {
        Ok(Some(v)) => v,
        Ok(None) => return (StatusCode::NOT_FOUND, "Verification not found").into_response(),
        Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", e)).into_response(),
    };

    if verification.status != "APPROVED" {
        return (
            StatusCode::BAD_REQUEST,
            "Verification must be APPROVED in Verification Management before final rejection",
        )
            .into_response();
    }

    match reject_profile_after_final_approval(
        &state,
        verification.user_id,
        &verification.role_key,
        payload.reason.as_deref(),
    )
    .await
    {
        Ok(_) => {}
        Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", e)).into_response(),
    }

    match VerificationRepository::update_status(
        &state.pool,
        id,
        "FINAL_REJECTED",
        Some(auth.user_id),
        Some("Final approval rejected"),
        payload.reason.as_deref(),
    )
    .await
    {
        Ok(v) => (StatusCode::OK, Json(v)).into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", e)).into_response(),
    }
}

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

    let existing = match JobRepository::get_by_id(&state.pool, id).await {
        Ok(Some(job)) => job,
        Ok(None) => return (StatusCode::NOT_FOUND, "Job not found").into_response(),
        Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", e)).into_response(),
    };

    if existing.status != "PENDING_APPROVAL" {
        return (StatusCode::BAD_REQUEST, "Job is not pending approval").into_response();
    }

    match JobRepository::approve(&state.pool, id, auth.user_id).await {
        Ok(job) => {
            let _ = ActivityLogRepository::create(
                &state.pool,
                auth.user_id,
                "EMPLOYEE",
                id,
                "JOB",
                "APPROVE",
                None,
            )
            .await;
            
            let company_info = sqlx::query_as::<_, (String, String, Uuid)>(
                "SELECT CONCAT(u.first_name, ' ', u.last_name) AS u_full_name, u.email, u.id FROM company_profiles c JOIN users u ON u.id = c.user_id WHERE c.id = $1",
            )
            .bind(existing.company_id)
            .fetch_optional(&state.pool)
            .await;

            if let Ok(Some((name, email, user_uuid))) = company_info {
                if let Err(e) = state.mail.send_job_approved_email(&email, &name, &existing.title).await {
                    tracing::error!("Failed to send job-approved email to {}: {:?}", email, e);
                }

                // Send in-app notification to company
                sqlx::query(
                    r#"INSERT INTO notifications (user_id, title, body, type, reference_id)
                       VALUES ($1, $2, $3, $4, $5)"#,
                )
                .bind(user_uuid)
                .bind("Your Job is Now Live!")
                .bind(format!("Your job posting '{}' has been approved and is now visible to job seekers.", existing.title))
                .bind("JOB")
                .bind(id)
                .execute(&state.pool)
                .await
                .ok();
            }
            finalize_verification_case_for_entity(&state.pool, id, "JOB_APPROVAL", "COMPLETED").await;
            (StatusCode::OK, Json(job)).into_response()
        }
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", e)).into_response(),
    }
}

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

    let existing = match JobRepository::get_by_id(&state.pool, id).await {
        Ok(Some(job)) => job,
        Ok(None) => return (StatusCode::NOT_FOUND, "Job not found").into_response(),
        Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", e)).into_response(),
    };

    if existing.status != "PENDING_APPROVAL" {
        return (StatusCode::BAD_REQUEST, "Job is not pending approval").into_response();
    }

    match JobRepository::reject(&state.pool, id, payload.reason.clone()).await {
        Ok(job) => {
            let _ = ActivityLogRepository::create(
                &state.pool,
                auth.user_id,
                "EMPLOYEE",
                id,
                "JOB",
                "REJECT",
                Some(serde_json::json!({ "reason": payload.reason })),
            )
            .await;

            let company_info = sqlx::query_as::<_, (String, String, Uuid)>(
                "SELECT CONCAT(u.first_name, ' ', u.last_name) AS u_full_name, u.email, u.id FROM company_profiles c JOIN users u ON u.id = c.user_id WHERE c.id = $1",
            )
            .bind(existing.company_id)
            .fetch_optional(&state.pool)
            .await;

            if let Ok(Some((name, email, user_uuid))) = company_info {
                let r = payload.reason.as_deref().unwrap_or("Rejected by admin");
                if let Err(e) = state.mail.send_job_rejected_email(&email, &name, &existing.title, r).await {
                    tracing::error!("Failed to send job-rejected email to {}: {:?}", email, e);
                }

                // Send in-app notification to company
                sqlx::query(
                    r#"INSERT INTO notifications (user_id, title, body, type, reference_id)
                       VALUES ($1, $2, $3, $4, $5)"#,
                )
                .bind(user_uuid)
                .bind("Your Job Posting Was Not Approved")
                .bind(format!("Your job posting '{}' was not approved. Reason: {}", existing.title, r))
                .bind("JOB")
                .bind(id)
                .execute(&state.pool)
                .await
                .ok();
            }
            finalize_verification_case_for_entity(&state.pool, id, "JOB_APPROVAL", "FINAL_REJECTED").await;
            (StatusCode::OK, Json(job)).into_response()
        }
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", e)).into_response(),
    }
}

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

    let existing = match RequirementRepository::get_by_id(&state.pool, id).await {
        Ok(Some(req)) => req,
        Ok(None) => return (StatusCode::NOT_FOUND, "Requirement not found").into_response(),
        Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", e)).into_response(),
    };

    if existing.status != "PENDING_APPROVAL" {
        return (StatusCode::BAD_REQUEST, "Requirement is not pending approval").into_response();
    }

    match RequirementRepository::approve(&state.pool, id, auth.user_id).await {
        Ok(req) => {
            let _ = ActivityLogRepository::create(
                &state.pool,
                auth.user_id,
                "EMPLOYEE",
                id,
                "REQUIREMENT",
                "APPROVE",
                None,
            )
            .await;

            // Send in-app notification to customer
            sqlx::query(
                r#"INSERT INTO notifications (user_id, title, body, type, reference_id)
                   VALUES ($1, $2, $3, $4, $5)"#,
            )
            .bind(req.created_by_user_id)
            .bind("Your Requirement is Now Live!")
            .bind(format!("Your requirement '{}' has been approved and is now visible to professionals.", req.title))
            .bind("REQUIREMENT")
            .bind(req.id)
            .execute(&state.pool)
            .await
            .ok();

            // Send email notification to customer
            if let Some(user_id) = req.created_by_user_id {
                if let Ok(user) = UserRepository::get_by_id(&state.pool, user_id).await {
                    let name = format!("{} {}", user.first_name.unwrap_or_default(), user.last_name.unwrap_or_default());
                    if let Err(e) = state.mail.send_requirement_approved_email(&user.email, &name, &req.title).await {
                        tracing::error!("Failed to send requirement-approved email to {}: {:?}", user.email, e);
                    }
                }
            }

            finalize_verification_case_for_entity(&state.pool, id, "REQUIREMENT_APPROVAL", "COMPLETED").await;
            (StatusCode::OK, Json(req)).into_response()
        }
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", e)).into_response(),
    }
}

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

    match RequirementRepository::reject(&state.pool, id, payload.reason.clone()).await {
        Ok(req) => {
            let _ = ActivityLogRepository::create(
                &state.pool,
                auth.user_id,
                "EMPLOYEE",
                id,
                "REQUIREMENT",
                "REJECT",
                Some(serde_json::json!({ "reason": payload.reason })),
            )
            .await;

            // Send in-app notification to customer
            let reason_str = payload.reason.as_deref().unwrap_or("Rejected by admin");
            if let Some(user_id) = req.created_by_user_id {
                sqlx::query(
                    r#"INSERT INTO notifications (user_id, title, body, type, reference_id)
                       VALUES ($1, $2, $3, $4, $5)"#,
                )
                .bind(user_id)
                .bind("Your Requirement Was Not Approved")
                .bind(format!("Your requirement '{}' was not approved. Reason: {}", req.title, reason_str))
                .bind("REQUIREMENT")
                .bind(req.id)
                .execute(&state.pool)
                .await
                .ok();
            }

            finalize_verification_case_for_entity(&state.pool, id, "REQUIREMENT_APPROVAL", "FINAL_REJECTED").await;
            (StatusCode::OK, Json(req)).into_response()
        }
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", e)).into_response(),
    }
}
