mod compat;

use athena_auth::AthenaAuth;
use athena_auth::adapters::{
    AccountOps, MemoryDatabaseAdapter, SessionOps, UserOps, VerificationOps,
};
use chrono::{DateTime, Utc};
use compat::helpers::*;
use std::sync::Arc;

/// Helper to create test AthenaAuth instance with memory database
async fn create_test_auth_memory() -> Arc<AthenaAuth<MemoryDatabaseAdapter>> {
    TestHarness::minimal().await.into_arc()
}

/// Helper to create user and get session token
async fn create_test_user_and_session(
    auth: Arc<AthenaAuth<MemoryDatabaseAdapter>>,
) -> (String, String) {
    let req = post_json(
        "/sign-up/email",
        serde_json::json!({
            "email": "integration@test.com",
            "password": "password123",
            "name": "Integration Test User"
        }),
    );
    let (status, json) = send_request(&auth, req).await;
    assert_eq!(status, 200);

    let user_id = json["user"]["id"].as_str().unwrap().to_string();
    let session_token = json["token"].as_str().unwrap().to_string();
    (user_id, session_token)
}

/// Integration test for get-session endpoint
#[tokio::test]
async fn test_get_session_integration() {
    let auth = create_test_auth_memory().await;
    let (_user_id, session_token) = create_test_user_and_session(auth.clone()).await;

    let (status, response_data) =
        send_request(&auth, get_with_auth("/get-session", &session_token)).await;
    assert_eq!(status, 200);

    assert!(response_data["session"]["token"].is_string());
    assert!(response_data["user"]["id"].is_string());
    assert_eq!(response_data["user"]["email"], "integration@test.com");
}

#[tokio::test]
async fn test_health_endpoint_via_direct_dispatch_matches_axum_contract() {
    let auth = create_server_binary_equivalent_memory_auth().await;

    let (status, response_data) = send_request(&auth, get_request("/health")).await;
    assert_eq!(status, 200);
    assert_eq!(response_data["status"], "ok");
    assert_eq!(response_data["service"], "athena-auth");
}

#[tokio::test]
async fn test_update_user_refreshes_get_session_user_cache() {
    let auth = create_test_auth_memory().await;
    let (_user_id, session_token) = create_test_user_and_session(auth.clone()).await;

    let (warm_status, warm_body) =
        send_request(&auth, get_with_auth("/get-session", &session_token)).await;
    assert_eq!(warm_status, 200);
    assert!(warm_body["user"]["image"].is_null());

    let (update_status, update_body) = send_request(
        &auth,
        post_json_with_auth(
            "/update-user",
            serde_json::json!({
                "image": "http://localhost:3000/avatar-updated.png"
            }),
            &session_token,
        ),
    )
    .await;
    assert_eq!(update_status, 200);
    assert_eq!(update_body["status"], true);

    let (status, response_data) =
        send_request(&auth, get_with_auth("/get-session", &session_token)).await;
    assert_eq!(status, 200);
    assert_eq!(
        response_data["user"]["image"],
        "http://localhost:3000/avatar-updated.png"
    );
}

#[tokio::test]
async fn test_signin_refreshes_cached_get_session_user_projection() {
    let auth = create_test_auth_memory().await;
    let (_user_id, initial_token) = create_test_user_and_session(auth.clone()).await;

    let (warm_status, warm_body) =
        send_request(&auth, get_with_auth("/get-session", &initial_token)).await;
    assert_eq!(warm_status, 200);
    let warmed_last_sign_in_at = parse_test_rfc3339(
        warm_body["user"]["lastSignInAt"]
            .as_str()
            .expect("warm get-session response missing user.lastSignInAt"),
    );

    tokio::time::sleep(std::time::Duration::from_millis(10)).await;

    let signin_request = post_json(
        "/sign-in/email",
        serde_json::json!({
            "email": "integration@test.com",
            "password": "password123"
        }),
    );
    let (signin_status, signin_body) = send_request(&auth, signin_request).await;
    assert_eq!(signin_status, 200);
    let new_token = signin_body["token"]
        .as_str()
        .expect("sign-in response missing token")
        .to_string();

    let (status, response_data) =
        send_request(&auth, get_with_auth("/get-session", &new_token)).await;
    assert_eq!(status, 200);

    let refreshed_last_sign_in_at = parse_test_rfc3339(
        response_data["user"]["lastSignInAt"]
            .as_str()
            .expect("refreshed get-session response missing user.lastSignInAt"),
    );

    assert!(
        refreshed_last_sign_in_at > warmed_last_sign_in_at,
        "expected cached get-session user projection to refresh lastSignInAt after sign-in"
    );
}

#[tokio::test]
async fn test_signin_response_reflects_direct_db_role_updates() {
    let auth = create_test_auth_memory().await;
    let (user_id, _session_token) = create_test_user_and_session(auth.clone()).await;

    auth.database()
        .update_user(
            &user_id,
            athena_auth::types::UpdateUser {
                role: Some("admin".to_string()),
                ..Default::default()
            },
        )
        .await
        .unwrap();

    let signin_request = post_json(
        "/sign-in/email",
        serde_json::json!({
            "email": "integration@test.com",
            "password": "password123"
        }),
    );
    let (signin_status, signin_body) = send_request(&auth, signin_request).await;
    assert_eq!(signin_status, 200);
    assert_eq!(
        signin_body["user"]["role"], "admin",
        "sign-in should return the user's current role from the database"
    );
}

/// Integration test for sign-out endpoint
#[tokio::test]
async fn test_sign_out_integration() {
    let auth = create_test_auth_memory().await;
    let (_user_id, session_token) = create_test_user_and_session(auth.clone()).await;

    let (status, response_data) =
        send_request(&auth, post_with_auth("/sign-out", &session_token)).await;
    assert_eq!(status, 200);
    assert_eq!(response_data["success"], true);

    // Verify session is no longer valid
    let (status2, _) = send_request(&auth, get_with_auth("/get-session", &session_token)).await;
    assert_eq!(status2, 401); // Session should be invalidated
}

fn parse_test_rfc3339(value: &str) -> DateTime<Utc> {
    DateTime::parse_from_rfc3339(value)
        .unwrap_or_else(|error| panic!("invalid RFC3339 timestamp '{value}': {error}"))
        .with_timezone(&Utc)
}

/// Integration test for list-sessions endpoint
#[tokio::test]
async fn test_list_sessions_integration() {
    let auth = create_test_auth_memory().await;
    let (_user_id, session_token) = create_test_user_and_session(auth.clone()).await;

    let (status, response_data) =
        send_request(&auth, get_with_auth("/list-sessions", &session_token)).await;
    assert_eq!(status, 200);

    let sessions = response_data.as_array().expect("expected array response");
    assert_eq!(sessions.len(), 1);
    assert!(sessions[0]["token"].is_string());
}

