//! Lazy retention sweep: GET /web/cases must auto-close stale cases
//! and auto-purge old closed cases, driven by per-user retention
//! settings from users.toml. Tests age the *comparison objects*
//! (m4a mtime, closed_at string) instead of faking "now" — gives
//! realistic behavioural coverage without a clock abstraction.

mod common;

use std::time::Duration;

use axum::http::StatusCode;
use tower::util::ServiceExt;

use common::{
    CLOSE_MARKER, TestConfig, get_with_cookie, past_rfc3339, paths, seed_case,
    seed_recording_with_age, test_user_with_retention, write_closed_marker,
};

/// Trigger the sweep via its natural path: GET /web/cases.
async fn trigger_sweep(app: &axum::Router, cookie: &str) {
    let resp = app
        .clone()
        .oneshot(get_with_cookie(paths::CASES, cookie))
        .await
        .unwrap();
    assert_eq!(resp.status(), StatusCode::OK);
}

fn build_cfg(
    label: &'static str,
    auto_close_days: u32,
    auto_delete_days: u32,
) -> std::sync::Arc<doctate_server::config::Config> {
    TestConfig::new()
        .with_label(label)
        .with_user(test_user_with_retention(
            "dr_a",
            auto_close_days,
            auto_delete_days,
        ))
        .build()
}

#[tokio::test]
async fn auto_close_fires_when_last_recording_too_old() {
    let cfg = build_cfg("close-old", 7, 0);
    let case_id = "11111111-1111-1111-1111-111111111111";
    let case_dir = seed_case(&cfg.data_path, "dr_a", case_id);
    seed_recording_with_age(
        &case_dir,
        "2026-04-01T10-00-00Z",
        Duration::from_secs(8 * 86400),
    );

    let app = doctate_server::create_router(cfg);
    let cookie = common::login(&app, "dr_a", "s").await;

    assert!(!case_dir.join(CLOSE_MARKER).exists());
    trigger_sweep(&app, &cookie).await;
    assert!(
        case_dir.join(CLOSE_MARKER).exists(),
        "stale case must be auto-closed"
    );
}

#[tokio::test]
async fn auto_close_disabled_when_days_zero() {
    // auto_close_days = 0 → sweep never closes for age; even a very
    // old recording must stay open.
    let cfg = build_cfg("close-zero", 0, 0);
    let case_id = "11111111-1111-1111-1111-111111111111";
    let case_dir = seed_case(&cfg.data_path, "dr_a", case_id);
    seed_recording_with_age(
        &case_dir,
        "2026-04-01T10-00-00Z",
        Duration::from_secs(365 * 86400),
    );

    let app = doctate_server::create_router(cfg);
    let cookie = common::login(&app, "dr_a", "s").await;

    trigger_sweep(&app, &cookie).await;
    assert!(
        !case_dir.join(CLOSE_MARKER).exists(),
        "case must stay open when auto_close_days = 0"
    );
}

#[tokio::test]
async fn auto_close_respects_newest_recording() {
    // Case has one very old .m4a and one fresh .m4a — must stay open.
    let cfg = build_cfg("close-newest", 7, 0);
    let case_id = "11111111-1111-1111-1111-111111111111";
    let case_dir = seed_case(&cfg.data_path, "dr_a", case_id);
    seed_recording_with_age(
        &case_dir,
        "2026-04-01T10-00-00Z",
        Duration::from_secs(30 * 86400),
    );
    seed_recording_with_age(&case_dir, "2026-04-01T11-00-00Z", Duration::from_secs(60));

    let app = doctate_server::create_router(cfg);
    let cookie = common::login(&app, "dr_a", "s").await;

    trigger_sweep(&app, &cookie).await;
    assert!(
        !case_dir.join(CLOSE_MARKER).exists(),
        "recent addendum must keep the case open"
    );
}

#[tokio::test]
async fn auto_purge_removes_old_closed_case() {
    let cfg = build_cfg("purge-old", 0, 30);
    let case_id = "11111111-1111-1111-1111-111111111111";
    let case_dir = seed_case(&cfg.data_path, "dr_a", case_id);
    seed_recording_with_age(&case_dir, "2026-04-01T10-00-00Z", Duration::from_secs(60));
    // closed_at = 31 days ago → must be purged.
    write_closed_marker(&case_dir, &past_rfc3339(Duration::from_secs(31 * 86400)));

    let app = doctate_server::create_router(cfg);
    let cookie = common::login(&app, "dr_a", "s").await;

    trigger_sweep(&app, &cookie).await;
    assert!(
        !case_dir.exists(),
        "old closed case must be purged (directory gone)"
    );
}

#[tokio::test]
async fn auto_purge_disabled_when_days_zero() {
    // Admin-test guarantee: auto_delete_days = 0 means closed cases
    // are retained indefinitely.
    let cfg = build_cfg("purge-zero", 0, 0);
    let case_id = "11111111-1111-1111-1111-111111111111";
    let case_dir = seed_case(&cfg.data_path, "dr_a", case_id);
    seed_recording_with_age(&case_dir, "2026-04-01T10-00-00Z", Duration::from_secs(60));
    write_closed_marker(&case_dir, &past_rfc3339(Duration::from_secs(365 * 86400)));

    let app = doctate_server::create_router(cfg);
    let cookie = common::login(&app, "dr_a", "s").await;

    trigger_sweep(&app, &cookie).await;
    assert!(
        case_dir.exists(),
        "closed case must survive when auto_delete_days = 0"
    );
    assert!(case_dir.join(CLOSE_MARKER).exists());
}

#[tokio::test]
async fn fresh_auto_close_is_not_immediately_purged() {
    // Vacation scenario: user returns after 40 days offline.
    // auto_close_days = 7, auto_delete_days = 30.
    // The sweep auto-closes (step 1, closed_at ≈ now), but the purge
    // check (step 2) sees closed_at ≈ now and skips — the user gets
    // the full 30-day grace period, nothing disappears silently.
    let cfg = build_cfg("vacation", 7, 30);
    let case_id = "11111111-1111-1111-1111-111111111111";
    let case_dir = seed_case(&cfg.data_path, "dr_a", case_id);
    seed_recording_with_age(
        &case_dir,
        "2026-04-01T10-00-00Z",
        Duration::from_secs(40 * 86400),
    );

    let app = doctate_server::create_router(cfg);
    let cookie = common::login(&app, "dr_a", "s").await;

    trigger_sweep(&app, &cookie).await;
    assert!(
        case_dir.exists(),
        "vacation: case directory must NOT be purged in the same sweep that auto-closed it"
    );
    assert!(
        case_dir.join(CLOSE_MARKER).exists(),
        "vacation: case must be auto-closed (but retained)"
    );
}

#[tokio::test]
async fn auto_purge_skips_open_cases() {
    let cfg = build_cfg("purge-skip-open", 0, 30);
    let case_id = "11111111-1111-1111-1111-111111111111";
    let case_dir = seed_case(&cfg.data_path, "dr_a", case_id);
    seed_recording_with_age(&case_dir, "2026-04-01T10-00-00Z", Duration::from_secs(60));

    let app = doctate_server::create_router(cfg);
    let cookie = common::login(&app, "dr_a", "s").await;

    trigger_sweep(&app, &cookie).await;
    assert!(
        case_dir.exists(),
        "open case without .closed marker must never be purged"
    );
}
