use std::sync::Arc;

use axum::{
    extract::{Path, Query, State},
    response::IntoResponse,
    Extension, Json,
};
use rustelo_core_lib::auth::{
    AuthError, ChangePasswordRequest, Disable2FARequest, GenerateBackupCodesRequest,
    Login2FARequest, LoginCredentials, OAuthProvider, PasswordResetConfirm, PasswordResetRequest,
    RefreshTokenRequest, RegisterUserData, Setup2FARequest, UpdateUserData, User, Verify2FARequest,
};
use serde::{Deserialize, Serialize};
use tower_cookies::Cookies;
use uuid::Uuid;

use super::{middleware::AuthContext, oauth::OAuthCallback, service::AuthService};

#[derive(Debug, Serialize)]
pub struct ApiResponse<T> {
    pub success: bool,
    pub data: Option<T>,
    pub message: Option<String>,
    pub errors: Option<Vec<String>>,
}

impl<T> ApiResponse<T> {
    pub fn success(data: T) -> Self {
        Self {
            success: true,
            data: Some(data),
            message: None,
            errors: None,
        }
    }

    pub fn success_with_message(data: T, message: String) -> Self {
        Self {
            success: true,
            data: Some(data),
            message: Some(message),
            errors: None,
        }
    }

    #[allow(dead_code)]
    pub fn error(message: String) -> ApiResponse<()> {
        ApiResponse {
            success: false,
            data: None,
            message: Some(message),
            errors: None,
        }
    }

    #[allow(dead_code)]
    pub fn validation_error(errors: Vec<String>) -> ApiResponse<()> {
        ApiResponse {
            success: false,
            data: None,
            message: Some("Validation failed".to_string()),
            errors: Some(errors),
        }
    }
}

/// Register a new user
pub async fn register(
    State(auth_service): State<Arc<AuthService>>,
    Json(data): Json<RegisterUserData>,
) -> impl IntoResponse {
    match auth_service.register_user(data).await {
        Ok(response) => Json(ApiResponse::success_with_message(
            response,
            "User registered successfully".to_string(),
        ))
        .into_response(),
        Err(err) => {
            use crate::auth::middleware::auth_error_response;
            auth_error_response(err)
        }
    }
}

/// Login with email and password (first step)
pub async fn login(
    State(auth_service): State<Arc<AuthService>>,
    cookies: Cookies,
    Json(credentials): Json<LoginCredentials>,
) -> impl IntoResponse {
    match auth_service.login(credentials, Some(cookies)).await {
        Ok(response) => {
            if response.requires_2fa {
                Json(ApiResponse::success_with_message(
                    response,
                    "2FA verification required".to_string(),
                ))
                .into_response()
            } else {
                Json(ApiResponse::success_with_message(
                    response,
                    "Login successful".to_string(),
                ))
                .into_response()
            }
        }
        Err(err) => {
            use crate::auth::middleware::auth_error_response;
            auth_error_response(err)
        }
    }
}

/// Logout current user
pub async fn logout(
    State(auth_service): State<Arc<AuthService>>,
    cookies: Cookies,
    Extension(auth_context): Extension<AuthContext>,
) -> impl IntoResponse {
    if let Some(user) = &auth_context.user {
        match auth_service.logout(user.id, Some(cookies)).await {
            Ok(_) => Json(ApiResponse::<()>::success_with_message(
                (),
                "Logout successful".to_string(),
            ))
            .into_response(),
            Err(err) => {
                use crate::auth::middleware::auth_error_response;
                auth_error_response(err)
            }
        }
    } else {
        use crate::auth::middleware::auth_error_response;
        auth_error_response(AuthError::InvalidToken)
    }
}

/// Refresh access token
pub async fn refresh_token(
    State(auth_service): State<Arc<AuthService>>,
    Json(request): Json<RefreshTokenRequest>,
) -> impl IntoResponse {
    match auth_service.refresh_token(request).await {
        Ok(response) => Json(ApiResponse::success(response)).into_response(),
        Err(err) => {
            use crate::auth::middleware::auth_error_response;
            auth_error_response(err)
        }
    }
}

/// Get current user profile
pub async fn get_profile(Extension(auth_context): Extension<AuthContext>) -> impl IntoResponse {
    if let Some(user) = &auth_context.user {
        Json(ApiResponse::success(user.clone())).into_response()
    } else {
        use crate::auth::middleware::auth_error_response;
        auth_error_response(AuthError::InvalidToken)
    }
}