/// Integration test for revoke-session endpoint
#[tokio::test]
async fn test_revoke_session_integration() {
    let auth = create_test_auth_memory().await;
    let (user_id, session_token1) = create_test_user_and_session(auth.clone()).await;

    // Create a second session for the same user
    use athena_auth::types::CreateSession;
    use chrono::{Duration, Utc};

    let create_session = CreateSession {
        user_id: user_id.clone(),
        expires_at: Utc::now() + Duration::hours(24),
        ip_address: Some("192.168.1.1".to_string()),
        user_agent: Some("test-agent-2".to_string()),
        impersonated_by: None,
        active_organization_id: None,
    };

    let session2 = auth
        .database()
        .create_session(create_session)
        .await
        .unwrap();

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert(
        "authorization".to_string(),
        format!("Bearer {}", session_token1),
    );

    let revoke_data = serde_json::json!({
        "token": session2.token
    });

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/revoke-session".to_string(),
        headers,
        Some(revoke_data.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let response_data: serde_json::Value = serde_json::from_str(&body_str).unwrap();

    assert_eq!(response_data["status"], true);
}

/// Integration test for revoke-sessions endpoint
#[tokio::test]
async fn test_revoke_sessions_integration() {
    let auth = create_test_auth_memory().await;
    let (_user_id, session_token) = create_test_user_and_session(auth.clone()).await;

    let (status, response_data) =
        send_request(&auth, post_with_auth("/revoke-sessions", &session_token)).await;
    assert_eq!(status, 200);
    assert_eq!(response_data["status"], true);
}

/// Integration test for unauthorized access
#[tokio::test]
async fn test_unauthorized_session_access() {
    let auth = create_test_auth_memory().await;

    // Try to access get-session without token
    let (status, _) = send_request(&auth, get_request("/get-session")).await;
    assert_eq!(status, 401);
}

/// Integration test for forget-password endpoint
#[tokio::test]
async fn test_forget_password_integration() {
    let auth = create_test_auth_memory().await;
    let (_user_id, _session_token) = create_test_user_and_session(auth.clone()).await;

    let (status, response_data) = send_request(
        &auth,
        post_json(
            "/forget-password",
            serde_json::json!({
                "email": "integration@test.com",
                "redirectTo": "http://localhost:3000/reset"
            }),
        ),
    )
    .await;
    assert_eq!(status, 200);
    assert_eq!(response_data["status"], true);
}

/// Integration test for reset-password endpoint
#[tokio::test]
async fn test_reset_password_integration() {
    let auth = create_test_auth_memory().await;
    let (_user_id, _session_token) = create_test_user_and_session(auth.clone()).await;

    // First, create a verification token manually

    use athena_auth::types::CreateVerification;
    use chrono::{Duration, Utc};
    use uuid::Uuid;

    let reset_token = format!("reset_{}", Uuid::new_v4());
    let create_verification = CreateVerification {
        identifier: "integration@test.com".to_string(),
        value: reset_token.clone(),
        expires_at: Utc::now() + Duration::hours(24),
    };
    auth.database()
        .create_verification(create_verification)
        .await
        .unwrap();

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());

    let reset_data = serde_json::json!({
        "newPassword": "NewPassword123!",
        "token": reset_token
    });

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/reset-password".to_string(),
        headers,
        Some(reset_data.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let response_data: serde_json::Value = serde_json::from_str(&body_str).unwrap();

    assert_eq!(response_data["status"], true);
}

/// Integration test for change-password endpoint
#[tokio::test]
async fn test_change_password_integration() {
    let auth = create_test_auth_memory().await;
    let (_user_id, session_token) = create_test_user_and_session(auth.clone()).await;

    let (status, response_data) = send_request(
        &auth,
        post_json_with_auth(
            "/change-password",
            serde_json::json!({
                "currentPassword": "password123",
                "newPassword": "NewPassword123!",
                "revokeOtherSessions": "false"
            }),
            &session_token,
        ),
    )
    .await;
    assert_eq!(status, 200);

    assert!(response_data["user"]["id"].is_string());
    assert!(response_data["token"].is_null()); // No new token when not revoking sessions
}

/// Integration test for change-password with session revocation
#[tokio::test]
async fn test_change_password_with_revocation_integration() {
    let auth = create_test_auth_memory().await;
    let (_user_id, session_token) = create_test_user_and_session(auth.clone()).await;

    let (status, response_data) = send_request(
        &auth,
        post_json_with_auth(
            "/change-password",
            serde_json::json!({
                "currentPassword": "password123",
                "newPassword": "NewPassword123!",
                "revokeOtherSessions": "true"
            }),
            &session_token,
        ),
    )
    .await;
    assert_eq!(status, 200);

    assert!(response_data["user"]["id"].is_string());
    assert!(response_data["token"].is_string()); // New token when revoking sessions
}

/// Integration test for reset-password token endpoint
#[tokio::test]
async fn test_reset_password_token_integration() {
    let auth = create_test_auth_memory().await;
    let (_user_id, _session_token) = create_test_user_and_session(auth.clone()).await;

    // Create a verification token manually

    use athena_auth::types::CreateVerification;
    use chrono::{Duration, Utc};
    use uuid::Uuid;

    let reset_token = format!("reset_{}", Uuid::new_v4());
    let create_verification = CreateVerification {
        identifier: "integration@test.com".to_string(),
        value: reset_token.clone(),
        expires_at: Utc::now() + Duration::hours(24),
    };
    auth.database()
        .create_verification(create_verification)
        .await
        .unwrap();

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Get,
        format!("/reset-password/{}", reset_token),
        HashMap::new(),
        None,
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let response_data: serde_json::Value = serde_json::from_str(&body_str).unwrap();

    assert_eq!(response_data["token"], reset_token);
}

/// Integration test for /ok endpoint
#[tokio::test]
async fn test_ok_endpoint() {
    let auth = create_test_auth_memory().await;

    let (status, response_data) = send_request(&auth, get_request("/ok")).await;
    assert_eq!(status, 200);
    assert_eq!(response_data["ok"], true);
}

/// Integration test for /error endpoint
#[tokio::test]
async fn test_error_endpoint() {
    let auth = create_test_auth_memory().await;

    let (status, response_data) = send_request(&auth, get_request("/error")).await;
    assert_eq!(status, 200);
    assert_eq!(response_data["ok"], false);
}

/// Integration test for POST /get-session (in addition to GET)
#[tokio::test]
async fn test_get_session_post_integration() {
    let auth = create_test_auth_memory().await;
    let (_user_id, session_token) = create_test_user_and_session(auth.clone()).await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert(
        "authorization".to_string(),
        format!("Bearer {}", session_token),
    );

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/get-session".to_string(),
        headers,
        Some(b"{}".to_vec()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let response_data: serde_json::Value = serde_json::from_str(&body_str).unwrap();

    assert!(response_data["session"]["token"].is_string());
    assert!(response_data["user"]["id"].is_string());
    assert_eq!(response_data["user"]["email"], "integration@test.com");
}

#[tokio::test]
async fn test_get_session_post_reflects_direct_db_role_updates_immediately() {
    let auth = create_test_auth_memory().await;
    let (user_id, session_token) = create_test_user_and_session(auth.clone()).await;

    auth.database()
        .update_user(
            &user_id,
            athena_auth::types::UpdateUser {
                role: Some("admin".to_string()),
                ..Default::default()
            },
        )
        .await
        .unwrap();

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert(
        "authorization".to_string(),
        format!("Bearer {}", session_token),
    );

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/get-session".to_string(),
        headers,
        Some(b"{}".to_vec()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let response_data: serde_json::Value = serde_json::from_slice(&response.body).unwrap();
    assert_eq!(
        response_data["user"]["role"], "admin",
        "POST /get-session should re-read the user and reflect the current role immediately"
    );
}

/// Integration test for POST /delete-user (changed from DELETE)
#[tokio::test]
async fn test_delete_user_post_method() {
    let auth = create_test_auth_memory().await;
    let (_user_id, session_token) = create_test_user_and_session(auth.clone()).await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert(
        "authorization".to_string(),
        format!("Bearer {}", session_token),
    );

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/delete-user".to_string(),
        headers,
        Some(b"{}".to_vec()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let response_data: serde_json::Value = serde_json::from_str(&body_str).unwrap();
    assert_eq!(response_data["success"], true);
}

/// Integration test for set-password success (social-only user, no password)
#[tokio::test]
async fn test_set_password_success() {
    let auth = create_test_auth_memory().await;

    use athena_auth::types::{AuthRequest, CreateSession, CreateUser};
    use chrono::{Duration, Utc};
    use std::collections::HashMap;

    // Create a user WITHOUT a password (social-only account)
    let create_user = CreateUser::new()
        .with_email("social@test.com")
        .with_name("Social User");
    let user = auth.database().create_user(create_user).await.unwrap();

    let create_session = CreateSession {
        user_id: user.id.clone(),
        expires_at: Utc::now() + Duration::hours(24),
        ip_address: None,
        user_agent: None,
        impersonated_by: None,
        active_organization_id: None,
    };
    let session = auth
        .database()
        .create_session(create_session)
        .await
        .unwrap();

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());
    headers.insert(
        "authorization".to_string(),
        format!("Bearer {}", session.token),
    );

    let set_data = serde_json::json!({
        "newPassword": "MyNewPassword123"
    });

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/set-password".to_string(),
        headers,
        Some(set_data.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let response_data: serde_json::Value = serde_json::from_str(&body_str).unwrap();
    assert_eq!(response_data["status"], true);
}

/// Integration test for set-password when user already has a password → 400
#[tokio::test]
async fn test_set_password_already_has_password() {
    let auth = create_test_auth_memory().await;
    let (_user_id, session_token) = create_test_user_and_session(auth.clone()).await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());
    headers.insert(
        "authorization".to_string(),
        format!("Bearer {}", session_token),
    );

    let set_data = serde_json::json!({
        "newPassword": "AnotherPassword123"
    });

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/set-password".to_string(),
        headers,
        Some(set_data.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 400);
}

/// Integration test for set-password unauthenticated → 401
#[tokio::test]
async fn test_set_password_unauthenticated() {
    let auth = create_test_auth_memory().await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());

    let set_data = serde_json::json!({
        "newPassword": "SomePassword123"
    });

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/set-password".to_string(),
        headers,
        Some(set_data.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 401);
}

/// Integration test for revoke-other-sessions endpoint
#[tokio::test]
async fn test_revoke_other_sessions_integration() {
    let auth = create_test_auth_memory().await;
    let (user_id, session_token1) = create_test_user_and_session(auth.clone()).await;

    // Create a second session for the same user

    use athena_auth::types::{AuthRequest, CreateSession};
    use chrono::{Duration, Utc};
    use std::collections::HashMap;

    let create_session = CreateSession {
        user_id: user_id.clone(),
        expires_at: Utc::now() + Duration::hours(24),
        ip_address: Some("192.168.1.1".to_string()),
        user_agent: Some("other-agent".to_string()),
        impersonated_by: None,
        active_organization_id: None,
    };
    let session2 = auth
        .database()
        .create_session(create_session)
        .await
        .unwrap();

    let mut headers = HashMap::new();
    headers.insert(
        "authorization".to_string(),
        format!("Bearer {}", session_token1),
    );

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/revoke-other-sessions".to_string(),
        headers,
        Some(b"{}".to_vec()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    // The current session should still be valid
    let s1 = auth.database().get_session(&session_token1).await.unwrap();
    assert!(s1.is_some());

    // The other session should be revoked
    let s2 = auth.database().get_session(&session2.token).await.unwrap();
    assert!(s2.is_none());
}

/// Integration test for cookie-based authentication
#[tokio::test]
async fn test_cookie_based_auth() {
    let auth = create_test_auth_memory().await;
    let (_user_id, session_token) = create_test_user_and_session(auth.clone()).await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    // Use cookie header instead of Bearer token
    let mut headers = HashMap::new();
    headers.insert(
        "cookie".to_string(),
        format!("athena-auth.session-token={}; other=value", session_token),
    );

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Get,
        "/get-session".to_string(),
        headers,
        None,
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let response_data: serde_json::Value = serde_json::from_str(&body_str).unwrap();
    assert_eq!(response_data["user"]["email"], "integration@test.com");
}

/// Integration test: Bearer token takes precedence over cookie
#[tokio::test]
async fn test_bearer_takes_precedence_over_cookie() {
    let auth = create_test_auth_memory().await;
    let (_user_id, session_token) = create_test_user_and_session(auth.clone()).await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    // Provide both Bearer and cookie, but cookie has invalid token
    let mut headers = HashMap::new();
    headers.insert(
        "authorization".to_string(),
        format!("Bearer {}", session_token),
    );
    headers.insert(
        "cookie".to_string(),
        "athena-auth.session-token=invalid_token".to_string(),
    );

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Get,
        "/get-session".to_string(),
        headers,
        None,
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    // Should succeed because Bearer token takes precedence
    assert_eq!(response.status, 200);
}

/// Integration test for unauthorized password operations
#[tokio::test]
async fn test_unauthorized_password_operations() {
    let auth = create_test_auth_memory().await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());

    let change_data = serde_json::json!({
        "currentPassword": "password123",
        "newPassword": "NewPassword123!"
    });

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/change-password".to_string(),
        headers,
        Some(change_data.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 401);
}
/// Integration test for change-email success
#[tokio::test]
async fn test_change_email_success() {
    let auth = create_test_auth_memory().await;
    let (_user_id, session_token) = create_test_user_and_session(auth.clone()).await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());
    headers.insert(
        "authorization".to_string(),
        format!("Bearer {}", session_token),
    );

    let body = serde_json::json!({ "newEmail": "newemail@test.com" });

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/change-email".to_string(),
        headers,
        Some(body.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let data: serde_json::Value = serde_json::from_str(&body_str).unwrap();
    assert_eq!(data["status"], true);
    assert_eq!(data["message"], "Email updated");
}

/// Integration test for change-email duplicate → 409
#[tokio::test]
async fn test_change_email_duplicate() {
    let auth = create_test_auth_memory().await;

    use athena_auth::types::{AuthRequest, CreateUser};
    use std::collections::HashMap;

    // Create first user
    let (_user_id, session_token) = create_test_user_and_session(auth.clone()).await;

    // Create second user with a different email
    let create_user = CreateUser::new()
        .with_email("existing@test.com")
        .with_name("Existing User");
    auth.database().create_user(create_user).await.unwrap();

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());
    headers.insert(
        "authorization".to_string(),
        format!("Bearer {}", session_token),
    );

    let body = serde_json::json!({ "newEmail": "existing@test.com" });

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/change-email".to_string(),
        headers,
        Some(body.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 409);
}

/// Integration test for change-email unauthenticated → 401
#[tokio::test]
async fn test_change_email_unauthenticated() {
    let auth = create_test_auth_memory().await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());

    let body = serde_json::json!({ "newEmail": "x@y.com" });

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/change-email".to_string(),
        headers,
        Some(body.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 401);
}

