//! Listen-relevante Mutationen (Close, Reopen, Bulk-Close) müssen den
//! pro-User Watermark bumpen. Sonst bleibt der ETag von
//! `/api/oneliners` stehen, jeder Poll liefert 304, und der Client
//! läuft dauerhaft mit einem Marker für einen Fall, den der Server
//! bereits geschlossen hat (ursprünglicher Bugreport: "Polymyalgia
//! rheumatica" blieb im Client sichtbar, obwohl im Web-UI bereits
//! gelöscht).

mod common;

use std::path::Path;

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

use common::{
    TestConfig, csrf_form_post, get_with_api_key, get_with_api_key_if_none_match, header_opt,
    login_with_csrf, paths, test_admin, test_user,
};

/// Create `<data_path>/<slug>/<case_id>/<ts>.m4a` so the case is
/// visible to `enumerate_recent_oneliners` (window defaults to 72h;
/// the fake mtime is "now" because write happens right now).
fn seed_case_with_recording(data_path: &Path, slug: &str, case_id: &str) {
    let case_dir = common::seed_case(data_path, slug, case_id);
    std::fs::write(case_dir.join("2026-04-19T10-00-00Z.m4a"), b"fake audio").unwrap();
}

async fn capture_etag(
    app: &axum::Router,
    api_key: &str,
    if_none_match: Option<&str>,
) -> (StatusCode, Option<String>) {
    let req = match if_none_match {
        Some(tag) => get_with_api_key_if_none_match(paths::ONELINERS_PATH, api_key, tag),
        None => get_with_api_key(paths::ONELINERS_PATH, api_key),
    };
    let resp = app.clone().oneshot(req).await.unwrap();
    let status = resp.status();
    let etag = header_opt(&resp, "etag").map(str::to_owned);
    (status, etag)
}

#[tokio::test]
async fn close_bumps_watermark() {
    let cfg = TestConfig::new().with_user(test_user("dr_a")).build();
    let case_id = "550e8400-e29b-41d4-a716-446655440000";
    seed_case_with_recording(&cfg.data_path, "dr_a", case_id);

    let (app, store) = doctate_server::create_router_and_session_store(cfg);
    let (cookie, csrf) = login_with_csrf(&app, &store, "dr_a", "s").await;

    // First GET: capture baseline ETag (watermark empty → seeded on 0).
    let (s1, etag_before) = capture_etag(&app, "key-dr_a", None).await;
    assert_eq!(s1, StatusCode::OK);
    let etag_before = etag_before.expect("server must set ETag");

    // Close via the web handler.
    let del = app
        .clone()
        .oneshot(csrf_form_post(
            paths::case_close(case_id),
            &cookie,
            &csrf,
            "",
        ))
        .await
        .unwrap();
    assert_eq!(
        del.status().as_u16() / 100,
        3,
        "close should redirect (3xx)"
    );

    // Same If-None-Match now must NOT match — watermark was bumped.
    let (s2, etag_after) = capture_etag(&app, "key-dr_a", Some(&etag_before)).await;
    assert_eq!(
        s2,
        StatusCode::OK,
        "watermark bump must invalidate cached ETag",
    );
    let etag_after = etag_after.expect("200 response must set ETag");
    assert_ne!(
        etag_after, etag_before,
        "close handler must change the per-user ETag"
    );
}

#[tokio::test]
async fn reopen_bumps_watermark() {
    let cfg = TestConfig::new().with_user(test_user("dr_a")).build();
    let case_id = "660e8400-e29b-41d4-a716-446655440000";
    seed_case_with_recording(&cfg.data_path, "dr_a", case_id);

    let (app, store) = doctate_server::create_router_and_session_store(cfg);
    let (cookie, csrf) = login_with_csrf(&app, &store, "dr_a", "s").await;

    // Close first so reopen has something to restore; grab the post-close ETag.
    app.clone()
        .oneshot(csrf_form_post(
            paths::case_close(case_id),
            &cookie,
            &csrf,
            "",
        ))
        .await
        .unwrap();
    let (_, etag_after_close) = capture_etag(&app, "key-dr_a", None).await;
    let etag_after_close = etag_after_close.expect("ETag must be set");

    // Reopen.
    let reopen = app
        .clone()
        .oneshot(csrf_form_post(
            paths::case_reopen(case_id),
            &cookie,
            &csrf,
            "",
        ))
        .await
        .unwrap();
    assert_eq!(reopen.status().as_u16() / 100, 3, "reopen should redirect");

    // Reopen must bump again → post-close ETag becomes stale.
    let (s, etag_after_reopen) = capture_etag(&app, "key-dr_a", Some(&etag_after_close)).await;
    assert_eq!(
        s,
        StatusCode::OK,
        "reopen must change the ETag so clients re-fetch"
    );
    let etag_after_reopen = etag_after_reopen.expect("200 response must set ETag");
    assert_ne!(etag_after_reopen, etag_after_close);
}

#[tokio::test]
async fn bulk_close_bumps_watermark() {
    // Bulk actions are admin-only: UI hides them from non-admins and
    // the handler rejects non-admin POSTs with 403.
    let cfg = TestConfig::new().with_user(test_admin("dr_a")).build();
    let case_a = "770e8400-e29b-41d4-a716-446655440000";
    let case_b = "880e8400-e29b-41d4-a716-446655440000";
    seed_case_with_recording(&cfg.data_path, "dr_a", case_a);
    seed_case_with_recording(&cfg.data_path, "dr_a", case_b);

    let (app, store) = doctate_server::create_router_and_session_store(cfg);
    let (cookie, csrf) = login_with_csrf(&app, &store, "dr_a", "s").await;

    let (_, etag_before) = capture_etag(&app, "key-dr_a", None).await;
    let etag_before = etag_before.expect("ETag must be set");

    // Form-encoded bulk close: action=close&case_id=a&case_id=b.
    let body = format!(
        "action={}&case_id={case_a}&case_id={case_b}",
        BulkAction::Close
    );
    let bulk = app
        .clone()
        .oneshot(csrf_form_post(paths::BULK, &cookie, &csrf, &body))
        .await
        .unwrap();
    assert_eq!(bulk.status().as_u16() / 100, 3, "bulk should redirect");

    let (s, etag_after) = capture_etag(&app, "key-dr_a", Some(&etag_before)).await;
    assert_eq!(s, StatusCode::OK, "bulk-close must bump the ETag");
    let etag_after = etag_after.expect("200 response must set ETag");
    assert_ne!(etag_after, etag_before);
}
