<script lang="ts">
    import { goto } from '$app/navigation';
    import { ApiError } from '$lib/api/client';
    import { createManga, type MangaStatus } from '$lib/api/mangas';
    import { createChapter } from '$lib/api/chapters';
    import { session } from '$lib/session.svelte';
    import { formatBytes, validateImageFile } from '$lib/upload-validation';
    import Chip from '$lib/components/Chip.svelte';
    import ChapterPagesEditor, {
        type PendingPage
    } from '$lib/components/ChapterPagesEditor.svelte';
    import Plus from '@lucide/svelte/icons/plus';
    import Trash2 from '@lucide/svelte/icons/trash-2';

    let { data } = $props();
    const genres = $derived(data.genres);

    // ---- Manga form state ----
    let mangaTitle = $state('');
    let mangaStatus = $state<MangaStatus>('ongoing');
    let mangaDescription = $state('');
    let mangaAuthors = $state<string[]>([]);
    let authorDraft = $state('');
    let mangaAltTitles = $state<string[]>([]);
    let altTitleDraft = $state('');
    let mangaGenreIds = $state<string[]>([]);
    let coverFile = $state<File | null>(null);
    let coverError = $state<string | null>(null);

    // ---- Initial-chapter staging ----
    type StagedChapter = {
        id: string;
        number: number;
        title: string;
        pages: PendingPage[];
        status: 'pending' | 'uploading' | 'done' | 'failed';
        error: string | null;
    };
    let stagedChapters = $state<StagedChapter[]>([]);

    let submitting = $state(false);
    let mangaError = $state<string | null>(null);
    let success = $state<string | null>(null);

    const allChapterPagesValid = $derived(
        stagedChapters.every((c) => c.pages.every((p) => !p.error))
    );
    const allChapterNumbersUnique = $derived(
        new Set(stagedChapters.map((c) => c.number)).size === stagedChapters.length
    );
    const allChapterNumbersValid = $derived(
        stagedChapters.every((c) => Number.isInteger(c.number) && c.number >= 1)
    );
    const allChaptersHavePages = $derived(
        stagedChapters.every((c) => c.pages.length > 0)
    );
    const canSubmit = $derived(
        mangaTitle.trim().length > 0 &&
            !coverError &&
            !submitting &&
            allChapterPagesValid &&
            allChapterNumbersUnique &&
            allChapterNumbersValid &&
            allChaptersHavePages
    );

    function addAuthor() {
        const name = authorDraft.trim();
        if (!name) return;
        if (!mangaAuthors.some((a) => a.toLowerCase() === name.toLowerCase())) {
            mangaAuthors = [...mangaAuthors, name];
        }
        authorDraft = '';
    }
    function removeAuthor(name: string) {
        mangaAuthors = mangaAuthors.filter((a) => a !== name);
    }
    function addAltTitle() {
        const t = altTitleDraft.trim();
        if (!t) return;
        if (!mangaAltTitles.includes(t)) {
            mangaAltTitles = [...mangaAltTitles, t];
        }
        altTitleDraft = '';
    }
    function removeAltTitle(t: string) {
        mangaAltTitles = mangaAltTitles.filter((x) => x !== t);
    }
    function toggleGenre(id: string) {
        mangaGenreIds = mangaGenreIds.includes(id)
            ? mangaGenreIds.filter((g) => g !== id)
            : [...mangaGenreIds, id];
    }
    function onCoverChange(e: Event) {
        const input = e.target as HTMLInputElement;
        const file = input.files?.[0] ?? null;
        coverFile = file;
        coverError = file ? validateImageFile(file) : null;
    }

    function addChapter() {
        // Auto-default to the next number after the highest pending
        // one (or 1 if this is the first).
        const next =
            stagedChapters.reduce((max, c) => Math.max(max, c.number), 0) + 1;
        stagedChapters = [
            ...stagedChapters,
            {
                id: crypto.randomUUID(),
                number: next,
                title: '',
                pages: [],
                status: 'pending',
                error: null
            }
        ];
    }
    function removeChapter(id: string) {
        // Releasing the staged-chapter row drops the ChapterPagesEditor,
        // which revokes its own object URLs on destroy — no leak.
        stagedChapters = stagedChapters.filter((c) => c.id !== id);
    }

    async function submit(e: SubmitEvent) {
        e.preventDefault();
        if (!canSubmit) return;
        if (authorDraft.trim()) addAuthor();
        if (altTitleDraft.trim()) addAltTitle();
        submitting = true;
        mangaError = null;
        success = null;
        let manga;
        try {
            manga = await createManga(
                {
                    title: mangaTitle.trim(),
                    status: mangaStatus,
                    authors: mangaAuthors,
                    alt_titles: mangaAltTitles,
                    genre_ids: mangaGenreIds,
                    description: mangaDescription.trim() || null
                },
                coverFile ?? undefined
            );
        } catch (e) {
            if (e instanceof ApiError && e.status === 401) {
                await goto('/login?next=/upload');
                return;
            }
            mangaError = e instanceof Error ? e.message : String(e);
            submitting = false;
            return;
        }

        // Manga is created; ship chapters one at a time and surface
        // per-row status. Failures don't roll back the manga — the
        // user can retry just the failed chapters from the manga
        // page's Upload-chapter button.
        for (const c of stagedChapters) {
            c.status = 'uploading';
            c.error = null;
            try {
                await createChapter(
                    manga.id,
                    { number: c.number, title: c.title.trim() || null },
                    c.pages.map((p) => p.file)
                );
                c.status = 'done';
            } catch (e) {
                c.status = 'failed';
                c.error = e instanceof Error ? e.message : String(e);
            }
        }

        const failed = stagedChapters.filter((c) => c.status === 'failed');
        if (failed.length === 0) {
            // All-good — land the user on the manga page where they
            // can confirm and continue uploading.
            await goto(`/manga/${manga.id}`);
            return;
        }
        success = `"${manga.title}" was created, but ${failed.length} of ${stagedChapters.length} chapters failed. Fix them and retry from the manga page.`;
        submitting = false;
    }