/// Integration test for delete-user/callback success
#[tokio::test]
async fn test_delete_user_callback_success() {
    let auth = create_test_auth_memory().await;
    let (user_id, _session_token) = create_test_user_and_session(auth.clone()).await;

    use athena_auth::types::{AuthRequest, CreateVerification};
    use chrono::{Duration, Utc};
    use std::collections::HashMap;

    // Create a deletion verification token
    let token = format!("delete_{}", uuid::Uuid::new_v4());
    let create_verification = CreateVerification {
        identifier: user_id.clone(),
        value: token.clone(),
        expires_at: Utc::now() + Duration::hours(24),
    };
    auth.database()
        .create_verification(create_verification)
        .await
        .unwrap();

    let mut query = HashMap::new();
    query.insert("token".to_string(), token);

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Get,
        "/delete-user/callback".to_string(),
        HashMap::new(),
        None,
        query,
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let data: serde_json::Value = serde_json::from_str(&body_str).unwrap();
    assert_eq!(data["success"], true);

    // Verify user is deleted
    let user = auth.database().get_user_by_id(&user_id).await.unwrap();
    assert!(user.is_none());
}

/// Integration test for delete-user/callback invalid token → 400
#[tokio::test]
async fn test_delete_user_callback_invalid_token() {
    let auth = create_test_auth_memory().await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut query = HashMap::new();
    query.insert("token".to_string(), "invalid_token".to_string());

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Get,
        "/delete-user/callback".to_string(),
        HashMap::new(),
        None,
        query,
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 400);
}

