//! The admin-session ABSOLUTE TTL cap (audit 2026-07-11 C1, migration 0070).
//!
//! `PICLOUD_SESSION_ABSOLUTE_TTL_HOURS` is the whole mitigation for a stolen-but-
//! warm admin token — the highest-value credential in the system. A session's
//! sliding `expires_at` is bumped on every use, but it is clamped at
//! `login_time + absolute_ttl`, so even a continuously-used or stolen token self-
//! expires. Nothing tested it (grepping `absolute_expires_at` across all tests
//! returned zero hits).
//!
//! The AUTHORITATIVE enforcement is the lookup filter: `admin_session_repo`'s
//! `lookup` selects `... AND absolute_expires_at > NOW()`, so a row past its cap
//! is invisible no matter how far into the future `touch` has pushed `expires_at`.
//! The middleware's `sliding.min(absolute_expires_at)` clamp is defense-in-depth
//! on top; the lookup filter is what actually expires the token, and it is what
//! these tests pin. Deleting the `absolute_expires_at > NOW()` predicate — the
//! plausible regression — makes every past-cap assertion below fail.
//!
//! Skips cleanly when `DATABASE_URL` is unset.

use chrono::{Duration, Utc};
use picloud_manager_core::admin_session_repo::{
    AdminSessionRepository, PostgresAdminSessionRepository,
};
use picloud_shared::AdminUserId;
use uuid::Uuid;

async fn mk_admin(pool: &sqlx::PgPool) -> AdminUserId {
    let uniq = Uuid::new_v4().simple().to_string();
    let (id,): (Uuid,) = sqlx::query_as(
        "INSERT INTO admin_users (username, password_hash) VALUES ($1, 'x') RETURNING id",
    )
    .bind(format!("sat-{uniq}"))
    .fetch_one(pool)
    .await
    .expect("insert admin");
    id.into()
}

/// A session within BOTH windows resolves; one past its absolute cap does NOT —
/// even though its sliding `expires_at` is still in the future. That gap (future
/// `expires_at`, past `absolute_expires_at`) is exactly the stolen-but-warm token
/// the cap exists to kill.
#[tokio::test]
async fn a_session_past_its_absolute_cap_does_not_resolve() {
    let Some(pool) = picloud_test_support::test_pool("session_absolute_ttl").await else {
        return;
    };
    let repo = PostgresAdminSessionRepository::new(pool.clone());
    let user = mk_admin(&pool).await;
    let now = Utc::now();

    // Warm and within its cap — resolves.
    repo.create(
        user,
        "hash-live",
        now + Duration::hours(1),
        now + Duration::hours(24),
    )
    .await
    .expect("create live");
    let live = repo.lookup("hash-live").await.expect("lookup live");
    assert!(live.is_some(), "a session within both windows must resolve");
    assert_eq!(live.unwrap().user_id, user);

    // Sliding window still open (expires_at in the future), but the ABSOLUTE cap
    // has passed. Must NOT resolve — this is the whole guarantee.
    repo.create(
        user,
        "hash-capped",
        now + Duration::hours(1),
        now - Duration::minutes(1),
    )
    .await
    .expect("create capped");
    assert!(
        repo.lookup("hash-capped")
            .await
            .expect("lookup capped")
            .is_none(),
        "a warm session (future expires_at) past its absolute cap must self-expire"
    );
}

/// `touch` cannot resurrect a past-cap session. Even after the sliding window is
/// bumped far into the future, the lookup's absolute filter keeps the row hidden —
/// so a continuously-used stolen token cannot slide past its cap.
#[tokio::test]
async fn touch_cannot_extend_a_session_past_its_absolute_cap() {
    let Some(pool) = picloud_test_support::test_pool("session_absolute_ttl").await else {
        return;
    };
    let repo = PostgresAdminSessionRepository::new(pool.clone());
    let user = mk_admin(&pool).await;
    let now = Utc::now();

    // Absolute cap already passed; sliding window open.
    repo.create(
        user,
        "hash-warm",
        now + Duration::hours(1),
        now - Duration::minutes(1),
    )
    .await
    .expect("create warm");

    // Simulate the sliding-window bump with an UNCLAMPED value a year out — i.e.
    // the exact thing the middleware clamp is meant to prevent. Even so:
    repo.touch("hash-warm", now + Duration::days(365))
        .await
        .expect("touch");

    assert!(
        repo.lookup("hash-warm").await.expect("lookup").is_none(),
        "even an unclamped touch a year out cannot resurrect a past-cap session — \
         the lookup's absolute_expires_at filter is the backstop"
    );
}
