//! M7.1 — integration tests for `compliance_core::auth::require_tenant_status`.
//!
//! Exercises the middleware end-to-end through an Axum router so we
//! catch wiring bugs (extension propagation, method matching) that pure
//! unit tests would miss.

#![allow(clippy::expect_used, clippy::unwrap_used)]

use axum::{
    body::Body,
    extract::Request,
    http::{Method, StatusCode},
    middleware::{from_fn, Next},
    response::Response,
    routing::{get, post},
    Router,
};
use compliance_core::{auth::require_tenant_status, TenantContext, TenantStatus};
use tower::ServiceExt;

fn ctx_with(status: TenantStatus) -> TenantContext {
    TenantContext {
        tenant_id: "t-1".to_string(),
        tenant_slug: "acme".to_string(),
        org_roles: vec![],
        products: vec![],
        plan: "starter".to_string(),
        status,
        user_id: "u-1".to_string(),
        user_name: None,
    }
}

fn router_with_ctx(ctx: Option<TenantContext>) -> Router {
    let injector = move |mut req: Request, next: Next| {
        let ctx = ctx.clone();
        async move {
            if let Some(c) = ctx {
                req.extensions_mut().insert(c);
            }
            next.run(req).await
        }
    };

    Router::new()
        .route("/r", get(|| async { "read" }))
        .route("/w", post(|| async { "write" }))
        .layer(from_fn(require_tenant_status))
        .layer(from_fn(injector))
}

async fn call(router: Router, method: Method, path: &str) -> Response {
    let req = Request::builder()
        .method(method)
        .uri(path)
        .body(Body::empty())
        .expect("request build");
    router.oneshot(req).await.expect("oneshot")
}

#[tokio::test]
async fn active_tenant_can_read_and_write() {
    let r = router_with_ctx(Some(ctx_with(TenantStatus::Active)));
    assert_eq!(
        call(r.clone(), Method::GET, "/r").await.status(),
        StatusCode::OK
    );
    assert_eq!(call(r, Method::POST, "/w").await.status(), StatusCode::OK);
}

#[tokio::test]
async fn trial_tenant_can_read_and_write() {
    let r = router_with_ctx(Some(ctx_with(TenantStatus::Trial)));
    assert_eq!(
        call(r.clone(), Method::GET, "/r").await.status(),
        StatusCode::OK
    );
    assert_eq!(call(r, Method::POST, "/w").await.status(), StatusCode::OK);
}

#[tokio::test]
async fn demo_tenant_can_read_and_write() {
    let r = router_with_ctx(Some(ctx_with(TenantStatus::Demo)));
    assert_eq!(
        call(r.clone(), Method::GET, "/r").await.status(),
        StatusCode::OK
    );
    assert_eq!(call(r, Method::POST, "/w").await.status(), StatusCode::OK);
}

#[tokio::test]
async fn frozen_tenant_can_read_but_not_write() {
    let r = router_with_ctx(Some(ctx_with(TenantStatus::Frozen)));
    assert_eq!(
        call(r.clone(), Method::GET, "/r").await.status(),
        StatusCode::OK
    );
    assert_eq!(
        call(r, Method::POST, "/w").await.status(),
        StatusCode::PAYMENT_REQUIRED
    );
}

#[tokio::test]
async fn archived_tenant_is_gone_on_every_method() {
    let r = router_with_ctx(Some(ctx_with(TenantStatus::Archived)));
    assert_eq!(
        call(r.clone(), Method::GET, "/r").await.status(),
        StatusCode::GONE
    );
    assert_eq!(call(r, Method::POST, "/w").await.status(), StatusCode::GONE);
}

#[tokio::test]
async fn no_context_passes_through() {
    let r = router_with_ctx(None);
    assert_eq!(
        call(r.clone(), Method::GET, "/r").await.status(),
        StatusCode::OK
    );
    assert_eq!(call(r, Method::POST, "/w").await.status(), StatusCode::OK);
}
