<script lang="ts">
    import Modal from './Modal.svelte';
    import {
        addMangaToCollection,
        createCollection,
        listMyCollections,
        getMyCollectionsContaining,
        removeMangaFromCollection,
        type CollectionSummary
    } from '$lib/api/collections';
    import Plus from '@lucide/svelte/icons/plus';

    let {
        open,
        mangaId,
        onClose
    }: {
        open: boolean;
        mangaId: string;
        onClose: () => void;
    } = $props();

    let collections = $state<CollectionSummary[]>([]);
    let containingIds = $state<Set<string>>(new Set());
    let busyIds = $state<Set<string>>(new Set());
    let newName = $state('');
    let creating = $state(false);
    let loading = $state(false);
    let error: string | null = $state(null);

    // Refetch every time the modal opens (and when the manga id changes
    // mid-session — unlikely but cheap). The data is per-user and per-
    // manga, so re-fetching is the simplest way to stay in sync with
    // changes made elsewhere (e.g., a collection deleted on another page).
    $effect(() => {
        if (open) {
            void load();
        }
    });

    async function load() {
        loading = true;
        error = null;
        try {
            const [page, ids] = await Promise.all([
                listMyCollections({ limit: 200 }),
                getMyCollectionsContaining(mangaId)
            ]);
            collections = page.items;
            containingIds = new Set(ids);
        } catch (e) {
            error = (e as Error).message;
        } finally {
            loading = false;
        }
    }

    // Functional set updates that read the latest state at mutation
    // time, so concurrent toggles on different rows don't clobber
    // each other by building from a stale snapshot.
    function withAdd<T>(s: Set<T>, v: T): Set<T> {
        const n = new Set(s);
        n.add(v);
        return n;
    }
    function withDelete<T>(s: Set<T>, v: T): Set<T> {
        const n = new Set(s);
        n.delete(v);
        return n;
    }

    async function toggle(collection: CollectionSummary) {
        if (busyIds.has(collection.id)) return;
        const wasIn = containingIds.has(collection.id);
        // Optimistic toggle — local set first; revert on failure.
        containingIds = wasIn
            ? withDelete(containingIds, collection.id)
            : withAdd(containingIds, collection.id);
        busyIds = withAdd(busyIds, collection.id);
        try {
            if (wasIn) {
                await removeMangaFromCollection(collection.id, mangaId);
                collection.manga_count = Math.max(0, collection.manga_count - 1);
            } else {
                await addMangaToCollection(collection.id, mangaId);
                collection.manga_count += 1;
            }
        } catch (e) {
            // Revert (read latest containingIds, not the pre-toggle snapshot).
            containingIds = wasIn
                ? withAdd(containingIds, collection.id)
                : withDelete(containingIds, collection.id);
            error = (e as Error).message;
        } finally {
            busyIds = withDelete(busyIds, collection.id);
        }
    }

    async function createAndAdd() {
        const name = newName.trim();
        if (!name || creating) return;
        creating = true;
        error = null;
        try {
            const created = await createCollection({ name });
            // The list endpoint sorts by updated_at DESC; adding the
            // manga immediately also bumps it. Append a synthetic
            // summary so the new collection appears checked-on right
            // away rather than waiting for a refetch.
            await addMangaToCollection(created.id, mangaId);
            collections = [
                {
                    ...created,
                    manga_count: 1,
                    sample_covers: []
                },
                ...collections
            ];
            containingIds = new Set([...containingIds, created.id]);
            newName = '';
        } catch (e) {
            error = (e as Error).message;
        } finally {
            creating = false;
        }
    }

    function onCreateSubmit(e: SubmitEvent) {
        e.preventDefault();
        void createAndAdd();
    }
</script>

<Modal {open} {onClose} title="Add to collection" size="md" testid="add-to-collection-modal">
    {#if loading}
        <p class="status">Loading your collections…</p>
    {:else if error}
        <p class="error" role="alert" data-testid="add-to-collection-error">{error}</p>
    {:else if collections.length === 0}
        <p class="status" data-testid="no-collections">
            You don't have any collections yet. Create one below to get started.
        </p>
    {:else}
        <ul class="collection-list">
            {#each collections as c (c.id)}
                {@const checked = containingIds.has(c.id)}
                {@const busy = busyIds.has(c.id)}
                <li>
                    <label class="row" class:checked>
                        <input
                            type="checkbox"
                            {checked}
                            disabled={busy}
                            onchange={() => toggle(c)}
                            data-testid={`collection-toggle-${c.id}`}
                        />
                        <span class="row-label">
                            <span class="row-name">{c.name}</span>
                            <span class="row-count">
                                {c.manga_count}
                                {c.manga_count === 1 ? 'manga' : 'mangas'}
                            </span>
                        </span>
                    </label>
                </li>
            {/each}
        </ul>
    {/if}

    <form
        class="create-form"
        onsubmit={onCreateSubmit}
        action="javascript:void(0)"
    >
        <input
            type="text"
            bind:value={newName}
            maxlength="64"
            placeholder="Create new collection"
            aria-label="New collection name"
            data-testid="new-collection-name"
        />
        <button
            type="submit"
            class="create-btn"
            disabled={!newName.trim() || creating}
            data-testid="create-collection-btn"
        >
            <Plus size={14} aria-hidden="true" />
            <span>{creating ? 'Creating…' : 'Create + add'}</span>
        </button>
    </form>
</Modal>

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

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

    .collection-list {
        list-style: none;
        padding: 0;
        margin: 0 0 var(--space-3);
        display: flex;
        flex-direction: column;
        gap: var(--space-1);
        max-height: 16rem;
        overflow-y: auto;
    }

    .row {
        display: flex;
        align-items: center;
        gap: var(--space-2);
        padding: var(--space-2);
        border-radius: var(--radius-md);
        cursor: pointer;
        transition: background var(--transition);
    }

    .row:hover {
        background: var(--surface-elevated);
    }

    .row.checked {
        background: var(--primary-soft-bg);
    }

    .row-label {
        display: flex;
        flex-direction: column;
        min-width: 0;
    }

    .row-name {
        color: var(--text);
        font-weight: var(--weight-medium);
    }

    .row-count {
        color: var(--text-muted);
        font-size: var(--font-xs);
    }

    .create-form {
        display: flex;
        gap: var(--space-2);
        align-items: center;
        padding-top: var(--space-3);
        border-top: 1px solid var(--border);
    }

    .create-form input {
        flex: 1;
        min-width: 0;
    }

    .create-btn {
        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: 36px;
        white-space: nowrap;
    }

    .create-btn:hover:not(:disabled) {
        background: var(--primary-hover);
        border-color: var(--primary-hover);
    }
</style>
