<script lang="ts">
    import { fileUrl } from '$lib/api/client';
    import { createBookmark, deleteBookmark, type Bookmark } from '$lib/api/bookmarks';
    import {
        attachTag,
        detachTag,
        type AuthorRef,
        type GenreRef,
        type TagRef
    } from '$lib/api/mangas';
    import { listTags, type Tag } from '$lib/api/tags';
    import { session } from '$lib/session.svelte';
    import Chip from '$lib/components/Chip.svelte';
    import Plus from '@lucide/svelte/icons/plus';

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

    const authors = $derived<AuthorRef[]>(manga.authors);
    const genres = $derived<GenreRef[]>(manga.genres);

    // svelte-ignore state_referenced_locally
    let tags = $state<TagRef[]>([...manga.tags]);
    // svelte-ignore state_referenced_locally
    let bookmarks = $state<Bookmark[]>([...data.bookmarks]);

    const mangaBookmark = $derived(
        bookmarks.find((b) => b.manga_id === manga.id && b.chapter_id === null) ?? null
    );

    let busy = $state(false);
    let altTitlesOpen = $state(false);

    async function toggleBookmark() {
        if (!session.user) return;
        busy = true;
        try {
            if (mangaBookmark) {
                const id = mangaBookmark.id;
                await deleteBookmark(id);
                bookmarks = bookmarks.filter((b) => b.id !== id);
            } else {
                const b = await createBookmark({ manga_id: manga.id });
                bookmarks = [b, ...bookmarks];
            }
        } finally {
            busy = false;
        }
    }

    // ---- Tag UI ----
    let tagDraft = $state('');
    let tagAddBusy = $state(false);
    let tagError = $state<string | null>(null);
    let suggestions = $state<Tag[]>([]);
    let suggestHighlight = $state(-1);
    let suggestTimer: ReturnType<typeof setTimeout> | null = null;
    // Monotonic counter — late-returning fetches with a stale seq are
    // discarded so a fast typist can't see results from a previous
    // query overwrite the current one.
    let suggestSeq = 0;
    const suggestListId = 'tag-suggest-list';

    function onTagDraftInput() {
        tagError = null;
        suggestHighlight = -1;
        if (suggestTimer) clearTimeout(suggestTimer);
        const q = tagDraft.trim();
        if (q.length === 0) {
            suggestions = [];
            suggestSeq++;
            return;
        }
        const seq = ++suggestSeq;
        suggestTimer = setTimeout(async () => {
            try {
                const matched = await listTags({ search: q, limit: 6 });
                if (seq !== suggestSeq) return;
                // Hide tags already attached so the menu only suggests new picks.
                const attached = new Set(tags.map((t) => t.id));
                suggestions = matched.filter((m) => !attached.has(m.id));
            } catch {
                if (seq === suggestSeq) suggestions = [];
            }
        }, 180);
    }

    function onTagKeydown(e: KeyboardEvent) {
        if (e.key === 'ArrowDown' && suggestions.length > 0) {
            e.preventDefault();
            suggestHighlight = (suggestHighlight + 1) % suggestions.length;
        } else if (e.key === 'ArrowUp' && suggestions.length > 0) {
            e.preventDefault();
            suggestHighlight =
                suggestHighlight <= 0 ? suggestions.length - 1 : suggestHighlight - 1;
        } else if (e.key === 'Escape') {
            suggestions = [];
            suggestHighlight = -1;
        }
        // Enter is handled by the form's onsubmit — if a suggestion is
        // highlighted we submit that name, otherwise we submit the
        // raw draft so a brand-new tag can still be created inline.
    }

    async function submitTag(name: string) {
        const trimmed = name.trim();
        if (!trimmed || !session.user || tagAddBusy) return;
        tagAddBusy = true;
        tagError = null;
        try {
            const attached = await attachTag(manga.id, trimmed);
            // If the tag was already attached by someone else, the
            // server returns 200 + the existing ref — replace any
            // matching entry to keep local state coherent.
            tags = [...tags.filter((t) => t.id !== attached.id), attached];
            tagDraft = '';
            suggestions = [];
            suggestHighlight = -1;
        } catch (e) {
            tagError = (e as Error).message;
        } finally {
            tagAddBusy = false;
        }
    }

    async function removeTag(tag: TagRef) {
        if (!session.user || tag.added_by !== session.user.id) return;
        const snapshot = tags;
        tags = tags.filter((t) => t.id !== tag.id);
        try {
            await detachTag(manga.id, tag.id);
        } catch (e) {
            tags = snapshot;
            tagError = (e as Error).message;
        }
    }

    function onTagFormSubmit(e: SubmitEvent) {
        e.preventDefault();
        // If the user arrowed down to a suggestion, pick it; otherwise
        // submit whatever's in the input (allows creating a new tag).
        const target =
            suggestHighlight >= 0 && suggestions[suggestHighlight]
                ? suggestions[suggestHighlight].name
                : tagDraft;
        submitTag(target);
    }

    const statusLabel = $derived(manga.status === 'completed' ? 'Completed' : 'Ongoing');
