//! Integration tests for the admin overview aggregates endpoint
//! (`GET /api/v1/admin/overview`). Auth gating mirrors the system tab;
//! the aggregate counts are asserted against seeded data.

mod common;

use axum::http::StatusCode;
use axum::Router;
use serde_json::json;
use sqlx::PgPool;
use tower::ServiceExt;

use mangalord::repo;

async fn seed_admin(pool: &PgPool, app: &Router) -> String {
    let (username, cookie) = common::register_user(app).await;
    let u = repo::user::find_by_username(pool, &username)
        .await
        .unwrap()
        .unwrap();
    repo::user::set_is_admin_unchecked(pool, u.id, true).await.unwrap();
    cookie
}

/// Seed a manga → chapter with `n` pages; mark the first `analyzed` of
/// them as a `done` page_analysis. Returns the page ids.
async fn seed_manga_pages(pool: &PgPool, title: &str, n: i32, analyzed: i32) -> Vec<uuid::Uuid> {
    let manga_id: uuid::Uuid =
        sqlx::query_scalar("INSERT INTO mangas (title) VALUES ($1) RETURNING id")
            .bind(title)
            .fetch_one(pool)
            .await
            .unwrap();
    let chapter_id: uuid::Uuid =
        sqlx::query_scalar("INSERT INTO chapters (manga_id, number) VALUES ($1, 1) RETURNING id")
            .bind(manga_id)
            .fetch_one(pool)
            .await
            .unwrap();
    let mut ids = Vec::new();
    for i in 1..=n {
        let id: uuid::Uuid = sqlx::query_scalar(
            "INSERT INTO pages (chapter_id, page_number, storage_key, content_type) \
             VALUES ($1, $2, $3, 'image/png') RETURNING id",
        )
        .bind(chapter_id)
        .bind(i)
        .bind(format!("k/{manga_id}/{i}.png"))
        .fetch_one(pool)
        .await
        .unwrap();
        if i <= analyzed {
            sqlx::query("INSERT INTO page_analysis (page_id, status) VALUES ($1, 'done')")
                .bind(id)
                .execute(pool)
                .await
                .unwrap();
        }
        ids.push(id);
    }
    ids
}

#[sqlx::test(migrations = "./migrations")]
async fn requires_admin(pool: PgPool) {
    let h = common::harness(pool);
    let (_u, cookie) = common::register_user(&h.app).await;
    let resp = h
        .app
        .oneshot(common::get_with_cookie("/api/v1/admin/overview", &cookie))
        .await
        .unwrap();
    assert_eq!(resp.status(), StatusCode::FORBIDDEN);
}

#[sqlx::test(migrations = "./migrations")]
async fn unauthenticated_request_is_rejected(pool: PgPool) {
    let h = common::harness(pool);
    let resp = h
        .app
        .oneshot(common::get("/api/v1/admin/overview"))
        .await
        .unwrap();
    assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
}

#[sqlx::test(migrations = "./migrations")]
async fn aggregates_users_mangas_and_coverage(pool: PgPool) {
    let h = common::harness(pool.clone());
    // seed_admin registers one user and promotes it; register a second
    // (non-admin) user so the counts have a known split.
    let cookie = seed_admin(&pool, &h.app).await;
    let _ = common::register_user(&h.app).await;

    // Two mangas: 3 pages with 2 analyzed, and 2 pages with 0 analyzed.
    seed_manga_pages(&pool, "Alpha", 3, 2).await;
    seed_manga_pages(&pool, "Beta", 2, 0).await;

    let resp = h
        .app
        .oneshot(common::get_with_cookie("/api/v1/admin/overview", &cookie))
        .await
        .unwrap();
    assert_eq!(resp.status(), StatusCode::OK);
    let body = common::body_json(resp).await;

    let users = body.get("users").expect("users key");
    assert_eq!(users["total"], json!(2));
    assert_eq!(users["admins"], json!(1));
    assert!(users["newest_username"].is_string());

    let mangas = body.get("mangas").expect("mangas key");
    assert_eq!(mangas["total"], json!(2));
    // Uploaded mangas (no manga_sources) classify as synced.
    assert_eq!(mangas["synced"], json!(2));
    assert_eq!(mangas["in_progress"], json!(0));
    assert_eq!(mangas["dropped"], json!(0));
    assert_eq!(mangas["total_chapters"], json!(2));
    assert_eq!(mangas["total_pages"], json!(5));
    assert!(mangas["newest_title"].is_string());

    let analysis = body.get("analysis").expect("analysis key");
    assert_eq!(analysis["total_pages"], json!(5));
    assert_eq!(analysis["analyzed_pages"], json!(2));
}