/// Update user profile
pub async fn update_profile(
    State(auth_service): State<Arc<AuthService>>,
    Extension(auth_context): Extension<AuthContext>,
    Json(data): Json<UpdateUserData>,
) -> impl IntoResponse {
    if let Some(user) = &auth_context.user {
        match auth_service.update_user_profile(user.id, data).await {
            Ok(updated_user) => Json(ApiResponse::success_with_message(
                updated_user,
                "Profile updated successfully".to_string(),
            ))
            .into_response(),
            Err(err) => {
                use crate::auth::middleware::auth_error_response;
                auth_error_response(err)
            }
        }
    } else {
        use crate::auth::middleware::auth_error_response;
        auth_error_response(AuthError::InvalidToken)
    }
}

/// Change password
pub async fn change_password(
    State(auth_service): State<Arc<AuthService>>,
    Extension(auth_context): Extension<AuthContext>,
    Json(req): Json<ChangePasswordRequest>,
) -> impl IntoResponse {
    if let Some(user) = &auth_context.user {
        match auth_service.change_password(user.id, req).await {
            Ok(_) => Json(ApiResponse::<()>::success_with_message(
                (),
                "Password changed successfully".to_string(),
            ))
            .into_response(),
            Err(err) => {
                use crate::auth::middleware::auth_error_response;
                auth_error_response(err)
            }
        }
    } else {
        use crate::auth::middleware::auth_error_response;
        auth_error_response(AuthError::InvalidToken)
    }
}

/// Request password reset
pub async fn request_password_reset(
    State(auth_service): State<Arc<AuthService>>,
    Json(request): Json<PasswordResetRequest>,
) -> impl IntoResponse {
    match auth_service.request_password_reset(request).await {
        Ok(_) => Json(ApiResponse::<()>::success_with_message(
            (),
            "Password reset email sent".to_string(),
        ))
        .into_response(),
        Err(err) => {
            use crate::auth::middleware::auth_error_response;
            auth_error_response(err)
        }
    }
}

/// Confirm password reset
pub async fn confirm_password_reset(
    State(auth_service): State<Arc<AuthService>>,
    Json(request): Json<PasswordResetConfirm>,
) -> impl IntoResponse {
    match auth_service.confirm_password_reset(request).await {
        Ok(_) => Json(ApiResponse::<()>::success_with_message(
            (),
            "Password reset successfully".to_string(),
        ))
        .into_response(),
        Err(err) => {
            use crate::auth::middleware::auth_error_response;
            auth_error_response(err)
        }
    }
}

/// Get OAuth authorization URL
pub async fn oauth_authorize(
    State(auth_service): State<Arc<AuthService>>,
    Path(provider): Path<String>,
) -> impl IntoResponse {
    let oauth_provider = match provider.as_str() {
        "google" => OAuthProvider::Google,
        "github" => OAuthProvider::GitHub,
        "discord" => OAuthProvider::Discord,
        "microsoft" => OAuthProvider::Microsoft,
        _ => {
            use crate::auth::middleware::auth_error_response;
            return auth_error_response(AuthError::ValidationError(
                "Invalid OAuth provider".to_string(),
            ));
        }
    };

    match auth_service
        .get_oauth_authorization_url(&oauth_provider)
        .await
    {
        Ok(auth_url) => Json(ApiResponse::success(auth_url)).into_response(),
        Err(err) => {
            use crate::auth::middleware::auth_error_response;
            auth_error_response(err)
        }
    }
}

/// Handle OAuth callback
#[derive(Debug, Deserialize)]
pub struct OAuthCallbackQuery {
    pub code: String,
    pub state: String,
    pub error: Option<String>,
    pub error_description: Option<String>,
}

pub async fn oauth_callback(
    State(auth_service): State<Arc<AuthService>>,
    Path(provider): Path<String>,
    Query(query): Query<OAuthCallbackQuery>,
) -> impl IntoResponse {
    // Check for OAuth errors
    if let Some(error) = query.error {
        let description = query
            .error_description
            .unwrap_or("Unknown error".to_string());
        use crate::auth::middleware::auth_error_response;
        return auth_error_response(AuthError::OAuthError(format!("{}: {}", error, description)));
    }

    let oauth_provider = match provider.as_str() {
        "google" => OAuthProvider::Google,
        "github" => OAuthProvider::GitHub,
        "discord" => OAuthProvider::Discord,
        "microsoft" => OAuthProvider::Microsoft,
        _ => {
            use crate::auth::middleware::auth_error_response;
            return auth_error_response(AuthError::ValidationError(
                "Invalid OAuth provider".to_string(),
            ));
        }
    };

    let callback = OAuthCallback {
        code: query.code,
        state: query.state,
    };

    // TODO: Retrieve PKCE verifier from session storage
    // For now, we'll pass None
    let pkce_verifier = None;

    match auth_service
        .handle_oauth_callback(&oauth_provider, callback, pkce_verifier)
        .await
    {
        Ok(response) => Json(ApiResponse::success_with_message(
            response,
            "OAuth login successful".to_string(),
        ))
        .into_response(),
        Err(err) => {
            use crate::auth::middleware::auth_error_response;
            auth_error_response(err)
        }
    }
}