/// Integration test for list-accounts (empty)
#[tokio::test]
async fn test_list_accounts_empty() {
    let auth = create_test_auth_memory().await;
    let (_user_id, session_token) = create_test_user_and_session(auth.clone()).await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert(
        "authorization".to_string(),
        format!("Bearer {}", session_token),
    );

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Get,
        "/list-accounts".to_string(),
        headers,
        None,
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let accounts: Vec<serde_json::Value> = serde_json::from_str(&body_str).unwrap();
    assert_eq!(accounts.len(), 0); // No linked accounts yet
}

/// Integration test for list-accounts with an account
#[tokio::test]
async fn test_list_accounts_with_account() {
    let auth = create_test_auth_memory().await;
    let (user_id, session_token) = create_test_user_and_session(auth.clone()).await;

    use athena_auth::types::{AuthRequest, CreateAccount};
    use std::collections::HashMap;

    // Create an account for the user
    let create_account = CreateAccount {
        account_id: "12345".to_string(),
        provider_id: "google".to_string(),
        user_id: user_id.clone(),
        access_token: Some("access_token".to_string()),
        refresh_token: None,
        id_token: None,
        access_token_expires_at: None,
        refresh_token_expires_at: None,
        scope: Some("email profile".to_string()),
        password: None,
    };
    auth.database()
        .create_account(create_account)
        .await
        .unwrap();

    let mut headers = HashMap::new();
    headers.insert(
        "authorization".to_string(),
        format!("Bearer {}", session_token),
    );

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Get,
        "/list-accounts".to_string(),
        headers,
        None,
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let accounts: Vec<serde_json::Value> = serde_json::from_str(&body_str).unwrap();
    assert_eq!(accounts.len(), 1);
    assert_eq!(accounts[0]["provider"], "google");
    assert_eq!(
        accounts[0]["scopes"],
        serde_json::json!(["email", "profile"])
    );
    // Sensitive fields should NOT be present
    assert!(accounts[0].get("access_token").is_none());
    assert!(accounts[0].get("password").is_none());
}

/// Integration test for unlink-account success
#[tokio::test]
async fn test_unlink_account_success() {
    let auth = create_test_auth_memory().await;
    let (user_id, session_token) = create_test_user_and_session(auth.clone()).await;

    use athena_auth::types::{AuthRequest, CreateAccount};
    use std::collections::HashMap;

    // Create two accounts
    for provider in &["google", "github"] {
        let create_account = CreateAccount {
            account_id: format!("id_{}", provider),
            provider_id: provider.to_string(),
            user_id: user_id.clone(),
            access_token: None,
            refresh_token: None,
            id_token: None,
            access_token_expires_at: None,
            refresh_token_expires_at: None,
            scope: None,
            password: None,
        };
        auth.database()
            .create_account(create_account)
            .await
            .unwrap();
    }

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());
    headers.insert(
        "authorization".to_string(),
        format!("Bearer {}", session_token),
    );

    let unlink_data = serde_json::json!({
        "providerId": "google"
    });

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/unlink-account".to_string(),
        headers,
        Some(unlink_data.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    // Verify only one account remains
    let accounts = auth.database().get_user_accounts(&user_id).await.unwrap();
    assert_eq!(accounts.len(), 1);
    assert_eq!(accounts[0].provider_id, "github");
}

/// Integration test for unlink-account last credential → 400
#[tokio::test]
async fn test_unlink_last_account_fails() {
    let auth = create_test_auth_memory().await;

    use athena_auth::types::{AuthRequest, CreateAccount, CreateSession, CreateUser};
    use chrono::{Duration, Utc};
    use std::collections::HashMap;

    // Create a social-only user (NO password)
    let create_user = CreateUser::new()
        .with_email("social-only@test.com")
        .with_name("Social Only");
    let user = auth.database().create_user(create_user).await.unwrap();

    let create_session = CreateSession {
        user_id: user.id.clone(),
        expires_at: Utc::now() + Duration::hours(24),
        ip_address: None,
        user_agent: None,
        impersonated_by: None,
        active_organization_id: None,
    };
    let session = auth
        .database()
        .create_session(create_session)
        .await
        .unwrap();

    // Add one account
    let create_account = CreateAccount {
        account_id: "id_google".to_string(),
        provider_id: "google".to_string(),
        user_id: user.id.clone(),
        access_token: None,
        refresh_token: None,
        id_token: None,
        access_token_expires_at: None,
        refresh_token_expires_at: None,
        scope: None,
        password: None,
    };
    auth.database()
        .create_account(create_account)
        .await
        .unwrap();

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());
    headers.insert(
        "authorization".to_string(),
        format!("Bearer {}", session.token),
    );

    let unlink_data = serde_json::json!({
        "providerId": "google"
    });

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/unlink-account".to_string(),
        headers,
        Some(unlink_data.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 400); // Cannot unlink last credential
}

/// Integration test for list-accounts unauthenticated → 401
#[tokio::test]
async fn test_list_accounts_unauthenticated() {
    let auth = create_test_auth_memory().await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Get,
        "/list-accounts".to_string(),
        HashMap::new(),
        None,
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 401);
}

