use std::sync::Arc;

use axum::{
    extract::State,
    routing::{get, post},
    Json, Router,
};

use crate::db;
use crate::middleware::auth::AuthUser;
use crate::models::auth::{AuthResponse, LoginRequest, RegisterRequest, UserPublic};
use crate::models::error::{ApiError, ApiResult};
use crate::services::auth as auth_service;
use crate::AppState;

pub fn router() -> Router<Arc<AppState>> {
    Router::new()
        .route("/login", post(login))
        .route("/register", post(register))
        .route("/verify-totp", post(verify_totp))
        .route("/refresh", post(refresh))
        .route("/setup-totp", post(setup_totp))
        .route("/backup-codes", post(backup_codes))
        .route("/logout", post(logout))
        .route("/me", get(me))
}

async fn login(
    State(state): State<Arc<AppState>>,
    Json(body): Json<LoginRequest>,
) -> ApiResult<Json<AuthResponse>> {
    // Look up user by email
    let user = db::users::get_user_by_email(&state.db, &body.email)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?
        .ok_or(ApiError::BadRequest("Invalid email or password".to_string()))?;

    // Verify password
    let valid = auth_service::verify_password(&body.password, &user.password_hash)
        .map_err(|e| ApiError::Internal(e.to_string()))?;

    if !valid {
        return Err(ApiError::BadRequest("Invalid email or password".to_string()));
    }

    // Check if TOTP is enabled (for now, skip TOTP challenge)
    let requires_totp = user.totp_enabled;

    // Look up license for the user
    let license = db::licenses::get_license_by_user(&state.db, user.id)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;

    let license_id = license.as_ref().map(|l| l.id);
    let role = None; // TODO: look up role from team_members

    // Create tokens
    let access_token = auth_service::create_access_token(
        &state.config,
        user.id,
        &user.email,
        license_id,
        role,
        user.is_super_admin,
    )
    .map_err(|e| ApiError::Internal(e.to_string()))?;

    let refresh_token = auth_service::create_refresh_token(
        &state.config,
        user.id,
        &user.email,
    )
    .map_err(|e| ApiError::Internal(e.to_string()))?;

    // Update last login timestamp
    let _ = db::users::update_last_login(&state.db, user.id).await;

    Ok(Json(AuthResponse {
        access_token,
        refresh_token,
        requires_totp,
        user: UserPublic {
            id: user.id,
            email: user.email,
            username: user.username,
            totp_enabled: user.totp_enabled,
            email_verified: user.email_verified,
            is_super_admin: user.is_super_admin,
        },
    }))
}

async fn register(
    State(state): State<Arc<AppState>>,
    Json(body): Json<RegisterRequest>,
) -> ApiResult<Json<AuthResponse>> {
    // Validate input
    if body.email.is_empty() || !body.email.contains('@') {
        return Err(ApiError::BadRequest("Invalid email address".to_string()));
    }
    if body.username.len() < 3 {
        return Err(ApiError::BadRequest("Username must be at least 3 characters".to_string()));
    }
    if body.password.len() < 8 {
        return Err(ApiError::BadRequest("Password must be at least 8 characters".to_string()));
    }

    // Verify the license key exists and is unclaimed
    let license = db::licenses::get_license_by_key(&state.db, &body.license_key)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?
        .ok_or(ApiError::BadRequest("Invalid license key".to_string()))?;

    if license.status != "active" {
        return Err(ApiError::BadRequest("License is not active".to_string()));
    }

    // Hash password
    let password_hash = auth_service::hash_password(&body.password)
        .map_err(|e| ApiError::Internal(e.to_string()))?;

    // Create user
    let user_id = db::users::create_user(&state.db, &body.email, &body.username, &password_hash)
        .await
        .map_err(|e| {
            if e.to_string().contains("duplicate key") || e.to_string().contains("unique") {
                ApiError::Conflict("Email or username already exists".to_string())
            } else {
                ApiError::Internal(e.to_string())
            }
        })?;

    // Create tokens (registered users are never super-admin)
    let access_token = auth_service::create_access_token(
        &state.config,
        user_id,
        &body.email,
        Some(license.id),
        Some("Owner".to_string()),
        false,
    )
    .map_err(|e| ApiError::Internal(e.to_string()))?;

    let refresh_token = auth_service::create_refresh_token(
        &state.config,
        user_id,
        &body.email,
    )
    .map_err(|e| ApiError::Internal(e.to_string()))?;

    Ok(Json(AuthResponse {
        access_token,
        refresh_token,
        requires_totp: false,
        user: UserPublic {
            id: user_id,
            email: body.email,
            username: body.username,
            totp_enabled: false,
            email_verified: false,
            is_super_admin: false,
        },
    }))
}

