<script lang="ts">
    import { goto } from '$app/navigation';
    import {
        deleteCollection,
        removeMangaFromCollection,
        updateCollection
    } from '$lib/api/collections';
    import {
        removePageFromCollection,
        type CollectionPageItem
    } from '$lib/api/page_collections';
    import type { Manga } from '$lib/api/client';
    import { fileUrl } from '$lib/api/client';
    import MangaCard from '$lib/components/MangaCard.svelte';
    import MangaGridSkeleton from '$lib/components/MangaGridSkeleton.svelte';
    import IconButton from '$lib/components/IconButton.svelte';
    import ArrowLeft from '@lucide/svelte/icons/arrow-left';
    import Pencil from '@lucide/svelte/icons/pencil';
    import Check from '@lucide/svelte/icons/check';
    import Trash2 from '@lucide/svelte/icons/trash-2';
    import X from '@lucide/svelte/icons/x';

    let { data } = $props();
    // svelte-ignore state_referenced_locally
    let collection = $state({ ...data.collection });
    // The manga/page lists are streamed and held as local mutable state so
    // removals can be optimistic. They're seeded once the stream resolves; a
    // skeleton shows until then.
    let mangas = $state<Manga[]>([]);
    let pages = $state<CollectionPageItem[]>([]);
    let contentLoading = $state(true);
    // Set when the streamed content fails to load, so we surface an inline
    // error instead of a misleading "this collection is empty".
    let contentError = $state<string | null>(null);

    // Seed (and re-seed on navigation) from the streamed content. A monotonic
    // guard drops a stale resolution that lands after a newer navigation.
    let contentSeq = 0;
    $effect(() => {
        const content = data.content;
        const seq = ++contentSeq;
        contentLoading = true;
        contentError = null;
        content
            .then((r) => {
                if (seq !== contentSeq) return;
                mangas = r.mangas;
                pages = r.pages;
                contentLoading = false;
            })
            .catch((e) => {
                if (seq !== contentSeq) return;
                mangas = [];
                pages = [];
                contentError = e instanceof Error ? e.message : 'Something went wrong.';
                contentLoading = false;
            });
    });

    let editing = $state(false);
    let editName = $state('');
    let editDescription = $state('');
    let editError: string | null = $state(null);
    let editBusy = $state(false);

    function startEdit() {
        editName = collection.name;
        editDescription = collection.description ?? '';
        editError = null;
        editing = true;
    }

    async function saveEdit() {
        if (editBusy) return;
        editBusy = true;
        editError = null;
        try {
            const updated = await updateCollection(collection.id, {
                name: editName.trim(),
                description: editDescription.trim() || null
            });
            collection = updated;
            editing = false;
        } catch (e) {
            editError = (e as Error).message;
        } finally {
            editBusy = false;
        }
    }

    async function onDeleteCollection() {
        if (!confirm(`Delete collection "${collection.name}"? This cannot be undone.`)) {
            return;
        }
        try {
            await deleteCollection(collection.id);
            goto('/collections');
        } catch (e) {
            editError = (e as Error).message;
        }
    }

    async function onRemoveManga(m: Manga) {
        const snapshot = mangas;
        mangas = mangas.filter((x) => x.id !== m.id);
        try {
            await removeMangaFromCollection(collection.id, m.id);
        } catch (e) {
            mangas = snapshot;
            editError = (e as Error).message;
        }
    }

    async function onRemovePage(p: CollectionPageItem) {
        const snapshot = pages;
        pages = pages.filter((x) => x.page_id !== p.page_id);
        try {
            await removePageFromCollection(collection.id, p.page_id);
        } catch (e) {
            pages = snapshot;
            editError = (e as Error).message;
        }
    }
</script>

<svelte:head>
    <title>Mangalord | {collection.name}</title>
</svelte:head>

<nav class="back">
    <a href="/collections" class="back-link">
        <ArrowLeft size={16} aria-hidden="true" />
        <span>All collections</span>
    </a>
</nav>