/*
/// Test basic Axum integration with memory database
#[tokio::test]
async fn test_axum_health_check() {
    let auth = create_test_auth_memory().await;

    let request = Request::builder()
        .method(Method::GET)
        .uri("/health")
        .body(Body::empty())
        .unwrap();

    let response = call_service(auth, request).await;

    assert_eq!(response.status(), StatusCode::OK);

    let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap();
    let response_json: Value = serde_json::from_slice(&body).unwrap();

    assert_eq!(response_json["status"], "ok");
    assert_eq!(response_json["service"], "athena-auth");
}

/// Test signup via Axum integration
#[tokio::test]
async fn test_axum_signup() {
    let auth = create_test_auth_memory().await;

    let signup_data = json!({
        "email": "axum@example.com",
        "password": "password123",
        "name": "Axum User"
    });

    let request = Request::builder()
        .method(Method::POST)
        .uri("/sign-up")
        .header("content-type", "application/json")
        .body(Body::from(signup_data.to_string()))
        .unwrap();

    let response = call_service(auth, request).await;

    assert_eq!(response.status(), StatusCode::CREATED);

    let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap();
    let response_json: Value = serde_json::from_slice(&body).unwrap();

    assert!(response_json["user"]["id"].is_string());
    assert_eq!(response_json["user"]["email"], "axum@example.com");
    assert_eq!(response_json["user"]["name"], "Axum User");
    assert!(response_json["session_token"].is_string());
}

/// Test signin via Axum integration
#[tokio::test]
async fn test_axum_signin() {
    let auth = create_test_auth_memory().await;

    // First signup
    let signup_data = json!({
        "email": "axum_signin@example.com",
        "password": "password123",
        "name": "Axum Signin User"
    });

    let signup_request = Request::builder()
        .method(Method::POST)
        .uri("/sign-up")
        .header("content-type", "application/json")
        .body(Body::from(signup_data.to_string()))
        .unwrap();

    let signup_response = call_service(auth.clone(), signup_request).await;
    assert_eq!(signup_response.status(), StatusCode::CREATED);

    // Then signin
    let signin_data = json!({
        "email": "axum_signin@example.com",
        "password": "password123"
    });

    let signin_request = Request::builder()
        .method(Method::POST)
        .uri("/sign-in")
        .header("content-type", "application/json")
        .body(Body::from(signin_data.to_string()))
        .unwrap();

    let signin_response = call_service(auth, signin_request).await;

    assert_eq!(signin_response.status(), StatusCode::OK);

    let body = axum::body::to_bytes(signin_response.into_body(), usize::MAX).await.unwrap();
    let response_json: Value = serde_json::from_slice(&body).unwrap();

    assert_eq!(response_json["user"]["email"], "axum_signin@example.com");
    assert!(response_json["session_token"].is_string());
}

/// Test error handling in Axum integration
#[tokio::test]
async fn test_axum_error_handling() {
    let auth = create_test_auth_memory().await;

    // Test invalid JSON
    let request = Request::builder()
        .method(Method::POST)
        .uri("/sign-up")
        .header("content-type", "application/json")
        .body(Body::from("invalid json"))
        .unwrap();

    let response = call_service(auth.clone(), request).await;
    assert_eq!(response.status(), StatusCode::BAD_REQUEST);

    // Test missing required fields
    let incomplete_data = json!({
        "email": "incomplete@example.com"
        // missing password
    });

    let request = Request::builder()
        .method(Method::POST)
        .uri("/sign-up")
        .header("content-type", "application/json")
        .body(Body::from(incomplete_data.to_string()))
        .unwrap();

    let response = call_service(auth.clone(), request).await;
    assert_eq!(response.status(), StatusCode::BAD_REQUEST);

    // Test non-existent route
    let request = Request::builder()
        .method(Method::GET)
        .uri("/non-existent")
        .body(Body::empty())
        .unwrap();

    let response = call_service(auth, request).await;
    assert_eq!(response.status(), StatusCode::NOT_FOUND);
}

/// Test duplicate email handling
#[tokio::test]
async fn test_axum_duplicate_email() {
    let auth = create_test_auth_memory().await;

    let signup_data = json!({
        "email": "duplicate_axum@example.com",
        "password": "password123"
    });

    let request1 = Request::builder()
        .method(Method::POST)
        .uri("/sign-up")
        .header("content-type", "application/json")
        .body(Body::from(signup_data.to_string()))
        .unwrap();

    let response1 = call_service(auth.clone(), request1).await;
    assert_eq!(response1.status(), StatusCode::CREATED);

    let request2 = Request::builder()
        .method(Method::POST)
        .uri("/sign-up")
        .header("content-type", "application/json")
        .body(Body::from(signup_data.to_string()))
        .unwrap();

    let response2 = call_service(auth, request2).await;
    assert_eq!(response2.status(), StatusCode::CONFLICT);
}

#[cfg(feature = "sqlx-postgres")]
mod postgres_tests {
    use super::*;
    use athena_auth::adapters::{PoolConfig, SqlxAdapter};
    use std::env;

    /// Helper to create test AthenaAuth instance with PostgreSQL
    async fn create_test_auth_postgres() -> Option<Arc<AthenaAuth<SqlxAdapter>>> {
        let database_url = env::var("TEST_DATABASE_URL").ok()?;

        let pool_config = PoolConfig {
            max_connections: 5,
            min_connections: 1,
            acquire_timeout: std::time::Duration::from_secs(10),
            idle_timeout: Some(std::time::Duration::from_secs(300)),
            max_lifetime: Some(std::time::Duration::from_secs(1800)),
        };

        let database = SqlxAdapter::with_config(&database_url, pool_config).await.ok()?;

        // Test connection
        database.test_connection().await.ok()?;

        let config = AuthConfig::new("postgres-test-secret-key-32-chars-long")
            .base_url("http://localhost:3000")
            .password_min_length(6);

        let auth = AthenaAuth::<SqlxAdapter>::new(config)
            .database(database)
            .plugin(EmailPasswordPlugin::new().enable_signup(true))
            .build()
            .await
            .ok()?;

        Some(Arc::new(auth))
    }

    /// Setup test database (run migrations, clean tables)
    async fn setup_test_database() -> Option<()> {
        let database_url = env::var("TEST_DATABASE_URL").ok()?;
        let pool = sqlx::PgPool::connect(&database_url).await.ok()?;

        // Clean up test data
        sqlx::query("DELETE FROM sessions WHERE user_id LIKE 'test_%'")
            .execute(&pool)
            .await
            .ok()?;

        sqlx::query("DELETE FROM users WHERE email LIKE '%test.example%'")
            .execute(&pool)
            .await
            .ok()?;

        pool.close().await;
        Some(())
    }

    /// Test PostgreSQL signup
    #[tokio::test]
    async fn test_postgres_signup() {
        if setup_test_database().await.is_none() {
            println!("Skipping PostgreSQL test - TEST_DATABASE_URL not set or database unavailable");
            return;
        }

        let Some(auth) = create_test_auth_postgres().await else {
            println!("Skipping PostgreSQL test - database setup failed");
            return;
        };

        let signup_data = json!({
            "email": "postgres.test.example@test.com",
            "password": "password123",
            "name": "PostgreSQL Test User"
        });

        let request = Request::builder()
            .method(Method::POST)
            .uri("/sign-up")
            .header("content-type", "application/json")
            .body(Body::from(signup_data.to_string()))
            .unwrap();

        let response = call_service(auth, request).await;

        assert_eq!(response.status(), StatusCode::CREATED);

        let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap();
        let response_json: Value = serde_json::from_slice(&body).unwrap();

        assert!(response_json["user"]["id"].is_string());
        assert_eq!(response_json["user"]["email"], "postgres.test.example@test.com");
        assert_eq!(response_json["user"]["name"], "PostgreSQL Test User");
        assert!(response_json["session_token"].is_string());
    }

    /// Test PostgreSQL signin
    #[tokio::test]
    async fn test_postgres_signin() {
        if setup_test_database().await.is_none() {
            println!("Skipping PostgreSQL test - TEST_DATABASE_URL not set or database unavailable");
            return;
        }

        let Some(auth) = create_test_auth_postgres().await else {
            println!("Skipping PostgreSQL test - database setup failed");
            return;
        };

        // First signup
        let signup_data = json!({
            "email": "postgres.signin.test.example@test.com",
            "password": "password123",
            "name": "PostgreSQL Signin User"
        });

        let signup_request = Request::builder()
            .method(Method::POST)
            .uri("/sign-up")
            .header("content-type", "application/json")
            .body(Body::from(signup_data.to_string()))
            .unwrap();

        let signup_response = call_service(auth.clone(), signup_request).await;
        assert_eq!(signup_response.status(), StatusCode::CREATED);

        // Then signin
        let signin_data = json!({
            "email": "postgres.signin.test.example@test.com",
            "password": "password123"
        });

        let signin_request = Request::builder()
            .method(Method::POST)
            .uri("/sign-in")
            .header("content-type", "application/json")
            .body(Body::from(signin_data.to_string()))
            .unwrap();

        let signin_response = call_service(auth, signin_request).await;

        assert_eq!(signin_response.status(), StatusCode::OK);

        let body = axum::body::to_bytes(signin_response.into_body(), usize::MAX).await.unwrap();
        let response_json: Value = serde_json::from_slice(&body).unwrap();

        assert_eq!(response_json["user"]["email"], "postgres.signin.test.example@test.com");
        assert!(response_json["session_token"].is_string());
    }

    /// Test PostgreSQL connection pool and session persistence
    #[tokio::test]
    async fn test_postgres_session_persistence() {
        if setup_test_database().await.is_none() {
            println!("Skipping PostgreSQL test - TEST_DATABASE_URL not set or database unavailable");
            return;
        }

        let Some(auth) = create_test_auth_postgres().await else {
            println!("Skipping PostgreSQL test - database setup failed");
            return;
        };

        // Create a user and session directly via database
        let database = auth.database();
        let session_manager = auth.session_manager();

        let create_user = athena_auth::types::CreateUser::new()
            .with_email("session.persistence.test.example@test.com")
            .with_name("Session Persistence User");

        let user = database.create_user(create_user).await.expect("Failed to create user");

        // Create a session
        let session = session_manager.create_session(&user,
            Some("127.0.0.1".to_string()),
            Some("test-user-agent".to_string())
        ).await.expect("Failed to create session");

        // Verify session can be retrieved
        let retrieved_session = session_manager.get_session(&session.token).await
            .expect("Failed to get session")
            .expect("Session not found");

        assert_eq!(retrieved_session.user_id, user.id);
        assert_eq!(retrieved_session.ip_address.as_deref(), Some("127.0.0.1"));
        assert_eq!(retrieved_session.user_agent.as_deref(), Some("test-user-agent"));

        // Test session cleanup
        let cleaned_count = session_manager.cleanup_expired_sessions().await
            .expect("Failed to cleanup sessions");

        // Should be 0 since session is not expired
        assert_eq!(cleaned_count, 0);

        // Delete the session
        session_manager.delete_session(&session.token).await
            .expect("Failed to delete session");

        // Verify session is gone
        let deleted_session = session_manager.get_session(&session.token).await
            .expect("Failed to check deleted session");
        assert!(deleted_session.is_none());
    }

    #[tokio::test]
    async fn test_postgres_expired_session_is_marked_inactive() {
        if setup_test_database().await.is_none() {
            println!("Skipping PostgreSQL test - TEST_DATABASE_URL not set or database unavailable");
            return;
        }

        let Some(auth) = create_test_auth_postgres().await else {
            println!("Skipping PostgreSQL test - database setup failed");
            return;
        };

        let database_url = env::var("TEST_DATABASE_URL").expect("TEST_DATABASE_URL must exist");
        let pool = sqlx::PgPool::connect(&database_url)
            .await
            .expect("raw pool should connect");

        let database = auth.database();
        let create_user = athena_auth::types::CreateUser::new()
            .with_email("expired.session.test.example@test.com")
            .with_name("Expired Session User");
        let user = database.create_user(create_user).await.expect("Failed to create user");

        let expired_session = database
            .create_session(athena_auth::types::CreateSession {
                user_id: user.id.clone(),
                expires_at: Utc::now() - chrono::Duration::minutes(5),
                ip_address: Some("127.0.0.1".to_string()),
                user_agent: Some("expired-session-test".to_string()),
                impersonated_by: None,
                active_organization_id: None,
            })
            .await
            .expect("Failed to create expired session");

        let listed_sessions = database
            .get_user_sessions(&user.id)
            .await
            .expect("Failed to list user sessions");
        assert!(listed_sessions.is_empty());

        let active = sqlx::query_scalar::<_, bool>(
            "SELECT active FROM athena.sessions WHERE token = $1",
        )
        .bind(&expired_session.token)
        .fetch_one(&pool)
        .await
        .expect("session row should still exist");
        assert!(!active, "expired session row should be marked inactive");

        let resolved = auth
            .session_manager()
            .get_session(&expired_session.token)
            .await
            .expect("expired session lookup should not error");
        assert!(resolved.is_none());

        pool.close().await;
    }

    /// Test PostgreSQL constraint violations (duplicate email)
    #[tokio::test]
    async fn test_postgres_constraints() {
        if setup_test_database().await.is_none() {
            println!("Skipping PostgreSQL test - TEST_DATABASE_URL not set or database unavailable");
            return;
        }

        let Some(auth) = create_test_auth_postgres().await else {
            println!("Skipping PostgreSQL test - database setup failed");
            return;
        };

        let signup_data = json!({
            "email": "constraint.test.example@test.com",
            "password": "password123"
        });

        // First signup should succeed
        let request1 = Request::builder()
            .method(Method::POST)
            .uri("/sign-up")
            .header("content-type", "application/json")
            .body(Body::from(signup_data.to_string()))
            .unwrap();

        let response1 = call_service(auth.clone(), request1).await;
        assert_eq!(response1.status(), StatusCode::CREATED);

        // Second signup with same email should fail
        let request2 = Request::builder()
            .method(Method::POST)
            .uri("/sign-up")
            .header("content-type", "application/json")
            .body(Body::from(signup_data.to_string()))
            .unwrap();

        let response2 = call_service(auth, request2).await;
        assert_eq!(response2.status(), StatusCode::CONFLICT);
    }
}

/// Performance test for concurrent requests
#[tokio::test]
async fn test_concurrent_requests() {
    let auth = create_test_auth_memory().await;

    let mut tasks = Vec::new();

    for i in 0..10 {
        let auth_clone = auth.clone();
        let task = tokio::spawn(async move {
            let signup_data = json!({
                "email": format!("concurrent{}@example.com", i),
                "password": "password123",
                "name": format!("Concurrent User {}", i)
            });

            let request = Request::builder()
                .method(Method::POST)
                .uri("/sign-up")
                .header("content-type", "application/json")
                .body(Body::from(signup_data.to_string()))
                .unwrap();

            call_service(auth_clone, request).await
        });

        tasks.push(task);
    }

    // Wait for all tasks to complete
    for task in tasks {
        let result = task.await;
        let response = result.unwrap();
        assert_eq!(response.status(), StatusCode::CREATED);
    }
}
*/

