use axum::{
    extract::{Multipart, Path, Query, State},
    http::StatusCode,
    response::IntoResponse,
    routing::{get, post},
    Json, Router,
};
use bytes::BufMut;
use serde::Deserialize;
use uuid::Uuid;
use db::models::customer::{CustomerRepository, UpsertCustomerProfilePayload};
use db::models::requirement::{RequirementRepository, CreateRequirementPayload as DbCreateRequirementPayload, UpdateRequirementPayload as DbUpdateRequirementPayload};
use db::models::lead_request::LeadRequestRepository;
use db::models::user::UserRepository;
use db::models::verification::VerificationRepository;
use db::models::tracecoin_wallet::TracecoinWalletRepository;
use contracts::auth_middleware::AuthUser;
use crate::AppState;

pub fn router() -> Router<AppState> {
    Router::new()
        .route("/profile/me", get(get_profile).patch(update_profile))
        .route("/profile/submit", post(submit_for_verification))
        .route("/profile/documents", post(upload_document))
        .route("/requirements", get(list_requirements).post(create_requirement))
        .route("/requirements/{id}", get(get_requirement).patch(update_requirement))
        .route("/requirements/{id}/submit", post(submit_requirement))
        .route("/requests", get(list_requests))
        .route("/requests/{lead_id}/approve", post(approve_request))
        .route("/requests/{lead_id}/reject", post(reject_request))
}

#[derive(Deserialize)]
pub struct PaginationQuery {
    pub page: Option<i64>,
    pub limit: Option<i64>,
    pub lead_id: Option<Uuid>,
}

#[derive(Deserialize)]
pub struct CreateRequirementRequest {
    pub profession_key: String,
    pub title: String,
    pub description: String,
    pub location: String,
    pub budget: Option<i32>,
    pub preferred_date: Option<String>,
    pub extra_data_json: Option<serde_json::Value>,
}

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

async fn get_profile(
    State(state): State<AppState>,
    auth: AuthUser,
) -> impl IntoResponse {
    match CustomerRepository::get_by_user_id(&state.pool, auth.user_id).await {
        Ok(Some(profile)) => (StatusCode::OK, Json(profile)).into_response(),
        Ok(None) => (StatusCode::NOT_FOUND, "Customer profile not found").into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}

async fn update_profile(
    State(state): State<AppState>,
    auth: AuthUser,
    Json(payload): Json<UpsertCustomerProfilePayload>,
) -> impl IntoResponse {
    match CustomerRepository::upsert(&state.pool, auth.user_id, payload).await {
        Ok(profile) => (StatusCode::OK, Json(profile)).into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}

/// POST /api/customers/profile/documents — uploads a KYC document (e.g. Aadhar)
/// to B2 storage and returns its URL. The caller merges the URL into
/// profile_data and persists it via PATCH /api/profile, same as every other
/// role's document flow (see extract_documents in apps/users/src/handlers/profile.rs).
async fn upload_document(
    State(state): State<AppState>,
    _auth: AuthUser,
    mut multipart: Multipart,
) -> impl IntoResponse {
    let mut file_bytes = bytes::BytesMut::new();
    let mut content_type = "application/octet-stream".to_string();
    let mut ext = "bin".to_string();
    let mut found = false;

    while let Ok(Some(field)) = multipart.next_field().await {
        let name = field.name().unwrap_or("").to_string();
        if name == "document" || name == "file" || !found {
            if let Some(ct) = field.content_type() {
                content_type = ct.to_string();
                ext = match ct {
                    "image/jpeg" => "jpg",
                    "image/png" => "png",
                    "image/webp" => "webp",
                    "application/pdf" => "pdf",
                    _ => "bin",
                }
                .to_string();
            } else if let Some(fname) = field.file_name() {
                if let Some(e) = fname.rsplit('.').next() {
                    ext = e.to_lowercase();
                }
            }

            let data = match field.bytes().await {
                Ok(b) => b,
                Err(e) => return (StatusCode::BAD_REQUEST, Json(serde_json::json!({ "error": format!("Failed to read file: {}", e) }))).into_response(),
            };

            if data.is_empty() {
                return (StatusCode::BAD_REQUEST, Json(serde_json::json!({ "error": "Empty file" }))).into_response();
            }

            if data.len() > 10 * 1024 * 1024 {
                return (StatusCode::PAYLOAD_TOO_LARGE, Json(serde_json::json!({ "error": "File too large. Maximum 10 MB." }))).into_response();
            }

            file_bytes.put(data);
            found = true;
            break;
        }
    }

    if !found || file_bytes.is_empty() {
        return (StatusCode::BAD_REQUEST, Json(serde_json::json!({ "error": "No document file provided. Send a multipart field named 'document'." }))).into_response();
    }

    match state.storage.upload("documents", &ext, file_bytes.freeze(), &content_type).await {
        Ok(url) => (StatusCode::OK, Json(serde_json::json!({ "url": url }))).into_response(),
        Err(e) => {
            tracing::error!("B2 upload failed: {:?}", e);
            (StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ "error": "storage_upload_failed", "detail": format!("{:?}", e) }))).into_response()
        }
    }
}