#[derive(serde::Deserialize)]
struct RefreshRequest {
    refresh_token: String,
}

async fn refresh(
    State(state): State<Arc<AppState>>,
    Json(body): Json<RefreshRequest>,
) -> ApiResult<Json<serde_json::Value>> {
    // Validate refresh token
    let claims = auth_service::validate_token(&state.config, &body.refresh_token)
        .map_err(|_| ApiError::Unauthorized)?;

    if claims.token_type != "refresh" {
        return Err(ApiError::Unauthorized);
    }

    // Look up user to ensure they still exist
    let user = db::users::get_user_by_id(&state.db, claims.sub)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?
        .ok_or(ApiError::Unauthorized)?;

    // Get license
    let license = db::licenses::get_license_by_user(&state.db, user.id)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;

    let license_id = license.as_ref().map(|l| l.id);

    // Issue new access token
    let access_token = auth_service::create_access_token(
        &state.config,
        user.id,
        &user.email,
        license_id,
        None,
        user.is_super_admin,
    )
    .map_err(|e| ApiError::Internal(e.to_string()))?;

    Ok(Json(serde_json::json!({
        "access_token": access_token,
    })))
}

async fn verify_totp() -> ApiResult<Json<serde_json::Value>> {
    // TODO: Implement TOTP verification
    Err(ApiError::BadRequest("TOTP not yet implemented".to_string()))
}

async fn setup_totp() -> ApiResult<Json<serde_json::Value>> {
    // TODO: Implement TOTP setup
    Err(ApiError::BadRequest("TOTP setup not yet implemented".to_string()))
}

async fn backup_codes() -> ApiResult<Json<serde_json::Value>> {
    // TODO: Implement backup code generation
    Err(ApiError::BadRequest("Backup codes not yet implemented".to_string()))
}

/// GET /api/auth/me — returns the authenticated user's profile + license
async fn me(
    auth: AuthUser,
    State(state): State<Arc<AppState>>,
) -> ApiResult<Json<serde_json::Value>> {
    let user = db::users::get_user_by_id(&state.db, auth.user_id)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?
        .ok_or(ApiError::Unauthorized)?;

    let license = if let Some(lid) = auth.license_id {
        db::licenses::get_license_by_id(&state.db, lid)
            .await
            .map_err(|e| ApiError::Internal(e.to_string()))?
    } else {
        db::licenses::get_license_by_user(&state.db, user.id)
            .await
            .map_err(|e| ApiError::Internal(e.to_string()))?
    };

    Ok(Json(serde_json::json!({
        "user": UserPublic {
            id: user.id,
            email: user.email,
            username: user.username,
            totp_enabled: user.totp_enabled,
            email_verified: user.email_verified,
            is_super_admin: user.is_super_admin,
        },
        "license": license,
    })))
}

async fn logout() -> ApiResult<Json<serde_json::Value>> {
    // Stateless JWT — logout is handled client-side by discarding tokens.
    // TODO: Implement token blacklisting if needed.
    Ok(Json(serde_json::json!({ "message": "Logged out" })))
}