// ---------------------------------------------------------------------------
// Username Support Tests (Stage 1.7)
// ---------------------------------------------------------------------------

/// Sign up with username, then sign in by username
#[tokio::test]
async fn test_sign_up_with_username_and_sign_in() {
    let auth = create_test_auth_memory().await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    // Sign up with username
    let signup_data = serde_json::json!({
        "email": "usernameguy@test.com",
        "password": "password123",
        "name": "Username Guy",
        "username": "cool_user",
        "displayUsername": "Cool User"
    });

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/sign-up/email".to_string(),
        headers.clone(),
        Some(signup_data.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let data: serde_json::Value = serde_json::from_str(&body_str).unwrap();
    assert_eq!(data["user"]["username"], "cool_user");
    assert_eq!(data["user"]["displayUsername"], "Cool User");

    // Now sign in by username
    let signin_data = serde_json::json!({
        "username": "cool_user",
        "password": "password123"
    });

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/sign-in/username".to_string(),
        headers,
        Some(signin_data.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let data: serde_json::Value = serde_json::from_str(&body_str).unwrap();
    assert!(data["token"].is_string());
    assert_eq!(data["user"]["username"], "cool_user");
    assert_eq!(data["user"]["email"], "usernameguy@test.com");
}

/// Sign in by username with wrong password → 401
#[tokio::test]
async fn test_sign_in_username_wrong_password() {
    let auth = create_test_auth_memory().await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    // Sign up with username
    let signup_data = serde_json::json!({
        "email": "wrongpw@test.com",
        "password": "password123",
        "name": "Wrong PW",
        "username": "wrongpw_user"
    });

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/sign-up/email".to_string(),
        headers.clone(),
        Some(signup_data.to_string().into_bytes()),
        HashMap::new(),
    );

    auth.handle_request(request).await.unwrap();

    // Sign in with wrong password
    let signin_data = serde_json::json!({
        "username": "wrongpw_user",
        "password": "wrong_password"
    });

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/sign-in/username".to_string(),
        headers,
        Some(signin_data.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 401);
}

/// Sign in by nonexistent username → 401
#[tokio::test]
async fn test_sign_in_username_nonexistent() {
    let auth = create_test_auth_memory().await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let signin_data = serde_json::json!({
        "username": "no_such_user",
        "password": "password123"
    });

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/sign-in/username".to_string(),
        headers,
        Some(signin_data.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 401);
}

// ---------------------------------------------------------------------------
// API Key Plugin Integration Tests
// ---------------------------------------------------------------------------

/// Helper: create auth with ApiKeyPlugin and return auth + session token
async fn create_auth_with_apikey() -> (Arc<AthenaAuth<MemoryDatabaseAdapter>>, String, String) {
    let auth = create_test_auth_memory().await;
    let (user_id, session_token) = create_test_user_and_session(auth.clone()).await;
    (auth, user_id, session_token)
}

/// Create an API key and return (raw_key, key_id)
async fn create_api_key(
    auth: &AthenaAuth<MemoryDatabaseAdapter>,
    token: &str,
    body: serde_json::Value,
) -> (String, String) {
    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());
    headers.insert("authorization".to_string(), format!("Bearer {}", token));

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/api-key/create".to_string(),
        headers,
        Some(body.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let data: serde_json::Value = serde_json::from_str(&body_str).unwrap();

    let key = data["key"].as_str().unwrap().to_string();
    let id = data["id"].as_str().unwrap().to_string();
    (key, id)
}

/// Integration test: create API key
#[tokio::test]
async fn test_api_key_create() {
    let (auth, _user_id, token) = create_auth_with_apikey().await;

    let (key, id) = create_api_key(
        &auth,
        &token,
        serde_json::json!({
            "name": "test-key",
            "prefix": "sk_"
        }),
    )
    .await;

    assert!(!key.is_empty(), "key should not be empty");
    assert!(key.starts_with("sk_"), "key should start with prefix");
    assert!(!id.is_empty(), "id should not be empty");
}

/// Integration test: create API key with remaining and expiry
#[tokio::test]
async fn test_api_key_create_with_options() {
    let (auth, _user_id, token) = create_auth_with_apikey().await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());
    headers.insert("authorization".to_string(), format!("Bearer {}", token));

    let body = serde_json::json!({
        "name": "limited-key",
        "remaining": 100,
        "expiresIn": 3600000,
        "rateLimitEnabled": true,
        "rateLimitMax": 10,
        "rateLimitTimeWindow": 60000
    });

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/api-key/create".to_string(),
        headers,
        Some(body.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let data: serde_json::Value = serde_json::from_str(&body_str).unwrap();

    assert!(data["key"].is_string());
    assert_eq!(data["name"], "limited-key");
    assert_eq!(data["remaining"], 100);
    assert_eq!(data["rateLimitEnabled"], true);
    assert_eq!(data["rateLimitMax"], 10);
    assert!(data["expiresAt"].is_string());
}

/// Integration test: get API key by ID
#[tokio::test]
async fn test_api_key_get() {
    let (auth, _user_id, token) = create_auth_with_apikey().await;
    let (_key, id) = create_api_key(&auth, &token, serde_json::json!({"name": "get-test"})).await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("authorization".to_string(), format!("Bearer {}", token));

    let mut query = HashMap::new();
    query.insert("id".to_string(), id.clone());

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Get,
        "/api-key/get".to_string(),
        headers,
        None,
        query,
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let data: serde_json::Value = serde_json::from_str(&body_str).unwrap();

    assert_eq!(data["id"], id);
    assert_eq!(data["name"], "get-test");
    assert!(data["enabled"].as_bool().unwrap());
    // key_hash should NOT be in the response
    assert!(data.get("keyHash").is_none());
    assert!(data.get("key_hash").is_none());
}

/// Integration test: list API keys
#[tokio::test]
async fn test_api_key_list() {
    let (auth, _user_id, token) = create_auth_with_apikey().await;

    // Create two keys
    create_api_key(&auth, &token, serde_json::json!({"name": "key-1"})).await;
    create_api_key(&auth, &token, serde_json::json!({"name": "key-2"})).await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("authorization".to_string(), format!("Bearer {}", token));

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Get,
        "/api-key/list".to_string(),
        headers,
        None,
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let data: Vec<serde_json::Value> = serde_json::from_str(&body_str).unwrap();

    assert_eq!(data.len(), 2);
}

/// Integration test: update API key
#[tokio::test]
async fn test_api_key_update() {
    let (auth, _user_id, token) = create_auth_with_apikey().await;
    let (_key, id) =
        create_api_key(&auth, &token, serde_json::json!({"name": "original-name"})).await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());
    headers.insert("authorization".to_string(), format!("Bearer {}", token));

    let update_body = serde_json::json!({
        "id": id,
        "name": "updated-name",
        "enabled": false,
        "remaining": 50
    });

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/api-key/update".to_string(),
        headers,
        Some(update_body.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let data: serde_json::Value = serde_json::from_str(&body_str).unwrap();

    assert_eq!(data["id"], id);
    assert_eq!(data["name"], "updated-name");
    assert_eq!(data["enabled"], false);
    assert_eq!(data["remaining"], 50);
}

/// Integration test: delete API key
#[tokio::test]
async fn test_api_key_delete() {
    let (auth, _user_id, token) = create_auth_with_apikey().await;
    let (_key, id) = create_api_key(&auth, &token, serde_json::json!({"name": "delete-me"})).await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());
    headers.insert("authorization".to_string(), format!("Bearer {}", token));

    let delete_body = serde_json::json!({"id": id});

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/api-key/delete".to_string(),
        headers,
        Some(delete_body.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let body_str = String::from_utf8(response.body).unwrap();
    let data: serde_json::Value = serde_json::from_str(&body_str).unwrap();
    assert_eq!(data["status"], true);

    // Verify it's gone by listing
    let mut headers2 = HashMap::new();
    headers2.insert("authorization".to_string(), format!("Bearer {}", token));

    let list_request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Get,
        "/api-key/list".to_string(),
        headers2,
        None,
        HashMap::new(),
    );

    let list_response = auth.handle_request(list_request).await.unwrap();
    let list_body: Vec<serde_json::Value> = serde_json::from_slice(&list_response.body).unwrap();
    assert_eq!(list_body.len(), 0);
}

