<script lang="ts">
    import { listTokens, createToken, deleteToken, type ApiToken } from '$lib/api/auth';
    import { ApiError } from '$lib/api/client';
    import { session } from '$lib/session.svelte';
    import { toast } from '$lib/toast.svelte';
    import Copy from '@lucide/svelte/icons/copy';
    import Trash2 from '@lucide/svelte/icons/trash-2';

    let tokens = $state<ApiToken[]>([]);
    let loading = $state(true);
    let error = $state<string | null>(null);

    // Create form.
    let newName = $state('');
    let newExpiry = $state<string>(''); // '' = never; otherwise days as a string
    let creating = $state(false);
    // The raw bearer, shown once right after creation.
    let freshBearer = $state<string | null>(null);

    let busyId = $state<string | null>(null);

    async function load() {
        loading = true;
        error = null;
        try {
            tokens = await listTokens();
        } catch (e) {
            error = e instanceof ApiError ? e.message : 'Could not load tokens.';
        } finally {
            loading = false;
        }
    }

    // Load once the session resolves. Guests get the sign-in prompt; a
    // signed-in user's tokens are fetched exactly once.
    let didLoad = $state(false);
    $effect(() => {
        if (!session.loaded) return;
        if (session.user && !didLoad) {
            didLoad = true;
            load();
        } else if (!session.user) {
            loading = false;
        }
    });

    async function onCreate(e: SubmitEvent) {
        e.preventDefault();
        const name = newName.trim();
        if (!name) return;
        creating = true;
        error = null;
        freshBearer = null;
        try {
            const days = newExpiry ? Number(newExpiry) : undefined;
            const created = await createToken(name, days);
            freshBearer = created.bearer;
            newName = '';
            newExpiry = '';
            await load();
        } catch (e) {
            error = e instanceof ApiError ? e.message : 'Could not create token.';
        } finally {
            creating = false;
        }
    }

    async function onRevoke(t: ApiToken) {
        if (!confirm(`Revoke the token "${t.name}"? Any bot using it will stop working.`)) return;
        busyId = t.id;
        try {
            await deleteToken(t.id);
            await load();
        } catch (e) {
            error = e instanceof ApiError ? e.message : 'Could not revoke token.';
        } finally {
            busyId = null;
        }
    }

    async function copyBearer() {
        if (!freshBearer) return;
        try {
            await navigator.clipboard.writeText(freshBearer);
            toast.success('Token copied to clipboard.');
        } catch {
            toast.error('Copy failed — select and copy it manually.');
        }
    }

    function fmt(d: string | null): string {
        return d ? new Date(d).toLocaleDateString() : '—';
    }
</script>