/// Get available OAuth providers
pub async fn oauth_providers(State(auth_service): State<Arc<AuthService>>) -> impl IntoResponse {
    let providers = auth_service.oauth_service.get_configured_providers();
    Json(ApiResponse::success(providers)).into_response()
}

/// Verify email (admin only)
pub async fn verify_email(
    State(auth_service): State<Arc<AuthService>>,
    Path(user_id): Path<Uuid>,
) -> impl IntoResponse {
    match auth_service.verify_email(user_id).await {
        Ok(_) => Json(ApiResponse::<()>::success_with_message(
            (),
            "Email verified successfully".to_string(),
        ))
        .into_response(),
        Err(err) => {
            use crate::auth::middleware::auth_error_response;
            auth_error_response(err)
        }
    }
}

/// Get user by ID (admin only)
pub async fn get_user(
    State(auth_service): State<Arc<AuthService>>,
    Path(user_id): Path<Uuid>,
) -> impl IntoResponse {
    match auth_service.get_user(user_id).await {
        Ok(user) => Json(ApiResponse::success(user)).into_response(),
        Err(err) => {
            use crate::auth::middleware::auth_error_response;
            auth_error_response(err)
        }
    }
}

/// Health check endpoint
pub async fn health() -> impl IntoResponse {
    Json(ApiResponse::success_with_message(
        "healthy".to_string(),
        "Authentication service is running".to_string(),
    ))
}

/// Get authentication status
pub async fn auth_status(Extension(auth_context): Extension<AuthContext>) -> impl IntoResponse {
    #[derive(Serialize)]
    struct AuthStatus {
        authenticated: bool,
        user: Option<User>,
    }

    let status = AuthStatus {
        authenticated: auth_context.is_authenticated(),
        user: auth_context.user.clone(),
    };

    Json(ApiResponse::success(status))
}

/// Admin endpoint to cleanup expired tokens and sessions
pub async fn cleanup_expired(State(auth_service): State<Arc<AuthService>>) -> impl IntoResponse {
    match auth_service.cleanup_expired().await {
        Ok(_) => Json(ApiResponse::<()>::success_with_message(
            (),
            "Expired tokens and sessions cleaned up".to_string(),
        ))
        .into_response(),
        Err(err) => {
            use crate::auth::middleware::auth_error_response;
            auth_error_response(err)
        }
    }
}

/// Login with 2FA code (second step)
pub async fn login_2fa(
    State(auth_service): State<Arc<AuthService>>,
    cookies: Cookies,
    Json(request): Json<Login2FARequest>,
) -> impl IntoResponse {
    // TODO: Extract IP address and User-Agent from request headers
    let ip_address = None;
    let user_agent = None;

    match auth_service
        .login_with_2fa(request, Some(cookies), ip_address, user_agent)
        .await
    {
        Ok(response) => Json(ApiResponse::success_with_message(
            response,
            "Login successful".to_string(),
        ))
        .into_response(),
        Err(err) => {
            use crate::auth::middleware::auth_error_response;
            auth_error_response(err)
        }
    }
}

/// Setup 2FA for current user
pub async fn setup_2fa(
    State(auth_service): State<Arc<AuthService>>,
    Extension(auth_context): Extension<AuthContext>,
    Json(request): Json<Setup2FARequest>,
) -> impl IntoResponse {
    if let Some(user) = &auth_context.user {
        match auth_service.setup_2fa(user.id, request).await {
            Ok(response) => Json(ApiResponse::success_with_message(
                response,
                "2FA setup initiated. Please verify with your authenticator app.".to_string(),
            ))
            .into_response(),
            Err(err) => {
                use crate::auth::middleware::auth_error_response;
                auth_error_response(err)
            }
        }
    } else {
        use crate::auth::middleware::auth_error_response;
        auth_error_response(AuthError::InvalidToken)
    }
}

