//! Cross-table upload history.
//!
//! Mangas and chapters are uploaded by users separately, but the
//! profile UI wants a single chronological feed. Rather than open a
//! UNION-ALL over two tables with mismatched columns we fetch each
//! side, then merge in Rust by `created_at`. Cheap for the volumes a
//! single user produces.
//!
//! Pagination uses limit-only for now; offsets across two unrelated
//! tables aren't trivially stable, and the realistic per-user upload
//! count is small. Switch to keyset pagination if real users blow
//! past a few hundred uploads.

use sqlx::PgPool;
use uuid::Uuid;

use crate::domain::chapter::Chapter;
use crate::domain::manga::Manga;
use crate::domain::upload_entry::UploadEntry;
use crate::error::AppResult;

#[derive(sqlx::FromRow)]
struct ChapterUploadRow {
    manga_id: Uuid,
    manga_title: String,
    manga_cover_image_path: Option<String>,
    chapter_id: Uuid,
    number: i32,
    title: Option<String>,
    page_count: i32,
    created_at: chrono::DateTime<chrono::Utc>,
    size_bytes: Option<i64>,
}

/// Returns up to `limit` of the user's most recent uploads (mangas and
/// chapters interleaved by `created_at DESC`) plus the unfiltered
/// total count (mangas + chapters owned by the user). The caller is
/// responsible for clamping `limit` to a sane value.
pub async fn list_for_user(
    pool: &PgPool,
    user_id: Uuid,
    limit: i64,
) -> AppResult<(Vec<UploadEntry>, i64)> {
    let mangas: Vec<Manga> = sqlx::query_as::<_, Manga>(
        r#"
        SELECT id, title, status, alt_titles, description,
               cover_image_path, created_at, updated_at
          FROM mangas
         WHERE uploaded_by = $1
         ORDER BY created_at DESC, id
         LIMIT $2
        "#,
    )
    .bind(user_id)
    .bind(limit)
    .fetch_all(pool)
    .await?;

    let chapters: Vec<ChapterUploadRow> = sqlx::query_as::<_, ChapterUploadRow>(
        r#"
        SELECT c.manga_id,
               m.title             AS manga_title,
               m.cover_image_path  AS manga_cover_image_path,
               c.id                AS chapter_id,
               c.number,
               c.title,
               c.page_count,
               c.created_at,
               (SELECT CASE
                         WHEN count(*) = 0 THEN 0
                         WHEN bool_or(p.size_bytes IS NULL) THEN NULL
                         ELSE sum(p.size_bytes)
                       END
                  FROM pages p WHERE p.chapter_id = c.id)::bigint AS size_bytes
          FROM chapters c
          JOIN mangas m ON m.id = c.manga_id
         WHERE c.uploaded_by = $1
         ORDER BY c.created_at DESC, c.id
         LIMIT $2
        "#,
    )
    .bind(user_id)
    .bind(limit)
    .fetch_all(pool)
    .await?;

    let mut entries: Vec<UploadEntry> = Vec::with_capacity(mangas.len() + chapters.len());
    for m in mangas {
        entries.push(UploadEntry::Manga {
            created_at: m.created_at,
            manga: m,
        });
    }
    for c in chapters {
        let created_at = c.created_at;
        entries.push(UploadEntry::Chapter {
            manga_id: c.manga_id,
            manga_title: c.manga_title,
            manga_cover_image_path: c.manga_cover_image_path,
            chapter: Chapter {
                id: c.chapter_id,
                manga_id: c.manga_id,
                number: c.number,
                title: c.title,
                page_count: c.page_count,
                created_at: c.created_at,
                size_bytes: c.size_bytes,
            },
            created_at,
        });
    }
    // Newest first; trim to limit after the merge.
    entries.sort_by_key(|b| std::cmp::Reverse(b.created_at()));
    entries.truncate(limit as usize);

    let (manga_total, chapter_total): (i64, i64) = sqlx::query_as(
        r#"
        SELECT
            (SELECT count(*) FROM mangas WHERE uploaded_by = $1),
            (SELECT count(*) FROM chapters WHERE uploaded_by = $1)
        "#,
    )
    .bind(user_id)
    .fetch_one(pool)
    .await?;
    Ok((entries, manga_total + chapter_total))
}