<section class="card" data-testid="tokens-page">
    <h2>Bot API tokens</h2>
    <p class="hint">
        Bot tokens authenticate scripts against the same HTTP API the site uses.
        Send one as <code>Authorization: Bearer &lt;token&gt;</code>. The token is
        shown only once, right after you create it — store it somewhere safe.
    </p>

    {#if !session.user}
        <p class="empty" data-testid="tokens-guest">Sign in to manage your API tokens.</p>
    {:else}
        <form class="create" onsubmit={onCreate} data-testid="token-create-form">
            <label>
                <span>Name</span>
                <input
                    type="text"
                    bind:value={newName}
                    maxlength="64"
                    placeholder="e.g. ci-bot"
                    data-testid="token-name"
                />
            </label>
            <label>
                <span>Expires</span>
                <select bind:value={newExpiry} data-testid="token-expiry">
                    <option value="">Never</option>
                    <option value="30">In 30 days</option>
                    <option value="90">In 90 days</option>
                    <option value="365">In 1 year</option>
                </select>
            </label>
            <button type="submit" class="primary" disabled={!newName.trim() || creating}>
                {creating ? 'Creating…' : 'Create token'}
            </button>
        </form>

        {#if freshBearer}
            <div class="fresh" data-testid="token-fresh">
                <p>Copy your new token now — you won't see it again:</p>
                <div class="fresh-row">
                    <code class="bearer">{freshBearer}</code>
                    <button type="button" onclick={copyBearer} aria-label="Copy token">
                        <Copy size={16} aria-hidden="true" />
                    </button>
                </div>
            </div>
        {/if}

        {#if error}
            <p class="error" data-testid="tokens-error">{error}</p>
        {/if}

        {#if loading}
            <p class="empty">Loading…</p>
        {:else if tokens.length === 0}
            <p class="empty" data-testid="tokens-empty">You have no API tokens yet.</p>
        {:else}
            <ul class="token-list" data-testid="token-list">
                {#each tokens as t (t.id)}
                    <li class="token" data-testid={`token-row-${t.id}`}>
                        <div class="meta">
                            <span class="name">{t.name}</span>
                            <span class="sub">
                                Created {fmt(t.created_at)} · Last used {fmt(t.last_used_at)} ·
                                {t.expires_at ? `Expires ${fmt(t.expires_at)}` : 'No expiry'}
                            </span>
                        </div>
                        <button
                            type="button"
                            class="danger"
                            disabled={busyId === t.id}
                            onclick={() => onRevoke(t)}
                            aria-label={`Revoke ${t.name}`}
                            data-testid={`token-revoke-${t.id}`}
                        >
                            <Trash2 size={16} aria-hidden="true" />
                            <span>Revoke</span>
                        </button>
                    </li>
                {/each}
            </ul>
        {/if}
    {/if}
</section>

<style>
    .card {
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: var(--radius-lg, 12px);
        padding: var(--space-4);
    }
    h2 {
        margin: 0 0 var(--space-2);
    }
    .hint {
        color: var(--text-muted);
        font-size: var(--font-sm);
        margin: 0 0 var(--space-3);
    }
    code {
        background: var(--surface-2, rgba(127, 127, 127, 0.12));
        padding: 0.1em 0.35em;
        border-radius: 4px;
    }
    .create {
        display: flex;
        flex-wrap: wrap;
        align-items: end;
        gap: var(--space-2);
        margin-bottom: var(--space-3);
    }
    .create label {
        display: flex;
        flex-direction: column;
        gap: var(--space-1);
        font-size: var(--font-sm);
    }
    .create input,
    .create select {
        padding: var(--space-2);
        border: 1px solid var(--border);
        border-radius: var(--radius-md, 8px);
        background: var(--surface);
        color: inherit;
        font: inherit;
    }
    button {
        display: inline-flex;
        align-items: center;
        gap: var(--space-1);
        padding: var(--space-2) var(--space-3);
        border-radius: var(--radius-md, 8px);
        border: 1px solid var(--border);
        background: transparent;
        color: inherit;
        font: inherit;
        cursor: pointer;
    }
    button:disabled {
        opacity: 0.6;
        cursor: default;
    }
    .primary {
        background: var(--primary);
        color: var(--primary-contrast, #fff);
        border-color: transparent;
    }
    .danger {
        color: var(--danger, #dc2626);
        border-color: var(--danger, #dc2626);
    }
    .fresh {
        border: 1px solid var(--primary);
        border-radius: var(--radius-md, 8px);
        padding: var(--space-3);
        margin-bottom: var(--space-3);
    }
    .fresh p {
        margin: 0 0 var(--space-2);
        font-size: var(--font-sm);
    }
    .fresh-row {
        display: flex;
        gap: var(--space-2);
        align-items: center;
    }
    .bearer {
        flex: 1;
        overflow-wrap: anywhere;
        font-size: var(--font-sm);
    }
    .error {
        color: var(--danger, #dc2626);
        font-size: var(--font-sm);
    }
    .empty {
        color: var(--text-muted);
        font-size: var(--font-sm);
    }
    .token-list {
        list-style: none;
        margin: 0;
        padding: 0;
        display: flex;
        flex-direction: column;
        gap: var(--space-2);
    }
    .token {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: var(--space-3);
        padding: var(--space-2) var(--space-3);
        border: 1px solid var(--border);
        border-radius: var(--radius-md, 8px);
    }
    .meta {
        display: flex;
        flex-direction: column;
        gap: 0.15rem;
        min-width: 0;
    }
    .name {
        font-weight: var(--weight-medium);
    }
    .sub {
        color: var(--text-muted);
        font-size: var(--font-xs, 0.75rem);
    }
</style>