<header class="overview">
    {#if editing}
        <form
            class="edit-form"
            onsubmit={(e) => {
                e.preventDefault();
                void saveEdit();
            }}
            action="javascript:void(0)"
        >
            <input
                type="text"
                bind:value={editName}
                maxlength="64"
                required
                aria-label="Collection name"
                data-testid="collection-edit-name"
            />
            <textarea
                bind:value={editDescription}
                rows="2"
                maxlength="1024"
                placeholder="Description (optional)"
                aria-label="Collection description"
                data-testid="collection-edit-description"
            ></textarea>
            <div class="edit-actions">
                <button
                    type="submit"
                    class="primary"
                    disabled={!editName.trim() || editBusy}
                    data-testid="collection-edit-save"
                >
                    <Check size={14} aria-hidden="true" />
                    <span>Save</span>
                </button>
                <button type="button" onclick={() => (editing = false)} disabled={editBusy}>
                    <X size={14} aria-hidden="true" />
                    <span>Cancel</span>
                </button>
            </div>
        </form>
    {:else}
        <div class="title-row">
            <h1 data-testid="collection-name">{collection.name}</h1>
            <IconButton
                onclick={startEdit}
                aria-label="Edit collection"
                title="Edit"
                data-testid="collection-edit-open"
            >
                <Pencil size={16} aria-hidden="true" />
            </IconButton>
            <IconButton
                variant="danger"
                onclick={onDeleteCollection}
                aria-label="Delete collection"
                title="Delete"
                data-testid="collection-delete"
            >
                <Trash2 size={16} aria-hidden="true" />
            </IconButton>
        </div>
        {#if collection.description}
            <p class="description" data-testid="collection-description">
                {collection.description}
            </p>
        {/if}
    {/if}
    {#if editError}
        <p class="error" role="alert">{editError}</p>
    {/if}
</header>

{#if contentLoading}
    <MangaGridSkeleton count={6} />
{:else if contentError}
    <p class="error" role="alert" data-testid="collection-content-error">
        Couldn't load this collection's contents: {contentError}
    </p>
{:else if mangas.length === 0 && pages.length === 0}
    <p class="status" data-testid="collection-empty">
        This collection is empty.
    </p>
{/if}

{#if !contentLoading && mangas.length > 0}
    <section aria-labelledby="mangas-heading">
        <h2 id="mangas-heading" class="section-heading">Mangas</h2>
        <ul class="manga-grid" data-testid="collection-manga-list">
            {#each mangas as m (m.id)}
                <li class="card-with-remove">
                    <MangaCard manga={m} testid={`collection-manga-${m.id}`} />
                    <button
                        type="button"
                        class="remove"
                        onclick={() => onRemoveManga(m)}
                        aria-label={`Remove ${m.title} from collection`}
                        title="Remove from collection"
                        data-testid={`collection-remove-manga-${m.id}`}
                    >
                        <X size={14} aria-hidden="true" />
                    </button>
                </li>
            {/each}
        </ul>
    </section>
{/if}

{#if !contentLoading && pages.length > 0}
    <section aria-labelledby="pages-heading">
        <h2 id="pages-heading" class="section-heading">Pages</h2>
        <ul class="page-grid" data-testid="collection-page-list">
            {#each pages as p (p.page_id)}
                <li class="card-with-remove">
                    <a
                        class="page-card"
                        href={`/manga/${p.manga_id}/chapter/${p.chapter_id}?page=${p.page_number}`}
                        data-testid={`collection-page-${p.page_id}`}
                    >
                        <img
                            src={fileUrl(p.storage_key)}
                            alt={`${p.manga_title} chapter ${p.chapter_number} page ${p.page_number}`}
                            class="page-thumb"
                            loading="lazy"
                        />
                        <span class="page-meta">
                            <span class="page-title">{p.manga_title}</span>
                            <span class="page-breadcrumb">
                                Ch. {p.chapter_number} · page {p.page_number}
                            </span>
                        </span>
                    </a>
                    <button
                        type="button"
                        class="remove"
                        onclick={() => onRemovePage(p)}
                        aria-label={`Remove ${p.manga_title} page ${p.page_number} from collection`}
                        title="Remove from collection"
                        data-testid={`collection-remove-page-${p.page_id}`}
                    >
                        <X size={14} aria-hidden="true" />
                    </button>
                </li>
            {/each}
        </ul>
    </section>
{/if}

<style>
    .back {
        margin-bottom: var(--space-3);
    }

    .back-link {
        display: inline-flex;
        align-items: center;
        gap: var(--space-1);
        color: var(--text-muted);
        font-size: var(--font-sm);
    }

    .overview {
        margin-bottom: var(--space-5);
    }

    .title-row {
        display: flex;
        align-items: center;
        gap: var(--space-2);
    }

    .title-row h1 {
        margin: 0;
        flex: 1;
    }

    .description {
        color: var(--text-muted);
        margin: var(--space-2) 0 0;
        white-space: pre-wrap;
    }

    .edit-form {
        display: flex;
        flex-direction: column;
        gap: var(--space-2);
    }

    .edit-actions {
        display: flex;
        gap: var(--space-2);
    }

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

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

    .error {
        color: var(--danger);
        margin: var(--space-2) 0 0;
    }

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

    /* `.manga-grid` is a shared global (see lib/styles/tokens.css). */

    .section-heading {
        margin: var(--space-5) 0 var(--space-3);
        font-size: var(--font-lg);
    }

    .section-heading:first-of-type {
        margin-top: 0;
    }

    .page-grid {
        list-style: none;
        padding: 0;
        margin: 0;
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
        gap: var(--space-3);
    }

    .page-card {
        display: flex;
        flex-direction: column;
        gap: var(--space-1);
        color: var(--text);
        text-decoration: none;
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: var(--radius-md);
        overflow: hidden;
    }

    .page-card:hover {
        border-color: var(--primary);
        text-decoration: none;
    }

    .page-thumb {
        width: 100%;
        aspect-ratio: 2 / 3;
        object-fit: cover;
        background: var(--surface-elevated);
    }

    .page-meta {
        display: flex;
        flex-direction: column;
        gap: 2px;
        padding: var(--space-2);
    }

    .page-title {
        font-weight: var(--weight-medium);
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .page-breadcrumb {
        color: var(--text-muted);
        font-size: var(--font-xs);
    }

    .card-with-remove {
        position: relative;
        list-style: none;
    }

    .remove {
        position: absolute;
        top: var(--space-1);
        right: var(--space-1);
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 24px;
        height: 24px;
        padding: 0;
        background: rgba(0, 0, 0, 0.6);
        color: white;
        border: 0;
        border-radius: 50%;
        cursor: pointer;
        opacity: 0;
        transition: opacity var(--transition);
    }

    .card-with-remove:hover .remove,
    .card-with-remove:focus-within .remove {
        opacity: 1;
    }

    /* Touch has no hover, so the reveal-on-hover treatment leaves the
       remove buttons invisible and undiscoverable. Show them persistently
       and enlarge the hit area on phones. (Capped well below 44px because
       the button floats over a 4-up cover thumbnail — a full touch-floor
       square would swallow the cover.) */
    @media (max-width: 640px) {
        .remove {
            opacity: 1;
            width: 32px;
            height: 32px;
        }
    }
</style>