async fn submit_for_verification(
    State(state): State<AppState>,
    auth: AuthUser,
) -> impl IntoResponse {
    let customer = match CustomerRepository::get_by_user_id(&state.pool, auth.user_id).await {
        Ok(Some(c)) => c,
        Ok(None) => return (StatusCode::NOT_FOUND, "Customer profile not found").into_response(),
        Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    };

    if matches!(
        customer.status.as_str(),
        "PENDING_REVIEW"
            | "PENDING"
            | "UNDER_REVIEW"
            | "DOCUMENTS_REQUESTED"
            | "REVISION_REQUESTED"
            | "APPROVED"
    ) {
        return (StatusCode::BAD_REQUEST, format!("Profile is already {}", customer.status)).into_response();
    }

    match CustomerRepository::submit_for_verification(&state.pool, auth.user_id).await {
        Ok(profile) => (StatusCode::OK, Json(serde_json::json!({
            "status": profile.status,
            "message": "Profile submitted for verification"
        }))).into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}

async fn list_requirements(
    State(state): State<AppState>,
    auth: AuthUser,
    Query(q): Query<PaginationQuery>,
) -> impl IntoResponse {
    let page = q.page.unwrap_or(1);
    let limit = q.limit.unwrap_or(20);
    match RequirementRepository::list_by_user_id(&state.pool, auth.user_id, page, limit).await {
        Ok(reqs) => (StatusCode::OK, Json(serde_json::json!({
            "data": reqs,
            "pagination": { "page": page, "limit": limit }
        }))).into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}

async fn create_requirement(
    State(state): State<AppState>,
    auth: AuthUser,
    Json(payload): Json<CreateRequirementRequest>,
) -> impl IntoResponse {
    let p_date = payload.preferred_date.and_then(|d| chrono::NaiveDate::parse_from_str(&d, "%Y-%m-%d").ok());

    let db_payload = DbCreateRequirementPayload {
        created_by_user_id: auth.user_id,
        profession_key: payload.profession_key,
        title: payload.title,
        description: payload.description,
        location: payload.location,
        budget_inr: payload.budget,
        required_date: p_date,
        extra_data_json: payload.extra_data_json,
    };

    match RequirementRepository::create(&state.pool, db_payload).await {
        Ok(req) => (StatusCode::CREATED, Json(req)).into_response(),
        Err(e) => {
            tracing::error!("create_requirement db error: {}", e);
            (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error").into_response()
        }
    }
}

async fn get_requirement(
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
    _auth: AuthUser,
) -> impl IntoResponse {
    match RequirementRepository::get_by_id(&state.pool, id).await {
        Ok(Some(req)) => (StatusCode::OK, Json(req)).into_response(),
        Ok(None) => (StatusCode::NOT_FOUND, "Requirement not found").into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}

async fn update_requirement(
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
    auth: AuthUser,
    Json(payload): Json<DbUpdateRequirementPayload>,
) -> impl IntoResponse {
    let req = match RequirementRepository::get_by_id(&state.pool, id).await {
        Ok(Some(r)) => r,
        _ => return (StatusCode::NOT_FOUND, "Requirement not found").into_response(),
    };

    if req.created_by_user_id != Some(auth.user_id) {
        return (StatusCode::FORBIDDEN, "You are not authorized to update this requirement").into_response();
    }

    match RequirementRepository::update(&state.pool, req.id, payload).await {
        Ok(updated) => (StatusCode::OK, Json(updated)).into_response(),
        Err(e) => {
            tracing::error!("update_requirement db error: {}", e);
            (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error").into_response()
        }
    }
}

async fn submit_requirement(
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
    auth: AuthUser,
) -> impl IntoResponse {
    let req = match RequirementRepository::get_by_id(&state.pool, id).await {
        Ok(Some(r)) => r,
        _ => return (StatusCode::NOT_FOUND, "Requirement not found").into_response(),
    };

    if req.status != "DRAFT" {
        return (StatusCode::BAD_REQUEST, "Requirement already submitted or closed").into_response();
    }

    match RequirementRepository::update_status(&state.pool, req.id, "PENDING_APPROVAL").await {
        Ok(updated) => {
            if let Ok(user) = UserRepository::get_by_id(&state.pool, auth.user_id).await {
                if let Err(e) = state.mail.send_requirement_submitted_email(&user.email, &format!("{} {}", user.first_name.unwrap_or_default(), user.last_name.unwrap_or_default()), &updated.title).await {
                    tracing::error!("Failed to send requirement-submitted email to {}: {:?}", user.email, e);
                }
            }

            // Create verification case so this request enters Verification Management first.
            let verification_payload = serde_json::json!({
                "entity_type": "REQUIREMENT",
                "entity_id": updated.id,
                "title": updated.title,
                "profession_key": updated.profession_key,
                "location": updated.location,
                "budget_inr": updated.budget_inr,
                "status": updated.status,
                "created_by_user_id": updated.created_by_user_id,
            });
            let _ = VerificationRepository::create(
                &state.pool,
                auth.user_id,
                "CUSTOMER",
                "REQUIREMENT_APPROVAL",
                "MEDIUM",
                verification_payload,
                serde_json::json!([]),
            )
            .await;
            (StatusCode::OK, Json(updated)).into_response()
        }
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}

async fn list_requests(
    State(state): State<AppState>,
    auth: AuthUser,
    Query(q): Query<PaginationQuery>,
) -> impl IntoResponse {
    let page = q.page.unwrap_or(1);
    let limit = q.limit.unwrap_or(20);
    let offset = (page - 1) * limit;

    let rows_result = if let Some(lead_id) = q.lead_id {
        sqlx::query_as::<_, db::models::lead_request::LeadRequest>(
            r#"
            SELECT * FROM lead_requests
            WHERE customer_user_id = $1 AND lead_id = $2
            ORDER BY requested_at DESC
            LIMIT $3 OFFSET $4
            "#
        )
        .bind(auth.user_id)
        .bind(lead_id)
        .bind(limit)
        .bind(offset)
        .fetch_all(&state.pool)
        .await
    } else {
        sqlx::query_as::<_, db::models::lead_request::LeadRequest>(
            r#"
            SELECT * FROM lead_requests
            WHERE customer_user_id = $1
            ORDER BY requested_at DESC
            LIMIT $2 OFFSET $3
            "#
        )
        .bind(auth.user_id)
        .bind(limit)
        .bind(offset)
        .fetch_all(&state.pool)
        .await
    };

    match rows_result {
        Ok(leads) => (StatusCode::OK, Json(serde_json::json!({
            "data": leads,
            "pagination": { "page": page, "limit": limit }
        }))).into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}

async fn approve_request(
    State(state): State<AppState>,
    Path(lead_id): Path<Uuid>,
    auth: AuthUser,
) -> impl IntoResponse {
    // Verify ownership — customer_user_id must match the authenticated user
    let owner_check = sqlx::query_scalar::<_, Uuid>(
        "SELECT customer_user_id FROM lead_requests WHERE id = $1"
    )
    .bind(lead_id)
    .fetch_optional(&state.pool)
    .await;

    match owner_check {
        Ok(Some(customer_user_id)) if customer_user_id != auth.user_id => {
            return (StatusCode::FORBIDDEN, "You are not authorized to approve this request").into_response();
        }
        Ok(None) => return (StatusCode::NOT_FOUND, "Lead request not found").into_response(),
        Err(e) => {
            tracing::error!("approve_request ownership check error: {}", e);
            return (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error").into_response();
        }
        Ok(Some(_)) => {} // ownership confirmed
    }

    let lead = match LeadRequestRepository::get_by_id(&state.pool, lead_id).await {
        Ok(Some(l)) => l,
        _ => return (StatusCode::NOT_FOUND, "Lead request not found").into_response(),
    };

    if lead.status != "PENDING" {
        return (StatusCode::BAD_REQUEST, "Lead already resolved").into_response();
    }

    match LeadRequestRepository::update_status(&state.pool, lead.id, "ACCEPTED").await {
        Ok(updated) => {
            match TracecoinWalletRepository::try_debit_reserved_tracecoins(
                &state.pool,
                lead.professional_user_id.unwrap(),
                lead.tracecoins_reserved,
                lead.id,
            ).await {
                Ok(true) => {}
                Ok(false) => return (StatusCode::CONFLICT, "Reserved Tracecoins unavailable").into_response(),
                Err(e) => {
                    tracing::error!("approve_request debit error: {}", e);
                    return (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error").into_response();
                }
            }

            (StatusCode::OK, Json(serde_json::json!({
                "lead_request": updated,
            }))).into_response()
        },
        Err(e) => {
            tracing::error!("approve_request update_status error: {}", e);
            (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error").into_response()
        }
    }
}

async fn reject_request(
    State(state): State<AppState>,
    Path(lead_id): Path<Uuid>,
    auth: AuthUser,
    Json(_payload): Json<RejectRequestPayload>,
) -> impl IntoResponse {
    // Verify ownership — customer_user_id must match the authenticated user
    let owner_check = sqlx::query_scalar::<_, Uuid>(
        "SELECT customer_user_id FROM lead_requests WHERE id = $1"
    )
    .bind(lead_id)
    .fetch_optional(&state.pool)
    .await;

    match owner_check {
        Ok(Some(customer_user_id)) if customer_user_id != auth.user_id => {
            return (StatusCode::FORBIDDEN, "You are not authorized to reject this request").into_response();
        }
        Ok(None) => return (StatusCode::NOT_FOUND, "Lead request not found").into_response(),
        Err(e) => {
            tracing::error!("reject_request ownership check error: {}", e);
            return (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error").into_response();
        }
        Ok(Some(_)) => {} // ownership confirmed
    }

    let lead = match LeadRequestRepository::get_by_id(&state.pool, lead_id).await {
        Ok(Some(l)) => l,
        _ => return (StatusCode::NOT_FOUND, "Lead request not found").into_response(),
    };

    if lead.status != "PENDING" {
        return (StatusCode::BAD_REQUEST, "Lead already resolved").into_response();
    }

    match LeadRequestRepository::update_status(&state.pool, lead.id, "REJECTED").await {
        Ok(updated) => {
            match TracecoinWalletRepository::try_release_reserved_tracecoins(
                &state.pool,
                lead.user_role_profile_id.unwrap(),
                lead.tracecoins_reserved,
                lead.id,
                "LEAD_REJECTED",
            ).await {
                Ok(true) => {}
                Ok(false) => return (StatusCode::CONFLICT, "Reserved Tracecoins unavailable").into_response(),
                Err(e) => {
                    tracing::error!("reject_request release error: {}", e);
                    return (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error").into_response();
                }
            }

            (StatusCode::OK, Json(updated)).into_response()
        },
        Err(e) => {
            tracing::error!("reject_request update_status error: {}", e);
            (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error").into_response()
        }
    }
}