/// Verify 2FA setup
pub async fn verify_2fa_setup(
    State(auth_service): State<Arc<AuthService>>,
    Extension(auth_context): Extension<AuthContext>,
    Json(request): Json<Verify2FARequest>,
) -> impl IntoResponse {
    if let Some(user) = &auth_context.user {
        // TODO: Extract IP address and User-Agent from request headers
        let ip_address = None;
        let user_agent = None;

        match auth_service
            .verify_2fa_setup(user.id, request, ip_address, user_agent)
            .await
        {
            Ok(_) => Json(ApiResponse::<()>::success_with_message(
                (),
                "2FA enabled successfully".to_string(),
            ))
            .into_response(),
            Err(err) => {
                use crate::auth::middleware::auth_error_response;
                auth_error_response(err)
            }
        }
    } else {
        use crate::auth::middleware::auth_error_response;
        auth_error_response(AuthError::InvalidToken)
    }
}

/// Get 2FA status
pub async fn get_2fa_status(
    State(auth_service): State<Arc<AuthService>>,
    Extension(auth_context): Extension<AuthContext>,
) -> impl IntoResponse {
    if let Some(user) = &auth_context.user {
        match auth_service.get_2fa_status(user.id).await {
            Ok(status) => Json(ApiResponse::success(status)).into_response(),
            Err(err) => {
                use crate::auth::middleware::auth_error_response;
                auth_error_response(err)
            }
        }
    } else {
        use crate::auth::middleware::auth_error_response;
        auth_error_response(AuthError::InvalidToken)
    }
}

/// Disable 2FA
pub async fn disable_2fa(
    State(auth_service): State<Arc<AuthService>>,
    Extension(auth_context): Extension<AuthContext>,
    Json(request): Json<Disable2FARequest>,
) -> impl IntoResponse {
    if let Some(user) = &auth_context.user {
        // TODO: Extract IP address and User-Agent from request headers
        let ip_address = None;
        let user_agent = None;

        match auth_service
            .disable_2fa(user.id, request, ip_address, user_agent)
            .await
        {
            Ok(_) => Json(ApiResponse::<()>::success_with_message(
                (),
                "2FA disabled successfully".to_string(),
            ))
            .into_response(),
            Err(err) => {
                use crate::auth::middleware::auth_error_response;
                auth_error_response(err)
            }
        }
    } else {
        use crate::auth::middleware::auth_error_response;
        auth_error_response(AuthError::InvalidToken)
    }
}

/// Generate new backup codes
pub async fn generate_backup_codes(
    State(auth_service): State<Arc<AuthService>>,
    Extension(auth_context): Extension<AuthContext>,
    Json(request): Json<GenerateBackupCodesRequest>,
) -> impl IntoResponse {
    if let Some(user) = &auth_context.user {
        // TODO: Extract IP address and User-Agent from request headers
        let ip_address = None;
        let user_agent = None;

        match auth_service
            .generate_backup_codes(user.id, request, ip_address, user_agent)
            .await
        {
            Ok(response) => Json(ApiResponse::success_with_message(
                response,
                "New backup codes generated successfully".to_string(),
            ))
            .into_response(),
            Err(err) => {
                use crate::auth::middleware::auth_error_response;
                auth_error_response(err)
            }
        }
    } else {
        use crate::auth::middleware::auth_error_response;
        auth_error_response(AuthError::InvalidToken)
    }
}

// ─── OTP / Passwordless
// ───────────────────────────────────────────────────────

#[derive(serde::Deserialize)]
pub struct OtpRequestBody {
    pub email: String,
}

#[derive(serde::Deserialize)]
pub struct OtpVerifyBody {
    pub email: String,
    pub code: String,
}

pub async fn request_otp(
    State(auth_service): State<Arc<AuthService>>,
    headers: axum::http::HeaderMap,
    Json(body): Json<OtpRequestBody>,
) -> impl IntoResponse {
    let otp = match &auth_service.otp_service {
        Some(svc) => svc,
        None => {
            return Json(ApiResponse::<()>::error(
                "OTP authentication is not configured".to_string(),
            ))
            .into_response();
        }
    };
    // Derive language from Accept-Language header (first tag, language subtag
    // only).
    let lang_owned = headers
        .get(axum::http::header::ACCEPT_LANGUAGE)
        .and_then(|v| v.to_str().ok())
        .and_then(|v| v.split(',').next())
        .and_then(|tag| tag.split(';').next())
        .and_then(|tag| tag.split('-').next())
        .map(str::trim)
        .filter(|s| !s.is_empty())
        .map(|s| s.to_ascii_lowercase())
        .unwrap_or_else(|| "en".to_string());
    let lang = lang_owned.as_str();
    let cfg = &auth_service.otp_config;
    match otp
        .request_otp(
            &body.email,
            cfg.otp_code_length,
            cfg.otp_expiry_minutes,
            lang,
        )
        .await
    {
        Ok(()) => Json(ApiResponse::success_with_message(
            (),
            "Verification code sent".to_string(),
        ))
        .into_response(),
        Err(err) => {
            use crate::auth::middleware::auth_error_response;
            auth_error_response(err)
        }
    }
}