/// Integration test: unauthenticated create → 401
#[tokio::test]
async fn test_api_key_create_unauthenticated() {
    let auth = create_test_auth_memory().await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());

    let body = serde_json::json!({"name": "no-auth"});

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/api-key/create".to_string(),
        headers,
        Some(body.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 401);
}

/// Integration test: unauthenticated list → 401
#[tokio::test]
async fn test_api_key_list_unauthenticated() {
    let auth = create_test_auth_memory().await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Get,
        "/api-key/list".to_string(),
        HashMap::new(),
        None,
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 401);
}

/// Integration test: get key owned by another user → 404
#[tokio::test]
async fn test_api_key_get_other_users_key() {
    let (auth, _user_id, token1) = create_auth_with_apikey().await;
    let (_key, id) = create_api_key(&auth, &token1, serde_json::json!({"name": "user1-key"})).await;

    // Create a second user
    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());

    let signup_data = serde_json::json!({
        "email": "user2@test.com",
        "password": "password123",
        "name": "Second User"
    });

    let signup_request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/sign-up/email".to_string(),
        headers,
        Some(signup_data.to_string().into_bytes()),
        HashMap::new(),
    );

    let signup_resp = auth.handle_request(signup_request).await.unwrap();
    let signup_body: serde_json::Value = serde_json::from_slice(&signup_resp.body).unwrap();
    let token2 = signup_body["token"].as_str().unwrap();

    // Try to get user1's key with user2's token
    let mut headers2 = HashMap::new();
    headers2.insert("authorization".to_string(), format!("Bearer {}", token2));

    let mut query = HashMap::new();
    query.insert("id".to_string(), id.clone());

    let get_request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Get,
        "/api-key/get".to_string(),
        headers2,
        None,
        query,
    );

    let response = auth.handle_request(get_request).await.unwrap();
    assert_eq!(response.status, 404);
}