</script>

<svelte:head>
    <title>{manga.title} — Mangalord</title>
</svelte:head>

<article>
    <header class="overview">
        {#if manga.cover_image_path}
            <img
                src={fileUrl(manga.cover_image_path)}
                alt="{manga.title} cover"
                class="cover"
                loading="eager"
                data-testid="manga-cover"
            />
        {/if}
        <div class="meta">
            <div class="title-row">
                <h1 data-testid="manga-title">{manga.title}</h1>
                <span
                    class="status-badge status-{manga.status}"
                    data-testid="manga-status"
                >
                    {statusLabel}
                </span>
            </div>

            {#if authors.length > 0}
                <div class="chip-row" data-testid="manga-authors">
                    <span class="chip-row-label">by</span>
                    {#each authors as a (a.id)}
                        <Chip label={a.name} href={`/authors/${a.id}`} variant="primary" />
                    {/each}
                </div>
            {/if}

            {#if manga.alt_titles.length > 0}
                <details
                    class="alt-titles"
                    bind:open={altTitlesOpen}
                    data-testid="manga-alt-titles"
                >
                    <summary>Also known as ({manga.alt_titles.length})</summary>
                    <ul>
                        {#each manga.alt_titles as alt}
                            <li>{alt}</li>
                        {/each}
                    </ul>
                </details>
            {/if}

            {#if genres.length > 0}
                <div class="chip-row" data-testid="manga-genres">
                    <span class="chip-row-label">Genres</span>
                    {#each genres as g (g.id)}
                        <Chip label={g.name} />
                    {/each}
                </div>
            {/if}

            <div class="chip-row tag-row" data-testid="manga-tags">
                <span class="chip-row-label">Tags</span>
                {#each tags as t (t.id)}
                    <Chip
                        label={t.name}
                        variant="soft"
                        onRemove={session.user && t.added_by === session.user.id
                            ? () => removeTag(t)
                            : undefined}
                        removeLabel="Remove tag"
                    />
                {/each}
                {#if session.user}
                    <form class="tag-form" onsubmit={onTagFormSubmit}>
                        <input
                            type="text"
                            role="combobox"
                            bind:value={tagDraft}
                            oninput={onTagDraftInput}
                            onkeydown={onTagKeydown}
                            placeholder="Add tag"
                            maxlength="64"
                            aria-label="Add tag"
                            aria-controls={suggestListId}
                            aria-expanded={suggestions.length > 0}
                            aria-autocomplete="list"
                            aria-activedescendant={suggestHighlight >= 0
                                ? `${suggestListId}-opt-${suggestHighlight}`
                                : undefined}
                            class="tag-input"
                            data-testid="tag-input"
                        />
                        <button
                            type="submit"
                            class="tag-add-btn"
                            disabled={!tagDraft.trim() || tagAddBusy}
                            aria-label="Add tag"
                            title="Add tag"
                        >
                            <Plus size={14} aria-hidden="true" />
                        </button>
                        {#if suggestions.length > 0}
                            <ul class="tag-suggestions" role="listbox" id={suggestListId}>
                                {#each suggestions as s, i (s.id)}
                                    <li
                                        id={`${suggestListId}-opt-${i}`}
                                        role="option"
                                        aria-selected={i === suggestHighlight}
                                        class:active={i === suggestHighlight}
                                    >
                                        <button
                                            type="button"
                                            tabindex="-1"
                                            onmouseenter={() => (suggestHighlight = i)}
                                            onclick={() => submitTag(s.name)}
                                            data-testid="tag-suggestion"
                                        >
                                            {s.name}
                                        </button>
                                    </li>
                                {/each}
                            </ul>
                        {/if}
                    </form>
                {/if}
            </div>
            {#if tagError}
                <p class="tag-error" role="alert">{tagError}</p>
            {/if}

            {#if manga.description}
                <p class="description" data-testid="manga-description">{manga.description}</p>
            {/if}

            {#if session.user}
                <button
                    type="button"
                    class="bookmark"
                    class:active={mangaBookmark}
                    onclick={toggleBookmark}
                    disabled={busy}
                    aria-pressed={mangaBookmark ? 'true' : 'false'}
                    data-testid="bookmark-toggle"
                >
                    {mangaBookmark ? '★ Bookmarked' : '☆ Bookmark'}
                </button>
            {:else}
                <a class="bookmark" href="/login" data-testid="bookmark-signin">
                    Sign in to bookmark
                </a>
            {/if}
        </div>
    </header>

    <section aria-label="chapters">
        <h2>Chapters</h2>
        {#if chapters.length === 0}
            <p data-testid="chapters-empty">No chapters yet.</p>
        {:else}
            <ol class="chapter-list" data-testid="chapter-list">
                {#each chapters as c (c.id)}
                    <li>
                        <a href="/manga/{manga.id}/chapter/{c.number}">
                            Chapter {c.number}{#if c.title}: {c.title}{/if}
                        </a>
                        <span class="pages">({c.page_count} pages)</span>
                    </li>
                {/each}
            </ol>
        {/if}
    </section>
</article>

<style>
    .overview {
        display: grid;
        grid-template-columns: minmax(0, 200px) 1fr;
        gap: var(--space-4);
        align-items: start;
        margin-bottom: var(--space-6);
    }

    @media (max-width: 640px) {
        .overview {
            grid-template-columns: 1fr;
        }
    }

    .cover {
        width: 100%;
        height: auto;
        border-radius: var(--radius-md);
        background: var(--surface);
    }

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

    .title-row h1 {
        margin: 0;
    }

    .status-badge {
        display: inline-flex;
        align-items: center;
        padding: 2px var(--space-2);
        border-radius: var(--radius-pill);
        font-size: var(--font-xs);
        font-weight: var(--weight-semibold);
        background: var(--surface-elevated);
        border: 1px solid var(--border-strong);
        color: var(--text-muted);
        text-transform: uppercase;
        letter-spacing: 0.04em;
    }

    .status-badge.status-completed {
        background: var(--success-soft-bg, var(--surface-elevated));
        color: var(--success);
        border-color: var(--success);
    }

    .chip-row {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: var(--space-2);
        margin: var(--space-2) 0;
    }

    .chip-row-label {
        color: var(--text-muted);
        font-size: var(--font-sm);
    }

    .alt-titles {
        margin: var(--space-2) 0;
        color: var(--text-muted);
        font-size: var(--font-sm);
    }

    .alt-titles ul {
        margin: var(--space-1) 0 0;
        padding-left: var(--space-5);
    }

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

    .tag-row {
        position: relative;
    }

    .tag-form {
        display: inline-flex;
        align-items: center;
        gap: var(--space-1);
        position: relative;
    }

    .tag-input {
        height: 28px;
        padding: 0 var(--space-2);
        font-size: var(--font-xs);
        width: 8rem;
    }

    .tag-add-btn {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 28px;
        height: 28px;
        padding: 0;
        background: var(--primary);
        color: var(--primary-contrast);
        border: 1px solid var(--primary);
    }

    .tag-add-btn:hover:not(:disabled) {
        background: var(--primary-hover);
    }

    .tag-suggestions {
        position: absolute;
        top: 100%;
        left: 0;
        margin: var(--space-1) 0 0;
        padding: var(--space-1) 0;
        list-style: none;
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: var(--radius-md);
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
        z-index: var(--z-dropdown);
        min-width: 10rem;
    }

    .tag-suggestions button {
        width: 100%;
        background: transparent;
        border: 0;
        text-align: left;
        padding: var(--space-1) var(--space-3);
        color: var(--text);
        cursor: pointer;
        font-size: var(--font-sm);
    }

    .tag-suggestions li.active button,
    .tag-suggestions button:hover {
        background: var(--primary-soft-bg);
    }

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

    .bookmark {
        display: inline-flex;
        align-items: center;
        gap: var(--space-2);
        margin-top: var(--space-2);
        padding: 0 var(--space-3);
        height: 36px;
        border: 1px solid var(--border-strong);
        border-radius: var(--radius-md);
        background: var(--surface);
        color: var(--text);
        text-decoration: none;
        cursor: pointer;
        font-size: var(--font-sm);
        font-weight: var(--weight-medium);
        transition:
            background var(--transition),
            border-color var(--transition),
            color var(--transition);
    }

    .bookmark:hover {
        background: var(--surface-elevated);
        text-decoration: none;
    }

    .bookmark.active {
        background: var(--warning-soft-bg);
        border-color: var(--warning-border);
        color: var(--text);
    }

    .chapter-list {
        padding-left: var(--space-6);
        color: var(--text);
    }

    .chapter-list li {
        padding: var(--space-1) 0;
    }

    .pages {
        color: var(--text-muted);
        margin-left: var(--space-2);
        font-size: var(--font-sm);
    }
</style>