pub async fn verify_otp(
    State(auth_service): State<Arc<AuthService>>,
    Json(body): Json<OtpVerifyBody>,
) -> impl IntoResponse {
    let otp = match &auth_service.otp_service {
        Some(svc) => svc,
        None => {
            return Json(ApiResponse::<()>::error(
                "OTP authentication is not configured".to_string(),
            ))
            .into_response();
        }
    };
    use crate::auth::otp::OtpVerifyOutcome;
    let max_attempts = auth_service.otp_config.otp_max_attempts;
    match otp.verify_otp(&body.email, &body.code, max_attempts).await {
        Ok(OtpVerifyOutcome::Authenticated(auth_response)) => Json(
            ApiResponse::success_with_message(auth_response, "Authenticated".to_string()),
        )
        .into_response(),
        Ok(OtpVerifyOutcome::NeedsConsent { pending_token }) => {
            #[derive(serde::Serialize)]
            struct NeedsConsentPayload {
                needs_consent: bool,
                pending_token: String,
            }
            Json(ApiResponse::success_with_message(
                NeedsConsentPayload {
                    needs_consent: true,
                    pending_token,
                },
                "GDPR consent required".to_string(),
            ))
            .into_response()
        }
        Err(err) => {
            use crate::auth::middleware::auth_error_response;
            auth_error_response(err)
        }
    }
}

/// Create authentication routes
pub fn create_auth_routes() -> axum::Router<Arc<AuthService>> {
    use axum::routing::{get, post, put};

    axum::Router::new()
        // Public routes
        .route("/health", get(health))
        .route("/register", post(register))
        .route("/login", post(login))
        .route("/login/2fa", post(login_2fa))
        .route("/refresh", post(refresh_token))
        .route("/password-reset/request", post(request_password_reset))
        .route("/password-reset/confirm", post(confirm_password_reset))
        .route("/oauth/providers", get(oauth_providers))
        .route("/oauth/{provider}/authorize", get(oauth_authorize))
        .route("/oauth/{provider}/callback", get(oauth_callback))
        // OTP / Passwordless routes
        .route("/otp/request", post(request_otp))
        .route("/otp/verify", post(verify_otp))
        // Protected routes (require authentication)
        .route("/status", get(auth_status))
        .route("/profile", get(get_profile))
        .route("/profile", put(update_profile))
        .route("/logout", post(logout))
        .route("/change-password", post(change_password))
        // 2FA routes (require authentication)
        .route("/2fa/setup", post(setup_2fa))
        .route("/2fa/verify", post(verify_2fa_setup))
        .route("/2fa/status", get(get_2fa_status))
        .route("/2fa/disable", post(disable_2fa))
        .route("/2fa/backup-codes", post(generate_backup_codes))
        // Admin routes (require admin role)
        .route("/admin/users/{user_id}", get(get_user))
        .route("/admin/users/{user_id}/verify-email", post(verify_email))
        .route("/admin/cleanup", post(cleanup_expired))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_api_response_success() {
        let response = ApiResponse::success("test data");
        assert!(response.success);
        assert_eq!(response.data, Some("test data"));
        assert!(response.message.is_none());
        assert!(response.errors.is_none());
    }

    #[test]
    fn test_api_response_error() {
        let response = ApiResponse::<()>::error("Test error".to_string());
        assert!(!response.success);
        assert!(response.data.is_none());
        assert_eq!(response.message, Some("Test error".to_string()));
        assert!(response.errors.is_none());
    }

    #[test]
    fn test_api_response_validation_error() {
        let errors = vec!["Error 1".to_string(), "Error 2".to_string()];
        let response = ApiResponse::<()>::validation_error(errors.clone());
        assert!(!response.success);
        assert!(response.data.is_none());
        assert_eq!(response.message, Some("Validation failed".to_string()));
        assert_eq!(response.errors, Some(errors));
    }
}
