import { request, type Page } from './client';

export type Chapter = {
    id: string;
    manga_id: string;
    number: number;
    title: string | null;
    page_count: number;
    created_at: string;
    /** Total bytes of this chapter's stored pages. `0` for an uncrawled
     * chapter; `null` when the size is unknown (a page predates the size
     * backfill). Render both `null` and uncrawled as an em-dash. */
    size_bytes: number | null;
};

export type ChaptersPage = {
    items: Chapter[];
    page: Page;
};

export function chapterLabel(c: Pick<Chapter, 'number' | 'title'>): string {
    return c.title ?? `Chapter ${c.number}`;
}

export type ListOptions = {
    limit?: number;
    offset?: number;
};

export async function listChapters(
    mangaId: string,
    opts: ListOptions = {}
): Promise<ChaptersPage> {
    const params = new URLSearchParams();
    if (opts.limit != null) params.set('limit', String(opts.limit));
    if (opts.offset != null) params.set('offset', String(opts.offset));
    const qs = params.toString();
    return request<ChaptersPage>(
        `/v1/mangas/${encodeURIComponent(mangaId)}/chapters${qs ? `?${qs}` : ''}`
    );
}

export async function getChapter(mangaId: string, chapterId: string): Promise<Chapter> {
    return request<Chapter>(
        `/v1/mangas/${encodeURIComponent(mangaId)}/chapters/${encodeURIComponent(chapterId)}`
    );
}

export type ChapterPage = {
    id: string;
    chapter_id: string;
    page_number: number;
    storage_key: string;
    content_type: string;
};

export async function getChapterPages(
    mangaId: string,
    chapterId: string
): Promise<ChapterPage[]> {
    const r = await request<{ pages: ChapterPage[] }>(
        `/v1/mangas/${encodeURIComponent(mangaId)}/chapters/${encodeURIComponent(chapterId)}/pages`
    );
    return r.pages;
}

export type NewChapter = {
    number: number;
    title?: string | null;
};

/**
 * `POST /api/v1/mangas/:id/chapters` is multipart: a `metadata` part
 * (JSON) plus one or more ordered `page` parts. Each page file is
 * renamed to `page-NNN.<ext>` before submission so the user's
 * original filenames (often personally-identifying or just messy:
 * `IMG_2837.HEIC`, `~/scans/full chapter pack/`) don't end up in
 * request bodies or server logs. The bytes are unchanged — the
 * backend still sniffs the MIME from magic bytes and stores under
 * its own `{nnnn}.{ext}` scheme.
 */
export async function createChapter(
    mangaId: string,
    metadata: NewChapter,
    pages: File[]
): Promise<Chapter> {
    const form = new FormData();
    form.append(
        'metadata',
        new Blob([JSON.stringify(metadata)], { type: 'application/json' })
    );
    pages.forEach((file, i) => {
        const ext = extensionFor(file);
        const renamed = new File(
            [file],
            `page-${String(i + 1).padStart(3, '0')}${ext}`,
            { type: file.type }
        );
        form.append('page', renamed);
    });
    return request<Chapter>(
        `/v1/mangas/${encodeURIComponent(mangaId)}/chapters`,
        { method: 'POST', body: form }
    );
}

/**
 * Pick a sensible extension for the renamed multipart part. Prefer
 * the original filename's extension when present (jpg/jpeg/png/webp/
 * gif/avif), otherwise derive from the MIME type. Falls back to an
 * empty string so the renamed file is just `page-001` — the
 * server sniffs bytes anyway.
 */
function extensionFor(file: File): string {
    const dot = file.name.lastIndexOf('.');
    if (dot > 0) {
        const ext = file.name.slice(dot).toLowerCase();
        if (/^\.(jpe?g|png|webp|gif|avif)$/.test(ext)) return ext;
    }
    const fromMime: Record<string, string> = {
        'image/jpeg': '.jpg',
        'image/png': '.png',
        'image/webp': '.webp',
        'image/gif': '.gif',
        'image/avif': '.avif'
    };
    return fromMime[file.type] ?? '';
}
