//! Integration tests for health check and authentication endpoints

use axum::http::StatusCode;
use helpers::*;
use serde_json::json;

mod helpers;

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_register_debug() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    let response = ctx
        .post(
            "/auth/register",
            json!({
                "login": "debuguser",
                "password": "TestPassword123!",
                "display_name": "Debug User"
            }),
            None,
        )
        .await
        .expect("Failed to make request");

    let status = response.status();
    println!("Status: {}", status);

    let body_text = response.text().await.expect("Failed to get body");
    println!("Body: {}", body_text);

    // This test is just for debugging - will fail if not 201
    assert_eq!(status, StatusCode::OK);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_health_check() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    let response = ctx
        .get("/health", None)
        .await
        .expect("Failed to make request");

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

    let body: serde_json::Value = response.json().await.expect("Failed to parse JSON");

    assert_eq!(body["status"], "ok");
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_health_detailed() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    let response = ctx
        .get("/health/detailed", None)
        .await
        .expect("Failed to make request");

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

    let body: serde_json::Value = response.json().await.expect("Failed to parse JSON");

    assert_eq!(body["status"], "ok");
    assert_eq!(body["database"], "connected");
    assert!(body["version"].is_string());
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_health_ready() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    let response = ctx
        .get("/health/ready", None)
        .await
        .expect("Failed to make request");

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

    // Readiness endpoint returns empty body with 200 status
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_health_live() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    let response = ctx
        .get("/health/live", None)
        .await
        .expect("Failed to make request");

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

    // Liveness endpoint returns empty body with 200 status
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_register_user() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    let response = ctx
        .post(
            "/auth/register",
            json!({
                "login": "newuser",
                "password": "SecurePassword123!",
                "display_name": "New User"
            }),
            None,
        )
        .await
        .expect("Failed to make request");

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

    let body: serde_json::Value = response.json().await.expect("Failed to parse JSON");

    assert!(body["data"].is_object());
    assert!(body["data"]["access_token"].is_string());
    assert!(body["data"]["refresh_token"].is_string());
    assert!(body["data"]["user"].is_object());
    assert_eq!(body["data"]["user"]["login"], "newuser");
    assert_eq!(body["data"]["user"]["display_name"], "New User");
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_register_duplicate_user() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    // Register first user
    let _ = ctx
        .post(
            "/auth/register",
            json!({
                "login": "duplicate",
                "password": "SecurePassword123!",
                "display_name": "Duplicate User"
            }),
            None,
        )
        .await
        .expect("Failed to make request");

    // Try to register same user again
    let response = ctx
        .post(
            "/auth/register",
            json!({
                "login": "duplicate",
                "password": "SecurePassword123!",
                "display_name": "Duplicate User"
            }),
            None,
        )
        .await
        .expect("Failed to make request");

    assert_eq!(response.status(), StatusCode::CONFLICT);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_register_invalid_password() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    let response = ctx
        .post(
            "/auth/register",
            json!({
                "login": "testuser",
                "password": "weak",
                "display_name": "Test User"
            }),
            None,
        )
        .await
        .expect("Failed to make request");

    assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_login_success() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    // Register a user first
    let _ = ctx
        .post(
            "/auth/register",
            json!({
                "login": "loginuser",
                "password": "SecurePassword123!",
                "display_name": "Login User"
            }),
            None,
        )
        .await
        .expect("Failed to register user");

    // Now try to login
    let response = ctx
        .post(
            "/auth/login",
            json!({
                "login": "loginuser",
                "password": "SecurePassword123!"
            }),
            None,
        )
        .await
        .expect("Failed to make request");

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

    let body: serde_json::Value = response.json().await.expect("Failed to parse JSON");

    assert!(body["data"]["access_token"].is_string());
    assert!(body["data"]["refresh_token"].is_string());
    assert_eq!(body["data"]["user"]["login"], "loginuser");
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_login_wrong_password() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    // Register a user first
    let _ = ctx
        .post(
            "/auth/register",
            json!({
                "login": "wrongpassuser",
                "password": "SecurePassword123!",
                "display_name": "Wrong Pass User"
            }),
            None,
        )
        .await
        .expect("Failed to register user");

    // Try to login with wrong password
    let response = ctx
        .post(
            "/auth/login",
            json!({
                "login": "wrongpassuser",
                "password": "WrongPassword123!"
            }),
            None,
        )
        .await
        .expect("Failed to make request");

    assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_login_nonexistent_user() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    let response = ctx
        .post(
            "/auth/login",
            json!({
                "login": "nonexistent",
                "password": "SomePassword123!"
            }),
            None,
        )
        .await
        .expect("Failed to make request");

    assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}