</script>

<svelte:head>
    <title>Upload — Mangalord</title>
</svelte:head>

<h1>Create manga</h1>

{#if !session.loaded}
    <p class="status" data-testid="upload-loading">Loading…</p>
{:else if !session.user}
    <p class="status" data-testid="upload-signin">
        <a href="/login?next=/upload">Sign in</a> to upload a manga.
    </p>
{:else}
    <form onsubmit={submit} action="javascript:void(0)" data-testid="manga-form">
        <section class="card">
            <h2>Manga details</h2>
            <label class="form-field">
                <span>Title <span aria-hidden="true">*</span></span>
                <input
                    type="text"
                    bind:value={mangaTitle}
                    required
                    maxlength="200"
                    data-testid="manga-title"
                />
            </label>

            <label class="form-field">
                <span>Status</span>
                <select bind:value={mangaStatus} data-testid="manga-status">
                    <option value="ongoing">Ongoing</option>
                    <option value="completed">Completed</option>
                </select>
            </label>

            <div class="form-field">
                <span>Authors</span>
                <div class="token-row">
                    {#each mangaAuthors as a (a)}
                        <Chip label={a} variant="primary" onRemove={() => removeAuthor(a)} />
                    {/each}
                </div>
                <div class="token-input-row">
                    <input
                        type="text"
                        bind:value={authorDraft}
                        onkeydown={(e) => {
                            if (e.key === 'Enter') {
                                e.preventDefault();
                                addAuthor();
                            }
                        }}
                        placeholder="Add author"
                        maxlength="200"
                        data-testid="manga-author-input"
                    />
                    <button
                        type="button"
                        class="icon-btn primary"
                        onclick={addAuthor}
                        disabled={!authorDraft.trim()}
                        aria-label="Add author"
                        title="Add author"
                    >
                        <Plus size={16} aria-hidden="true" />
                    </button>
                </div>
            </div>

            <div class="form-field">
                <span>Genres</span>
                <div class="genre-grid" data-testid="manga-genres">
                    {#each genres as g (g.id)}
                        <label class="genre-toggle">
                            <input
                                type="checkbox"
                                checked={mangaGenreIds.includes(g.id)}
                                onchange={() => toggleGenre(g.id)}
                            />
                            <span>{g.name}</span>
                        </label>
                    {/each}
                </div>
            </div>

            <div class="form-field">
                <span>Alternative titles</span>
                <div class="token-row">
                    {#each mangaAltTitles as t (t)}
                        <Chip label={t} onRemove={() => removeAltTitle(t)} />
                    {/each}
                </div>
                <div class="token-input-row">
                    <input
                        type="text"
                        bind:value={altTitleDraft}
                        onkeydown={(e) => {
                            if (e.key === 'Enter') {
                                e.preventDefault();
                                addAltTitle();
                            }
                        }}
                        placeholder="Add alternative title"
                        maxlength="200"
                        data-testid="manga-alt-input"
                    />
                    <button
                        type="button"
                        class="icon-btn primary"
                        onclick={addAltTitle}
                        disabled={!altTitleDraft.trim()}
                        aria-label="Add alternative title"
                        title="Add alternative title"
                    >
                        <Plus size={16} aria-hidden="true" />
                    </button>
                </div>
            </div>

            <label class="form-field">
                <span>Description</span>
                <textarea
                    bind:value={mangaDescription}
                    rows="4"
                    data-testid="manga-description"
                ></textarea>
            </label>
            <label class="form-field">
                <span>Cover (optional)</span>
                <input
                    type="file"
                    accept="image/*"
                    onchange={onCoverChange}
                    data-testid="manga-cover"
                />
                {#if coverFile}
                    <span class="hint">{coverFile.name} ({formatBytes(coverFile.size)})</span>
                {/if}
                {#if coverError}
                    <span class="field-error" role="alert">{coverError}</span>
                {/if}
            </label>
        </section>

        <section class="card">
            <div class="chapters-header">
                <h2>Initial chapters (optional)</h2>
                <button
                    type="button"
                    class="add-chapter"
                    onclick={addChapter}
                    data-testid="add-chapter"
                >
                    <Plus size={14} aria-hidden="true" />
                    <span>Add chapter</span>
                </button>
            </div>
            {#if stagedChapters.length === 0}
                <p class="hint" data-testid="no-chapters-hint">
                    You can skip this — chapters can also be added later from the
                    manga's page.
                </p>
            {:else}
                <ul class="chapter-list" data-testid="staged-chapter-list">
                    {#each stagedChapters as c, idx (c.id)}
                        <li class="staged-chapter" data-testid="staged-chapter">
                            <div class="staged-header">
                                <span class="staged-index">#{idx + 1}</span>
                                <label class="staged-field number-field">
                                    <span class="visually-hidden">Chapter number</span>
                                    <input
                                        type="number"
                                        min="1"
                                        step="1"
                                        bind:value={c.number}
                                        required
                                        data-testid="staged-chapter-number"
                                    />
                                </label>
                                <label class="staged-field title-field">
                                    <span class="visually-hidden">Chapter title</span>
                                    <input
                                        type="text"
                                        placeholder="Chapter title (optional)"
                                        bind:value={c.title}
                                        maxlength="200"
                                        data-testid="staged-chapter-title"
                                    />
                                </label>
                                <span class="staged-status status-{c.status}">
                                    {#if c.status === 'uploading'}
                                        Uploading…
                                    {:else if c.status === 'done'}
                                        ✓ Uploaded
                                    {:else if c.status === 'failed'}
                                        Failed
                                    {/if}
                                </span>
                                <button
                                    type="button"
                                    class="icon-btn danger"
                                    onclick={() => removeChapter(c.id)}
                                    aria-label="Remove chapter"
                                    title="Remove chapter"
                                    data-testid="staged-chapter-remove"
                                >
                                    <Trash2 size={16} aria-hidden="true" />
                                </button>
                            </div>
                            {#if c.error}
                                <p class="field-error" role="alert">{c.error}</p>
                            {/if}
                            <ChapterPagesEditor
                                bind:pages={c.pages}
                                testidPrefix="staged-chapter-pages"
                            />
                        </li>
                    {/each}
                </ul>
                {#if !allChapterNumbersUnique}
                    <p class="field-error" role="alert">
                        Two staged chapters share the same number — each must
                        be unique.
                    </p>
                {/if}
                {#if !allChaptersHavePages && stagedChapters.length > 0}
                    <p class="field-error" role="alert">
                        Each staged chapter needs at least one page.
                    </p>
                {/if}
            {/if}
        </section>

        <button
            class="primary"
            type="submit"
            disabled={!canSubmit}
            data-testid="manga-submit"
        >
            {submitting ? 'Submitting…' : 'Create manga'}
        </button>
        {#if success}
            <p class="success" data-testid="manga-success">{success}</p>
        {/if}
        {#if mangaError}
            <p role="alert" class="form-error" data-testid="manga-error">{mangaError}</p>
        {/if}
    </form>
{/if}

<style>
    .status {
        color: var(--text-muted);
    }

    form {
        display: flex;
        flex-direction: column;
        gap: var(--space-4);
    }

    .card {
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: var(--radius-md);
        padding: var(--space-4);
        display: flex;
        flex-direction: column;
        gap: var(--space-3);
    }

    .primary {
        background: var(--primary);
        color: var(--primary-contrast);
        border-color: var(--primary);
        align-self: flex-start;
    }

    .primary:hover:not(:disabled) {
        background: var(--primary-hover);
        border-color: var(--primary-hover);
    }

    .hint {
        color: var(--text-muted);
        font-size: var(--font-sm);
    }

    .field-error {
        color: var(--danger);
        font-size: var(--font-sm);
    }

    .form-error {
        color: var(--danger);
    }

    .success {
        color: var(--success);
    }

    .token-row {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-2);
        margin-bottom: var(--space-1);
    }

    .token-input-row {
        display: flex;
        gap: var(--space-2);
    }

    .token-input-row input {
        flex: 1;
        min-width: 0;
    }

    .genre-grid {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
        gap: var(--space-2);
    }

    .genre-toggle {
        display: inline-flex;
        align-items: center;
        gap: var(--space-2);
        color: var(--text);
        font-size: var(--font-sm);
        cursor: pointer;
    }

    .icon-btn {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 32px;
        height: 32px;
        padding: 0;
        background: transparent;
        color: var(--text-muted);
        border: 1px solid transparent;
        border-radius: var(--radius-sm);
    }

    .icon-btn:hover:not(:disabled) {
        background: var(--surface-elevated);
        color: var(--text);
    }

    .icon-btn.primary {
        background: var(--primary);
        color: var(--primary-contrast);
        border-color: var(--primary);
    }

    .icon-btn.primary:hover:not(:disabled) {
        background: var(--primary-hover);
        border-color: var(--primary-hover);
    }

    .icon-btn.danger:hover:not(:disabled) {
        color: var(--danger);
    }

    .chapters-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: var(--space-3);
    }

    .chapters-header h2 {
        margin: 0;
    }

    .add-chapter {
        display: inline-flex;
        align-items: center;
        gap: var(--space-1);
        background: var(--primary);
        color: var(--primary-contrast);
        border: 1px solid var(--primary);
        padding: 0 var(--space-3);
        height: 32px;
        border-radius: var(--radius-sm);
        font-size: var(--font-sm);
        cursor: pointer;
    }

    .add-chapter:hover {
        background: var(--primary-hover);
        border-color: var(--primary-hover);
    }

    .chapter-list {
        list-style: none;
        padding: 0;
        margin: 0;
        display: flex;
        flex-direction: column;
        gap: var(--space-3);
    }

    .staged-chapter {
        background: var(--surface-elevated);
        border: 1px solid var(--border);
        border-radius: var(--radius-md);
        padding: var(--space-3);
        display: flex;
        flex-direction: column;
        gap: var(--space-2);
    }

    .staged-header {
        display: grid;
        grid-template-columns: auto 80px 1fr auto auto;
        gap: var(--space-2);
        align-items: center;
    }

    .staged-index {
        color: var(--text-muted);
        font-size: var(--font-sm);
        font-weight: var(--weight-semibold);
        white-space: nowrap;
    }

    .staged-field {
        display: contents;
    }

    .staged-status {
        font-size: var(--font-sm);
        color: var(--text-muted);
        white-space: nowrap;
    }

    .staged-status.status-done {
        color: var(--success);
    }

    .staged-status.status-failed {
        color: var(--danger);
    }

    .visually-hidden {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
        border: 0;
    }
</style>
