<script lang="ts" generics="T">
    import { ApiError } from '$lib/api/client';

    // Renders an accumulating list with a "Load more" button. The parent seeds
    // the first page (`initial` + `total`, usually from a streamed loader that
    // also gates auth); further pages are fetched on demand via `fetchMore`,
    // which is handed the current item count as its offset. The `children`
    // snippet receives the accumulated array so the parent controls layout.
    let {
        initial,
        total,
        fetchMore,
        children
    }: {
        initial: T[];
        total: number | null;
        fetchMore: (offset: number) => Promise<{ items: T[]; total: number | null }>;
        children: import('svelte').Snippet<[T[]]>;
    } = $props();

    let extra = $state<T[]>([]);
    let curTotal = $state<number | null>(total);
    let loading = $state(false);
    let error = $state<string | null>(null);

    const all = $derived([...initial, ...extra]);
    const hasMore = $derived(curTotal != null && all.length < curTotal);

    async function more() {
        loading = true;
        error = null;
        try {
            const r = await fetchMore(all.length);
            extra = [...extra, ...r.items];
            curTotal = r.total;
        } catch (e) {
            error = e instanceof ApiError ? e.message : 'Could not load more.';
        } finally {
            loading = false;
        }
    }
</script>

{@render children(all)}

{#if hasMore}
    <div class="load-more">
        <button type="button" onclick={more} disabled={loading} data-testid="load-more">
            {loading ? 'Loading…' : 'Load more'}
        </button>
        {#if error}
            <p class="lm-error" role="alert">{error}</p>
        {/if}
    </div>
{/if}

<style>
    .load-more {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: var(--space-2);
        margin-top: var(--space-3);
    }
    button {
        padding: var(--space-2) var(--space-4);
        border: 1px solid var(--border);
        border-radius: var(--radius-md, 8px);
        background: transparent;
        color: inherit;
        font: inherit;
        cursor: pointer;
    }
    button:disabled {
        opacity: 0.6;
        cursor: default;
    }
    .lm-error {
        color: var(--danger, #dc2626);
        font-size: var(--font-sm);
        margin: 0;
    }
</style>