/// Integration test: delete key owned by another user → 404
#[tokio::test]
async fn test_api_key_delete_other_users_key() {
    let (auth, _user_id, token1) = create_auth_with_apikey().await;
    let (_key, id) = create_api_key(&auth, &token1, serde_json::json!({"name": "user1-key"})).await;

    // Create a second user
    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());

    let signup_data = serde_json::json!({
        "email": "user3@test.com",
        "password": "password123",
        "name": "Third User"
    });

    let signup_request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/sign-up/email".to_string(),
        headers,
        Some(signup_data.to_string().into_bytes()),
        HashMap::new(),
    );

    let signup_resp = auth.handle_request(signup_request).await.unwrap();
    let signup_body: serde_json::Value = serde_json::from_slice(&signup_resp.body).unwrap();
    let token2 = signup_body["token"].as_str().unwrap();

    // Try to delete user1's key with user2's token
    let mut headers2 = HashMap::new();
    headers2.insert("content-type".to_string(), "application/json".to_string());
    headers2.insert("authorization".to_string(), format!("Bearer {}", token2));

    let delete_body = serde_json::json!({"id": id});

    let delete_request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/api-key/delete".to_string(),
        headers2,
        Some(delete_body.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(delete_request).await.unwrap();
    assert_eq!(response.status, 404);
}

/// Integration test: update key owned by another user → 404
#[tokio::test]
async fn test_api_key_update_other_users_key() {
    let (auth, _user_id, token1) = create_auth_with_apikey().await;
    let (_key, id) = create_api_key(&auth, &token1, serde_json::json!({"name": "user1-key"})).await;

    // Create a second user
    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("content-type".to_string(), "application/json".to_string());

    let signup_data = serde_json::json!({
        "email": "user4@test.com",
        "password": "password123",
        "name": "Fourth User"
    });

    let signup_request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/sign-up/email".to_string(),
        headers,
        Some(signup_data.to_string().into_bytes()),
        HashMap::new(),
    );

    let signup_resp = auth.handle_request(signup_request).await.unwrap();
    let signup_body: serde_json::Value = serde_json::from_slice(&signup_resp.body).unwrap();
    let token2 = signup_body["token"].as_str().unwrap();

    // Try to update user1's key with user2's token
    let mut headers2 = HashMap::new();
    headers2.insert("content-type".to_string(), "application/json".to_string());
    headers2.insert("authorization".to_string(), format!("Bearer {}", token2));

    let update_body = serde_json::json!({
        "id": id,
        "name": "hijacked-name"
    });

    let update_request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Post,
        "/api-key/update".to_string(),
        headers2,
        Some(update_body.to_string().into_bytes()),
        HashMap::new(),
    );

    let response = auth.handle_request(update_request).await.unwrap();
    assert_eq!(response.status, 404);
}

/// Integration test: list keys for user with no keys → empty array
#[tokio::test]
async fn test_api_key_list_empty() {
    let (auth, _user_id, token) = create_auth_with_apikey().await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("authorization".to_string(), format!("Bearer {}", token));

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Get,
        "/api-key/list".to_string(),
        headers,
        None,
        HashMap::new(),
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 200);

    let data: Vec<serde_json::Value> = serde_json::from_slice(&response.body).unwrap();
    assert_eq!(data.len(), 0);
}

/// Integration test: get key with missing 'id' query param → 400
#[tokio::test]
async fn test_api_key_get_missing_id() {
    let (auth, _user_id, token) = create_auth_with_apikey().await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("authorization".to_string(), format!("Bearer {}", token));

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Get,
        "/api-key/get".to_string(),
        headers,
        None,
        HashMap::new(),
    ); // no 'id' param

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 400);
}

/// Integration test: get nonexistent key → 404
#[tokio::test]
async fn test_api_key_get_nonexistent() {
    let (auth, _user_id, token) = create_auth_with_apikey().await;

    use athena_auth::types::AuthRequest;
    use std::collections::HashMap;

    let mut headers = HashMap::new();
    headers.insert("authorization".to_string(), format!("Bearer {}", token));

    let mut query = HashMap::new();
    query.insert("id".to_string(), "nonexistent-id".to_string());

    let request = AuthRequest::from_parts(
        athena_auth::types::HttpMethod::Get,
        "/api-key/get".to_string(),
        headers,
        None,
        query,
    );

    let response = auth.handle_request(request).await.unwrap();
    assert_eq!(response.status, 404);
}

/// get_user_by_username works via database adapter
#[tokio::test]
async fn test_get_user_by_username_adapter() {
    use athena_auth::adapters::MemoryDatabaseAdapter;
    use athena_auth::types::CreateUser;

    let db = MemoryDatabaseAdapter::new();

    // Create user with username
    let create = CreateUser::new()
        .with_email("dbtest@test.com")
        .with_name("DB Test")
        .with_username("db_user");

    let user = db.create_user(create).await.unwrap();

    // Lookup by username
    let found = db.get_user_by_username("db_user").await.unwrap();
    assert!(found.is_some());
    assert_eq!(found.unwrap().id, user.id);

    // Lookup nonexistent
    let not_found = db.get_user_by_username("no_user").await.unwrap();
    assert!(not_found.is_none());
}
