use crate::AppState;
use db::models::company::CompanyProfile;
use db::models::job::Job;
use db::models::application::Application;
use axum::{
    extract::{Path, Query, State},
    http::StatusCode,
    response::IntoResponse,
    routing::{get, patch, post},
    Json, Router,
};
use chrono::{DateTime, Utc};
use contracts::auth_middleware::AuthUser;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

pub fn router() -> Router<AppState> {
    Router::new()
        .route("/", get(list_companies))
        .route("/{id}", get(get_company))
        .route("/{id}/approve", patch(approve_company))
        .route("/{id}/reject", patch(reject_company))
        .route("/{id}/suspend", patch(suspend_company))
        .route("/{id}/job-slots", patch(grant_job_slots))
        .route("/jobs", get(list_jobs))
        .route("/jobs/{id}/approve", post(approve_job))
        .route("/jobs/{id}/reject", post(reject_job))
        .route("/applications", get(list_applications))
}

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

#[derive(Serialize)]
pub struct AdminCompanyRow {
    pub id: Uuid,
    pub user_id: Uuid,
    pub company_name: String,
    pub registration_number: Option<String>,
    pub industry: Option<String>,
    pub status: String,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

impl From<CompanyProfile> for AdminCompanyRow {
    fn from(c: CompanyProfile) -> Self {
        Self {
            id: c.id,
            user_id: c.user_id,
            company_name: c.company_name,
            registration_number: c.registration_number,
            industry: c.industry,
            status: c.status,
            created_at: c.created_at,
            updated_at: c.updated_at,
        }
    }
}

#[derive(Serialize)]
pub struct AdminJobRow {
    pub id: Uuid,
    pub title: String,
    pub description: Option<String>,
    pub company_id: Uuid,
    pub company_name: String,
    pub location: Option<String>,
    pub job_type: Option<String>,
    pub salary_min: Option<i32>,
    pub salary_max: Option<i32>,
    pub status: String,
    pub applications_count: i64,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

impl From<Job> for AdminJobRow {
    fn from(j: Job) -> Self {
        Self {
            id: j.id,
            title: j.title,
            description: Some(j.description),
            company_id: j.company_id,
            company_name: String::new(),
            location: Some(j.location),
            job_type: Some(j.job_type),
            salary_min: j.salary_min,
            salary_max: j.salary_max,
            status: j.status,
            applications_count: 0,
            created_at: j.created_at,
            updated_at: j.updated_at,
        }
    }
}

#[derive(Serialize)]
pub struct AdminApplicationRow {
    pub id: Uuid,
    pub reference_number: String,
    pub job_id: Uuid,
    pub job_title: String,
    pub company_id: Uuid,
    pub company_name: String,
    pub applicant_id: Uuid,
    pub applicant_name: String,
    pub applicant_email: String,
    pub status: String,
    pub cover_note: Option<String>,
    pub applied_at: DateTime<Utc>,
    pub created_at: DateTime<Utc>,
}

impl From<Application> for AdminApplicationRow {
    fn from(a: Application) -> Self {
        Self {
            id: a.id,
            reference_number: a.reference_number,
            job_id: a.job_id,
            job_title: String::new(),
            company_id: Uuid::nil(),
            company_name: String::new(),
            applicant_id: a.applicant_user_id,
            applicant_name: String::new(),
            applicant_email: String::new(),
            status: a.status,
            cover_note: a.cover_note,
            applied_at: a.applied_at,
            created_at: a.updated_at,
        }
    }
}

async fn list_companies(
    _auth: AuthUser,
    State(state): State<AppState>,
    Query(_q): Query<ListQuery>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    let companies = sqlx::query_as::<_, CompanyProfile>(
        r#"
            SELECT id, user_id, company_name, registration_number, industry, website_url,
                   employee_count, business_type, gst_number, contact_name, contact_email,
                   contact_phone, address_line1, city, state, country, postal_code,
                   status, free_job_slots, purchased_job_slots, free_contact_views,
                   purchased_contact_views, created_at, updated_at
            FROM company_profiles
            ORDER BY created_at DESC
            LIMIT 100
        "#,
    )
    .fetch_all(&state.pool)
    .await
    .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;

    let list: Vec<AdminCompanyRow> = companies.into_iter().map(|c| c.into()).collect();
    Ok(Json(list))
}

async fn get_company(
    _auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    let company = sqlx::query_as::<_, CompanyProfile>(
        r#"
            SELECT id, user_id, company_name, registration_number, industry, website_url,
                   employee_count, business_type, gst_number, contact_name, contact_email,
                   contact_phone, address_line1, city, state, country, postal_code,
                   status, free_job_slots, purchased_job_slots, free_contact_views,
                   purchased_contact_views, created_at, updated_at
            FROM company_profiles WHERE id = $1
        "#,
    )
    .bind(id)
    .fetch_optional(&state.pool)
    .await
    .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;

    match company {
        Some(c) => Ok(Json(AdminCompanyRow::from(c))),
        None => Err((StatusCode::NOT_FOUND, "Company not found".to_string())),
    }
}

async fn approve_company(
    _auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    sqlx::query("UPDATE company_profiles SET status = 'APPROVED', updated_at = NOW() WHERE id = $1")
        .bind(id)
        .execute(&state.pool)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;
    Ok(Json(serde_json::json!({ "status": "APPROVED" })))
}

async fn reject_company(
    _auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    sqlx::query("UPDATE company_profiles SET status = 'REJECTED', updated_at = NOW() WHERE id = $1")
        .bind(id)
        .execute(&state.pool)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;
    Ok(Json(serde_json::json!({ "status": "REJECTED" })))
}

async fn suspend_company(
    _auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    sqlx::query("UPDATE company_profiles SET status = 'SUSPENDED', updated_at = NOW() WHERE id = $1")
        .bind(id)
        .execute(&state.pool)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;
    Ok(Json(serde_json::json!({ "status": "SUSPENDED" })))
}

#[derive(Deserialize)]
pub struct GrantJobSlotsPayload {
    pub slots: i32,
}

/// Manual top-up for `purchased_job_slots` while the self-serve purchase flow
/// (TraceCoin/PayU) doesn't exist yet. Support-only unblock path.
async fn grant_job_slots(
    _auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
    Json(payload): Json<GrantJobSlotsPayload>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    if payload.slots <= 0 {
        return Err((StatusCode::BAD_REQUEST, "slots must be a positive integer".to_string()));
    }

    let new_total: i32 = sqlx::query_scalar(
        "UPDATE company_profiles SET purchased_job_slots = purchased_job_slots + $1, updated_at = NOW() WHERE id = $2 RETURNING purchased_job_slots"
    )
    .bind(payload.slots)
    .bind(id)
    .fetch_optional(&state.pool)
    .await
    .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?
    .ok_or((StatusCode::NOT_FOUND, "Company not found".to_string()))?;

    Ok(Json(serde_json::json!({ "purchased_job_slots": new_total })))
}

async fn list_jobs(
    _auth: AuthUser,
    State(state): State<AppState>,
    Query(_q): Query<ListQuery>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    let jobs = sqlx::query_as::<_, Job>(
        r#"
            SELECT id, company_id, title, category, description, location, job_type,
                   salary_min, salary_max, experience_years, skills, status, rejection_reason,
                   expires_at, approved_at, approved_by, created_at, updated_at
            FROM jobs
            ORDER BY created_at DESC
            LIMIT 100
        "#,
    )
    .fetch_all(&state.pool)
    .await
    .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;

    let list: Vec<AdminJobRow> = jobs.into_iter().map(|j| j.into()).collect();
    Ok(Json(list))
}

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

async fn approve_job(
    auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
) -> impl IntoResponse {
    let result = sqlx::query(
        "UPDATE jobs SET status = 'LIVE', approved_at = NOW(), approved_by = $1 WHERE id = $2 AND status = 'PENDING_APPROVAL'"
    )
    .bind(auth.user_id)
    .bind(id)
    .execute(&state.pool)
    .await;

    match result {
        Ok(r) if r.rows_affected() == 0 => {
            (StatusCode::NOT_FOUND, "Job not found or not in PENDING_APPROVAL state").into_response()
        }
        Ok(_) => (StatusCode::OK, Json(serde_json::json!({ "status": "ACTIVE" }))).into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}

async fn reject_job(
    auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
    Json(payload): Json<RejectJobPayload>,
) -> impl IntoResponse {
    let result = sqlx::query(
        "UPDATE jobs SET status = 'REJECTED', rejection_reason = $1, approved_by = $2 WHERE id = $3 AND status = 'PENDING_APPROVAL'"
    )
    .bind(payload.reason.as_deref().unwrap_or(""))
    .bind(auth.user_id)
    .bind(id)
    .execute(&state.pool)
    .await;

    match result {
        Ok(r) if r.rows_affected() == 0 => {
            (StatusCode::NOT_FOUND, "Job not found or not in PENDING_APPROVAL state").into_response()
        }
        Ok(_) => (StatusCode::OK, Json(serde_json::json!({ "status": "REJECTED" }))).into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}

async fn list_applications(
    _auth: AuthUser,
    State(state): State<AppState>,
    Query(_q): Query<ListQuery>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    let applications = sqlx::query_as::<_, Application>(
        r#"
            SELECT id, job_id, applicant_user_id, cover_note, status,
                   applied_at, updated_at
            FROM job_applications
            ORDER BY applied_at DESC
            LIMIT 100
        "#,
    )
    .fetch_all(&state.pool)
    .await
    .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;

    let list: Vec<AdminApplicationRow> = applications.into_iter().map(|a| a.into()).collect();
    Ok(Json(list))
}
