use crate::AppState;
use axum::{
    extract::{Path, Query, State},
    http::StatusCode,
    response::IntoResponse,
    routing::{get, post, patch},
    Json, Router,
};
use contracts::auth_middleware::{AuthUser, require_admin};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use db::models::employee::{EmployeeRepository, CreateEmployeePayload};
use auth::crypto::hash_password;

pub fn router() -> Router<AppState> {
    Router::new()
        .route("/", get(list_employees).post(create_employee))
        .route("/provision", post(provision_employee))
        .route("/{id}", get(get_employee).patch(update_employee).delete(delete_employee))
        .route("/{id}/change-password", patch(change_password))
}

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

#[derive(Serialize)]
#[allow(dead_code)]
pub struct EmployeeResponse {
    pub id: Uuid,
    pub first_name: String,
    pub last_name: String,
    pub email: String,
    pub employee_code: Option<String>,
    pub role_code: String,
    pub status: String,
}

async fn list_employees(
    auth: AuthUser,
    State(state): State<AppState>,
    Query(q): Query<ListQuery>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    if let Err(_) = require_admin(&auth) {
        return Err((StatusCode::FORBIDDEN, "Insufficient permissions".to_string()));
    }
    let employees = EmployeeRepository::list(&state.pool, q.q)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;

    Ok(Json(employees))
}

async fn get_employee(
    auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    if let Err(_) = require_admin(&auth) {
        return Err((StatusCode::FORBIDDEN, "Insufficient permissions".to_string()));
    }
    let employee = EmployeeRepository::list(&state.pool, None)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?
        .into_iter()
        .find(|e| e.id == id)
        .ok_or((StatusCode::NOT_FOUND, "Employee not found".to_string()))?;

    Ok(Json(employee))
}

async fn create_employee(
    auth: AuthUser,
    State(state): State<AppState>,
    Json(payload): Json<CreateEmployeePayload>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    if let Err(_) = require_admin(&auth) {
        return Err((StatusCode::FORBIDDEN, "Insufficient permissions".to_string()));
    }
    let employee = EmployeeRepository::create(&state.pool, payload)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;

    Ok((StatusCode::CREATED, Json(employee)))
}

#[derive(Deserialize)]
pub struct ProvisionEmployeePayload {
    pub email: String,
    pub first_name: String,
    pub last_name: String,
    pub phone: Option<String>,
    pub role_code: String,
    pub department_id: Option<Uuid>,
    pub designation_id: Option<Uuid>,
    pub employee_code: Option<String>,
    pub password: String,
}

async fn provision_employee(
    auth: AuthUser,
    State(state): State<AppState>,
    Json(payload): Json<ProvisionEmployeePayload>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    if let Err(_) = require_admin(&auth) {
        return Err((StatusCode::FORBIDDEN, "Insufficient permissions".to_string()));
    }

    let password_hash = hash_password(&payload.password)
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Password hash error: {}", e)))?;

    let create_payload = CreateEmployeePayload {
        first_name: payload.first_name,
        last_name: payload.last_name,
        email: payload.email,
        phone: payload.phone,
        password_hash,
        department_id: payload.department_id,
        designation_id: payload.designation_id,
        role_code: payload.role_code,
    };

    let employee = EmployeeRepository::create_with_code(&state.pool, create_payload, payload.employee_code)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {}", e)))?;

    Ok((StatusCode::CREATED, Json(employee)))
}

#[derive(Deserialize)]
pub struct UpdateEmployeePayload {
    pub first_name: Option<String>,
    pub last_name: Option<String>,
    pub email: Option<String>,
    pub department_id: Option<Uuid>,
    pub designation_id: Option<Uuid>,
    pub role_code: Option<String>,
    pub status: Option<String>,
}

async fn update_employee(
    auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
    Json(payload): Json<UpdateEmployeePayload>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    if let Err(_) = require_admin(&auth) {
        return Err((StatusCode::FORBIDDEN, "Insufficient permissions".to_string()));
    }
    let employee = EmployeeRepository::update(
        &state.pool,
        id,
        payload.first_name,
        payload.last_name,
        payload.email,
        payload.department_id,
        payload.designation_id,
        payload.role_code,
        payload.status,
    )
    .await
    .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;

    Ok(Json(employee))
}

async fn delete_employee(
    auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    if let Err(_) = require_admin(&auth) {
        return Err((StatusCode::FORBIDDEN, "Insufficient permissions".to_string()));
    }
    EmployeeRepository::delete(&state.pool, id)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;

    Ok(StatusCode::NO_CONTENT)
}

#[derive(Deserialize)]
pub struct ChangePasswordPayload {
    pub password: String,
}

async fn change_password(
    auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
    Json(payload): Json<ChangePasswordPayload>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    if let Err(_) = require_admin(&auth) {
        return Err((StatusCode::FORBIDDEN, "Insufficient permissions".to_string()));
    }

    let password_hash = hash_password(&payload.password)
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Password hash error: {}", e)))?;

    EmployeeRepository::change_password(&state.pool, id, &password_hash)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {}", e)))?;

    Ok(Json(serde_json::json!({ "message": "Password updated successfully" })))
}
