use axum::{extract::State, http::StatusCode, response::IntoResponse, Json};
use serde::{Deserialize, Serialize};
use validator::Validate;

use crate::{
    auth::jwt::Claims, config::AppState, models::audit_log::AuditEventType, models::user::User,
};

#[derive(Debug, Deserialize, Validate)]
pub struct RegisterRequest {
    #[validate(email)]
    pub email: String,
    #[validate(length(min = 3))]
    pub username: String,
    #[validate(length(min = 8))]
    pub password: String,
    /// Optional recovery phrase for password recovery
    pub recovery_phrase: Option<String>,
}

#[derive(Debug, Serialize)]
pub struct AuthResponse {
    pub token: String,
    pub user_id: String,
    pub email: String,
    pub username: String,
}

pub async fn register(
    State(state): State<AppState>,
    Json(req): Json<RegisterRequest>,
) -> impl IntoResponse {
    if let Err(errors) = req.validate() {
        return (
            StatusCode::BAD_REQUEST,
            Json(serde_json::json!({
                "error": "validation failed",
                "details": errors.to_string()
            })),
        )
            .into_response();
    }

    // Check if user already exists
    match state.db.find_user_by_email(&req.email).await {
        Ok(Some(_)) => {
            return (
                StatusCode::CONFLICT,
                Json(serde_json::json!({
                    "error": "user already exists"
                })),
            )
                .into_response()
        }
        Ok(None) => {}
        Err(e) => {
            tracing::error!("Failed to check user existence: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({
                    "error": "database error"
                })),
            )
                .into_response();
        }
    }

    // Create new user
    let user = match User::new(
        req.email.clone(),
        req.username.clone(),
        req.password,
        req.recovery_phrase,
    ) {
        Ok(u) => u,
        Err(e) => {
            tracing::error!("Failed to create user: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({
                    "error": "failed to create user"
                })),
            )
                .into_response();
        }
    };

    // Get token_version before saving
    let token_version = user.token_version;

    // Save user to database
    let user_id = match state.db.create_user(&user).await {
        Ok(Some(id)) => {
            // Log registration (Phase 2.6)
            if let Some(ref audit) = state.audit_logger {
                let _ = audit
                    .log_event(
                        AuditEventType::LoginSuccess, // Using LoginSuccess as registration event
                        Some(id),
                        Some(req.email.clone()),
                        "0.0.0.0".to_string(),
                        None,
                        None,
                    )
                    .await;
            }
            id
        }
        Ok(None) => {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({
                    "error": "failed to create user"
                })),
            )
                .into_response()
        }
        Err(e) => {
            tracing::error!("Failed to save user: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({
                    "error": "database error"
                })),
            )
                .into_response();
        }
    };

    // Generate JWT token
    let claims = Claims::new(user_id.to_string(), user.email.clone(), token_version);
    let (token, _refresh_token) = match state.jwt_service.generate_tokens(claims) {
        Ok(t) => t,
        Err(e) => {
            tracing::error!("Failed to generate token: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({
                    "error": "failed to generate token"
                })),
            )
                .into_response();
        }
    };

    let response = AuthResponse {
        token,
        user_id: user_id.to_string(),
        email: user.email,
        username: user.username,
    };

    (StatusCode::CREATED, Json(response)).into_response()
}

#[derive(Debug, Deserialize, Validate)]
pub struct LoginRequest {
    #[validate(email)]
    pub email: String,
    #[validate(length(min = 1))]
    pub password: String,
}

