//! Tests for schema API endpoints: `/schema/clients`, `/schema/tables`, `/schema/columns`.
//!
//! These tests use an empty Postgres registry; they assert on 400 when the client
//! is missing or unknown. Integration tests with a real DB would be needed for 200 responses.

use actix_web::http::StatusCode;
use actix_web::web::Data;
use actix_web::{App, test};
use athena_rs::api::schema::services;
use athena_rs::AppState;
use athena_rs::drivers::postgresql::sqlx_driver::PostgresClientRegistry;
use moka::future::Cache;
use reqwest::Client;
use serde_json::Value;
use std::sync::Arc;
use std::time::Instant;

fn test_app_state() -> Data<AppState> {
    let cache: Arc<Cache<String, Value>> = Arc::new(Cache::builder().build());
    let immortal_cache: Arc<Cache<String, Value>> = Arc::new(Cache::builder().build());
    let client = Client::builder()
        .build()
        .expect("Failed to build HTTP client");
    let jdbc_pool_cache = Arc::new(Cache::builder().max_capacity(64).build());
    Data::new(AppState {
        cache,
        immortal_cache,
        client,
        process_start_time_seconds: 0,
        process_started_at: Instant::now(),
        pg_registry: Arc::new(PostgresClientRegistry::empty()),
        jdbc_pool_cache,
        gateway_force_camel_case_to_snake_case: false,
        gateway_auto_cast_uuid_filter_values_to_text: true,
        pipeline_registry: None,
        logging_client_name: None,
        gateway_auth_client_name: None,
        gateway_jdbc_allow_private_hosts: true,
        gateway_jdbc_allowed_hosts: vec![],
        prometheus_metrics_enabled: false,
        metrics_state: Arc::new(athena_rs::api::metrics::MetricsState::new()),
    })
}

#[actix_web::test]
async fn schema_columns_requires_x_athena_client() {
    let state = test_app_state();
    let app = test::init_service(
        App::new().app_data(state).configure(services),
    )
    .await;

    let req = test::TestRequest::get()
        .uri("/schema/columns?table_name=users")
        .to_request();
    let resp = test::call_service(&app, req).await;

    assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
    let body: Value = test::read_body_json(resp).await;
    assert!(
        body.get("message").and_then(Value::as_str).map_or(false, |m| m.contains("X-Athena-Client")),
        "expected message about X-Athena-Client, got {:?}",
        body
    );
}

#[actix_web::test]
async fn schema_columns_accepts_x_api_key_header() {
    let state = test_app_state();
    let app = test::init_service(
        App::new().app_data(state).configure(services),
    )
    .await;

    let req = test::TestRequest::get()
        .uri("/schema/columns?table_name=users")
        .insert_header(("X-Athena-Client", "athena_logging"))
        .insert_header(("X-API-Key", "test-key"))
        .to_request();
    let resp = test::call_service(&app, req).await;

    assert!(
        resp.status() == StatusCode::BAD_REQUEST
            && test::read_body_json::<Value, _>(resp).await.get("message").and_then(Value::as_str).map_or(false, |m| m.contains("not configured") || m.contains("Client")),
        "with X-Athena-Client and X-API-Key we expect 400 (client not configured), not 401"
    );
}

#[actix_web::test]
async fn schema_columns_rejects_missing_table_name() {
    let state = test_app_state();
    let app = test::init_service(
        App::new().app_data(state).configure(services),
    )
    .await;

    let req = test::TestRequest::get()
        .uri("/schema/columns")
        .insert_header(("X-Athena-Client", "athena_logging"))
        .to_request();
    let resp = test::call_service(&app, req).await;

    assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
}

#[actix_web::test]
async fn schema_columns_rejects_invalid_table_name() {
    let state = test_app_state();
    let app = test::init_service(
        App::new().app_data(state).configure(services),
    )
    .await;

    let req = test::TestRequest::get()
        .uri("/schema/columns?table_name=foo-bar")
        .insert_header(("X-Athena-Client", "athena_logging"))
        .to_request();
    let resp = test::call_service(&app, req).await;

    assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
    let body: Value = test::read_body_json(resp).await;
    assert_eq!(body.get("error").and_then(Value::as_str), Some("Invalid table_name parameter"));
}

#[actix_web::test]
async fn schema_columns_unknown_client_returns_bad_request() {
    let state = test_app_state();
    let app = test::init_service(
        App::new().app_data(state).configure(services),
    )
    .await;

    let req = test::TestRequest::get()
        .uri("/schema/columns?table_name=users")
        .insert_header(("X-Athena-Client", "nonexistent_client"))
        .to_request();
    let resp = test::call_service(&app, req).await;

    assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
    let body: Value = test::read_body_json(resp).await;
    assert!(
        body.get("message").and_then(Value::as_str).map_or(false, |m| m.contains("not available") || m.contains("registry")),
        "expected message about client not configured, got {:?}",
        body
    );
}

#[actix_web::test]
async fn schema_columns_accepts_table_schema_param() {
    let state = test_app_state();
    let app = test::init_service(
        App::new().app_data(state).configure(services),
    )
    .await;

    let req = test::TestRequest::get()
        .uri("/schema/columns?table_name=users&table_schema=public")
        .insert_header(("X-Athena-Client", "athena_logging"))
        .to_request();
    let resp = test::call_service(&app, req).await;

    assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
    let body: Value = test::read_body_json(resp).await;
    assert!(
        body.get("message").and_then(Value::as_str).map_or(false, |m| m.contains("not available") || m.contains("registry")),
        "with table_schema we still get 400 for unknown client, got {:?}",
        body
    );
}

#[actix_web::test]
async fn schema_columns_rejects_invalid_table_schema() {
    let state = test_app_state();
    let app = test::init_service(
        App::new().app_data(state).configure(services),
    )
    .await;

    let req = test::TestRequest::get()
        .uri("/schema/columns?table_name=users&table_schema=bad-schema")
        .insert_header(("X-Athena-Client", "athena_logging"))
        .to_request();
    let resp = test::call_service(&app, req).await;

    assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
    let body: Value = test::read_body_json(resp).await;
    assert_eq!(body.get("error").and_then(Value::as_str), Some("Invalid table_schema parameter"));
}