// ── LDAP auth tests ──────────────────────────────────────────────────

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_ldap_login_returns_501_when_not_configured() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    let response = ctx
        .post(
            "/auth/ldap/login",
            json!({
                "login": "jdoe",
                "password": "secret"
            }),
            None,
        )
        .await
        .expect("Failed to make request");

    // LDAP is not configured in config.test.yaml, so the endpoint
    // should return 501 Not Implemented.
    assert_eq!(response.status(), StatusCode::NOT_IMPLEMENTED);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_ldap_login_validates_empty_login() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    let response = ctx
        .post(
            "/auth/ldap/login",
            json!({
                "login": "",
                "password": "secret"
            }),
            None,
        )
        .await
        .expect("Failed to make request");

    // Validation should fail before we even check LDAP config
    assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_ldap_login_validates_empty_password() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    let response = ctx
        .post(
            "/auth/ldap/login",
            json!({
                "login": "jdoe",
                "password": ""
            }),
            None,
        )
        .await
        .expect("Failed to make request");

    assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_ldap_login_validates_missing_fields() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    let response = ctx
        .post("/auth/ldap/login", json!({}), None)
        .await
        .expect("Failed to make request");

    // Missing required fields should return 422
    assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
}

// ── auth/settings LDAP field tests ──────────────────────────────────

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_auth_settings_includes_ldap_fields_disabled() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    let response = ctx
        .get("/auth/settings", None)
        .await
        .expect("Failed to make request");

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

    let body: serde_json::Value = response.json().await.expect("Failed to parse JSON");

    // LDAP is not configured in config.test.yaml, so these should all
    // reflect the disabled state.
    assert_eq!(body["data"]["ldap_enabled"], false);
    assert_eq!(body["data"]["ldap_visible_by_default"], false);
    assert!(body["data"]["ldap_provider_name"].is_null());
    assert!(body["data"]["ldap_provider_label"].is_null());
    assert!(body["data"]["ldap_provider_icon_url"].is_null());

    // Existing fields should still be present
    assert!(body["data"]["authentication_enabled"].is_boolean());
    assert!(body["data"]["local_password_enabled"].is_boolean());
    assert!(body["data"]["oidc_enabled"].is_boolean());
    assert!(body["data"]["self_registration_enabled"].is_boolean());
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_get_current_user() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context")
        .with_auth()
        .await
        .expect("Failed to authenticate");

    let response = ctx
        .get("/auth/me", ctx.token())
        .await
        .expect("Failed to make request");

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

    let body: serde_json::Value = response.json().await.expect("Failed to parse JSON");

    assert!(body["data"].is_object());
    assert!(body["data"]["id"].is_number());
    assert!(body["data"]["login"].is_string());
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_get_current_user_unauthorized() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    let response = ctx
        .get("/auth/me", None)
        .await
        .expect("Failed to make request");

    assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_get_current_user_invalid_token() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    let response = ctx
        .get("/auth/me", Some("invalid-token"))
        .await
        .expect("Failed to make request");

    assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_refresh_token() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    // Register a user first
    let register_response = ctx
        .post(
            "/auth/register",
            json!({
                "login": "refreshuser",
                "email": "refresh@example.com",
                "password": "SecurePassword123!",
                "display_name": "Refresh User"
            }),
            None,
        )
        .await
        .expect("Failed to register user");

    let register_body: serde_json::Value = register_response
        .json()
        .await
        .expect("Failed to parse JSON");

    let refresh_token = register_body["data"]["refresh_token"]
        .as_str()
        .expect("Missing refresh token");

    // Use refresh token to get new access token
    let response = ctx
        .post(
            "/auth/refresh",
            json!({
                "refresh_token": refresh_token
            }),
            None,
        )
        .await
        .expect("Failed to make request");

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

    let body: serde_json::Value = response.json().await.expect("Failed to parse JSON");

    assert!(body["data"]["access_token"].is_string());
    assert!(body["data"]["refresh_token"].is_string());
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_refresh_with_invalid_token() {
    let ctx = TestContext::new()
        .await
        .expect("Failed to create test context");

    let response = ctx
        .post(
            "/auth/refresh",
            json!({
                "refresh_token": "invalid-refresh-token"
            }),
            None,
        )
        .await
        .expect("Failed to make request");

    assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}