pub async fn login(
    State(state): State<AppState>,
    Json(req): Json<LoginRequest>,
) -> impl IntoResponse {
    if let Err(errors) = req.validate() {
        return (
            StatusCode::BAD_REQUEST,
            Json(serde_json::json!({
                "error": "validation failed",
                "details": errors.to_string()
            })),
        )
            .into_response();
    }

    // Check account lockout status (Phase 2.6)
    if let Some(ref lockout) = state.account_lockout {
        match lockout.check_lockout(&req.email).await {
            Ok(true) => {
                return (
                    StatusCode::TOO_MANY_REQUESTS,
                    Json(serde_json::json!({
                        "error": "account is temporarily locked due to too many failed attempts",
                        "retry_after": "please try again later"
                    })),
                )
                    .into_response()
            }
            Ok(false) => {}
            Err(e) => {
                tracing::error!("Failed to check lockout status: {}", e);
            }
        }
    }

    // Find user by email
    let user = match state.db.find_user_by_email(&req.email).await {
        Ok(Some(u)) => u,
        Ok(None) => {
            // Record failed attempt (Phase 2.6)
            if let Some(ref lockout) = state.account_lockout {
                let _ = lockout.record_failed_attempt(&req.email).await;
            }

            // Log failed login (Phase 2.6)
            if let Some(ref audit) = state.audit_logger {
                let _ = audit
                    .log_event(
                        AuditEventType::LoginFailed,
                        None,
                        Some(req.email.clone()),
                        "0.0.0.0".to_string(), // TODO: Extract real IP
                        None,
                        None,
                    )
                    .await;
            }

            return (
                StatusCode::UNAUTHORIZED,
                Json(serde_json::json!({
                    "error": "invalid credentials"
                })),
            )
                .into_response();
        }
        Err(e) => {
            tracing::error!("Failed to find user: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({
                    "error": "database error"
                })),
            )
                .into_response();
        }
    };

    let user_id = user
        .id
        .ok_or_else(|| {
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({
                    "error": "invalid user state"
                })),
            )
        })
        .unwrap();

    // Verify password
    match user.verify_password(&req.password) {
        Ok(true) => {
            // Reset failed attempts on successful login (Phase 2.6)
            if let Some(ref lockout) = state.account_lockout {
                let _ = lockout.reset_attempts(&req.email).await;
            }
        }
        Ok(false) => {
            // Record failed attempt (Phase 2.6)
            if let Some(ref lockout) = state.account_lockout {
                let _ = lockout.record_failed_attempt(&req.email).await;
            }

            // Log failed login (Phase 2.6)
            if let Some(ref audit) = state.audit_logger {
                let _ = audit
                    .log_event(
                        AuditEventType::LoginFailed,
                        Some(user_id),
                        Some(req.email.clone()),
                        "0.0.0.0".to_string(), // TODO: Extract real IP
                        None,
                        None,
                    )
                    .await;
            }

            return (
                StatusCode::UNAUTHORIZED,
                Json(serde_json::json!({
                    "error": "invalid credentials"
                })),
            )
                .into_response();
        }
        Err(e) => {
            tracing::error!("Failed to verify password: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({
                    "error": "authentication error"
                })),
            )
                .into_response();
        }
    }

    // Update last active timestamp (TODO: Implement in database layer)

    // Generate JWT token
    let claims = Claims::new(user_id.to_string(), user.email.clone(), user.token_version);
    let (token, _refresh_token) = match state.jwt_service.generate_tokens(claims) {
        Ok(t) => t,
        Err(e) => {
            tracing::error!("Failed to generate token: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({
                    "error": "failed to generate token"
                })),
            )
                .into_response();
        }
    };

    // Log successful login (Phase 2.6)
    if let Some(ref audit) = state.audit_logger {
        let _ = audit
            .log_event(
                AuditEventType::LoginSuccess,
                Some(user_id),
                Some(req.email.clone()),
                "0.0.0.0".to_string(), // TODO: Extract real IP
                None,
                None,
            )
            .await;
    }

    let response = AuthResponse {
        token,
        user_id: user_id.to_string(),
        email: user.email,
        username: user.username,
    };

    (StatusCode::OK, Json(response)).into_response()
}

#[derive(Debug, Deserialize, Validate)]
pub struct RecoverPasswordRequest {
    #[validate(email)]
    pub email: String,
    #[validate(length(min = 1))]
    pub recovery_phrase: String,
    #[validate(length(min = 8))]
    pub new_password: String,
}

pub async fn recover_password(
    State(state): State<AppState>,
    Json(req): Json<RecoverPasswordRequest>,
) -> impl IntoResponse {
    if let Err(errors) = req.validate() {
        return (
            StatusCode::BAD_REQUEST,
            Json(serde_json::json!({
                "error": "validation failed",
                "details": errors.to_string()
            })),
        )
            .into_response();
    }

    // Find user by email
    let mut user = match state.db.find_user_by_email(&req.email).await {
        Ok(Some(u)) => u,
        Ok(None) => {
            return (
                StatusCode::UNAUTHORIZED,
                Json(serde_json::json!({
                    "error": "invalid credentials"
                })),
            )
                .into_response()
        }
        Err(e) => {
            tracing::error!("Failed to find user: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({
                    "error": "database error"
                })),
            )
                .into_response();
        }
    };

    // Verify recovery phrase
    match user.verify_recovery_phrase(&req.recovery_phrase) {
        Ok(true) => {}
        Ok(false) => {
            return (
                StatusCode::UNAUTHORIZED,
                Json(serde_json::json!({
                    "error": "invalid credentials"
                })),
            )
                .into_response()
        }
        Err(e) => {
            tracing::error!("Failed to verify recovery phrase: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({
                    "error": "authentication error"
                })),
            )
                .into_response();
        }
    }

    // Update password
    match user.update_password(req.new_password) {
        Ok(_) => {}
        Err(e) => {
            tracing::error!("Failed to update password: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({
                    "error": "failed to update password"
                })),
            )
                .into_response();
        }
    }

    // Save updated user
    match state.db.update_user(&user).await {
        Ok(_) => {
            // Log password recovery (Phase 2.6)
            if let Some(ref audit) = state.audit_logger {
                let user_id_for_log = user.id;
                let _ = audit
                    .log_event(
                        AuditEventType::PasswordRecovery,
                        user_id_for_log,
                        Some(req.email.clone()),
                        "0.0.0.0".to_string(),
                        None,
                        None,
                    )
                    .await;
            }

            (StatusCode::NO_CONTENT, ()).into_response()
        }
        Err(e) => {
            tracing::error!("Failed to save user: {}", e);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({
                    "error": "database error"
                })),
            )
                .into_response()
        }
    }
}
