use crate::AppState;
use axum::{
    extract::{Path, Query, State},
    http::StatusCode,
    response::IntoResponse,
    routing::get,
    Json, Router,
};
use contracts::auth_middleware::AuthUser;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use sqlx::FromRow;

pub fn router() -> Router<AppState> {
    Router::new()
        .route("/", get(list_users))
        .route("/customers", get(list_customers))
        .route("/candidates", get(list_candidates))
        .route("/{id}/status", axum::routing::patch(update_user_status))
}

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

#[derive(Serialize, Deserialize, FromRow)]
pub struct AdminUserRow {
    pub id: Uuid,
    pub reference_number: String,
    pub email: String,
    pub first_name: Option<String>,
    pub last_name: Option<String>,
    pub status: String,
    pub created_at: chrono::DateTime<chrono::Utc>,
    pub roles: Vec<String>,
}

async fn list_users(
    _auth: AuthUser,
    State(state): State<AppState>,
    Query(q): Query<ListQuery>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    let search = q.q.as_deref().unwrap_or_default().to_lowercase();
    let role_filter = q.role.as_deref().unwrap_or_default().to_uppercase();

    let sql = if role_filter.is_empty() {
        // Generic list: users + their approved roles
        r#"
            SELECT
                u.id, u.reference_number, u.email, u.first_name, u.last_name, u.status, u.created_at,
                COALESCE(array_agg(r.key) FILTER (WHERE r.key IS NOT NULL), '{}') as roles
            FROM users u
            LEFT JOIN user_role_assignments ur ON ur.user_id = u.id AND ur.status = 'APPROVED'
            LEFT JOIN roles r ON r.id = ur.role_id
            WHERE ($1 = '' OR LOWER(u.first_name) LIKE '%' || $1 || '%' OR LOWER(u.last_name) LIKE '%' || $1 || '%' OR LOWER(u.email) LIKE '%' || $1 || '%')
            GROUP BY u.id
            ORDER BY u.created_at DESC
            LIMIT 100
        "#.to_string()
    } else {
        // Role-specific list: joins with the specific profile table to get THAT role's status
        let table = match role_filter.as_str() {
            "PHOTOGRAPHER"          => "photographer_profiles",
            "MAKEUP_ARTIST"         => "makeup_artist_profiles",
            "TUTOR"                 => "tutor_profiles",
            "DEVELOPER"             => "developer_profiles",
            "VIDEO_EDITOR"          => "video_editor_profiles",
            "GRAPHIC_DESIGNER"       => "graphic_designer_profiles",
            "SOCIAL_MEDIA_MANAGER"  => "social_media_manager_profiles",
            "FITNESS_TRAINER"       => "fitness_trainer_profiles",
            "CATERING_SERVICES"     => "catering_service_profiles",
            "CUSTOMER"              => "customer_profiles",
            "COMPANY"               => "company_profiles",
            "JOB_SEEKER"            => "job_seeker_profiles",
            _ => "user_role_assignments", // fallback
        };

        format!(
            r#"
            SELECT 
                u.id, u.reference_number, u.email, u.first_name, u.last_name, p.status, u.created_at,
                ARRAY['{}']::text[] as roles
            FROM users u
            JOIN {} p ON p.user_id = u.id
            WHERE ($1 = '' OR LOWER(u.first_name) LIKE '%' || $1 || '%' OR LOWER(u.last_name) LIKE '%' || $1 || '%' OR LOWER(u.email) LIKE '%' || $1 || '%')
            ORDER BY u.created_at DESC
            LIMIT 100
            "#,
            role_filter, table
        )
    };

    let rows = sqlx::query_as::<_, AdminUserRow>(&sql)
        .bind(search)
        .fetch_all(&state.pool)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;

    Ok(Json(rows))
}

async fn list_customers(
    _auth: AuthUser,
    State(state): State<AppState>,
    Query(q): Query<ListQuery>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    let search = q.q.unwrap_or_default().to_lowercase();
    
    let sql = r#"
        SELECT
            u.id, u.reference_number, u.email, u.first_name, u.last_name, u.status, u.created_at,
            ARRAY['CUSTOMER']::text[] as roles
        FROM users u
        JOIN user_role_assignments ur ON ur.user_id = u.id AND ur.status = 'APPROVED'
        JOIN roles r ON r.id = ur.role_id AND r.key = 'CUSTOMER'
        WHERE ($1 = '' OR LOWER(u.first_name) LIKE '%' || $1 || '%' OR LOWER(u.last_name) LIKE '%' || $1 || '%' OR LOWER(u.email) LIKE '%' || $1 || '%')
        ORDER BY u.created_at DESC
        LIMIT 50
    "#;

    let rows = sqlx::query_as::<_, AdminUserRow>(sql)
    .bind(search)
    .fetch_all(&state.pool)
    .await
    .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;

    Ok(Json(rows))
}

async fn list_candidates(
    _auth: AuthUser,
    State(state): State<AppState>,
    Query(q): Query<ListQuery>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    let search = q.q.unwrap_or_default().to_lowercase();
    
    let sql = r#"
        SELECT
            u.id, u.reference_number, u.email, u.first_name, u.last_name, u.status, u.created_at,
            ARRAY['JOB_SEEKER']::text[] as roles
        FROM users u
        JOIN user_role_assignments ur ON ur.user_id = u.id AND ur.status = 'APPROVED'
        JOIN roles r ON r.id = ur.role_id AND r.key = 'JOB_SEEKER'
        WHERE ($1 = '' OR LOWER(u.first_name) LIKE '%' || $1 || '%' OR LOWER(u.last_name) LIKE '%' || $1 || '%' OR LOWER(u.email) LIKE '%' || $1 || '%')
        ORDER BY u.created_at DESC
        LIMIT 50
    "#;

    let rows = sqlx::query_as::<_, AdminUserRow>(sql)
    .bind(search)
    .fetch_all(&state.pool)
    .await
    .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;

    Ok(Json(rows))
}

#[derive(Deserialize)]
pub struct StatusPayload {
    pub status: String,
}

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

    Ok(StatusCode::OK)
}
