<script lang="ts">
    import { onMount, onDestroy } from 'svelte';
    import {
        getHealth,
        updateHealthThresholds,
        type HealthReport,
        type HealthThresholds,
        type CheckStatus
    } from '$lib/api/admin';

    const POLL_MS = 30_000;

    let report = $state<HealthReport | null>(null);
    let loading = $state(true);
    let error = $state<string | null>(null);
    let timer: ReturnType<typeof setInterval> | null = null;
    let inflight: AbortController | null = null;

    // Threshold editor state.
    let editing = $state(false);
    let form = $state<HealthThresholds | null>(null);
    let saving = $state(false);
    let saveError = $state<string | null>(null);
    let saved = $state(false);

    async function load() {
        inflight?.abort();
        const ctrl = new AbortController();
        inflight = ctrl;
        try {
            const r = await getHealth({ signal: ctrl.signal });
            report = r;
            error = null;
        } catch (e) {
            if ((e as Error)?.name === 'AbortError') return;
            error = e instanceof Error ? e.message : 'Failed to load health.';
        } finally {
            if (inflight === ctrl) loading = false;
        }
    }

    onMount(() => {
        load();
        timer = setInterval(load, POLL_MS);
    });
    onDestroy(() => {
        if (timer) clearInterval(timer);
        inflight?.abort();
    });

    function startEdit() {
        // Snapshot current thresholds into an editable copy.
        form = report ? { ...report.thresholds } : null;
        saveError = null;
        saved = false;
        editing = true;
    }

    async function save() {
        if (!form) return;
        saving = true;
        saveError = null;
        saved = false;
        try {
            report = await updateHealthThresholds(form);
            saved = true;
            editing = false;
        } catch (e) {
            saveError = e instanceof Error ? e.message : 'Failed to save thresholds.';
        } finally {
            saving = false;
        }
    }

    const STATUS_GLYPH: Record<CheckStatus, string> = {
        ok: '✓',
        warn: '⚠',
        critical: '✗'
    };
    const STATUS_LABEL: Record<CheckStatus, string> = {
        ok: 'Healthy',
        warn: 'Needs attention',
        critical: 'Critical'
    };

    const failing = $derived(
        report ? report.checks.filter((c) => c.status !== 'ok').length : 0
    );

    // Editable threshold fields, with units for the form labels.
    const FIELDS: { key: keyof HealthThresholds; label: string; unit: string }[] = [
        { key: 'disk_pct', label: 'Disk usage', unit: '%' },
        { key: 'mem_pct', label: 'Memory usage', unit: '%' },
        { key: 'dead_jobs_max', label: 'Dead jobs (max)', unit: '' },
        { key: 'crawl_fail_pct', label: 'Crawl failure rate', unit: '%' },
        { key: 'analysis_fail_pct', label: 'Analysis failure rate', unit: '%' },
        { key: 'cron_freshness_hours', label: 'Cron freshness', unit: 'h' },
        { key: 'missing_covers_max', label: 'Missing covers (max)', unit: '' }
    ];
</script>

<section class="health" data-testid="health-checks">
    {#if error}
        <p class="error" role="alert">{error}</p>
    {/if}

    {#if loading && !report}
        <p class="muted" data-testid="health-loading">Loading…</p>
    {:else if report}
        <header class="overall status-{report.status}" data-testid="health-overall">
            <span class="glyph">{STATUS_GLYPH[report.status]}</span>
            <span class="overall-label">{STATUS_LABEL[report.status]}</span>
            {#if failing > 0}
                <span class="muted">· {failing} check{failing === 1 ? '' : 's'} need attention</span>
            {/if}
        </header>

        <ul class="checks">
            {#each report.checks as c (c.id)}
                <li class="check status-{c.status}" data-testid={`health-check-${c.id}`}>
                    <span class="glyph" aria-hidden="true">{STATUS_GLYPH[c.status]}</span>
                    <span class="label">{c.label}</span>
                    <span class="value">
                        {c.value}{#if c.threshold}<span class="muted"> / {c.threshold}</span>{/if}
                    </span>
                    <span class="meta">
                        {#if c.hint}<span class="hint muted">{c.hint}</span>{/if}
                        {#if c.action_href && c.status !== 'ok'}
                            <a class="fix" href={c.action_href}>Fix →</a>
                        {/if}
                    </span>
                </li>
            {/each}
        </ul>

        <div class="thresholds">
            <button
                type="button"
                class="disclosure"
                onclick={() => (editing ? (editing = false) : startEdit())}
                aria-expanded={editing}
                data-testid="health-thresholds-toggle"
            >
                {editing ? '▾' : '▸'} Configure thresholds
            </button>
            {#if saved && !editing}<span class="ok-note">Saved ✓</span>{/if}

            {#if editing && form}
                <form
                    class="threshold-form"
                    onsubmit={(e) => {
                        e.preventDefault();
                        save();
                    }}
                >
                    {#each FIELDS as f (f.key)}
                        <label>
                            <span>{f.label}{f.unit ? ` (${f.unit})` : ''}</span>
                            <input
                                type="number"
                                step="1"
                                min="0"
                                bind:value={form[f.key]}
                                data-testid={`threshold-${f.key}`}
                            />
                        </label>
                    {/each}
                    {#if saveError}<p class="error" role="alert">{saveError}</p>{/if}
                    <div class="actions">
                        <button type="submit" disabled={saving} data-testid="threshold-save">
                            {saving ? 'Saving…' : 'Save'}
                        </button>
                        <button type="button" class="secondary" onclick={() => (editing = false)}>
                            Cancel
                        </button>
                    </div>
                </form>
            {/if}
        </div>
    {/if}
</section>

<style>
    .overall {
        display: flex;
        align-items: center;
        gap: var(--space-2);
        font-size: var(--font-md);
        font-weight: var(--weight-semibold);
        margin-bottom: var(--space-3);
    }
    .overall .glyph {
        font-size: var(--font-lg);
    }
    .checks {
        list-style: none;
        margin: 0 0 var(--space-4) 0;
        padding: 0;
        border: 1px solid var(--border);
        border-radius: var(--radius-md);
    }
    .check {
        display: grid;
        grid-template-columns: 1.4rem 1fr auto;
        grid-template-areas: 'glyph label value' '. meta meta';
        align-items: center;
        gap: var(--space-1) var(--space-2);
        padding: var(--space-2) var(--space-3);
        border-bottom: 1px solid var(--border);
        font-size: var(--font-sm);
    }
    .check:last-child {
        border-bottom: none;
    }
    .check .glyph {
        grid-area: glyph;
        font-weight: var(--weight-semibold);
    }
    .label {
        grid-area: label;
    }
    .value {
        grid-area: value;
        text-align: right;
        font-variant-numeric: tabular-nums;
    }
    .meta {
        grid-area: meta;
        display: flex;
        justify-content: space-between;
        gap: var(--space-2);
        font-size: var(--font-xs);
    }
    .hint {
        font-size: var(--font-xs);
    }
    .fix {
        white-space: nowrap;
    }
    /* Status colors on the glyph + left accent. */
    .status-ok .glyph {
        color: var(--success, #16a34a);
    }
    .status-warn {
        box-shadow: inset 3px 0 0 var(--warning, #d97706);
    }
    .status-warn .glyph {
        color: var(--warning, #d97706);
    }
    .status-critical {
        box-shadow: inset 3px 0 0 var(--danger, #dc2626);
    }
    .status-critical .glyph {
        color: var(--danger, #dc2626);
    }
    .muted {
        color: var(--text-muted);
    }
    .disclosure {
        background: none;
        border: none;
        color: var(--text);
        cursor: pointer;
        font-size: var(--font-sm);
        padding: 0;
    }
    .ok-note {
        margin-left: var(--space-2);
        color: var(--success, #16a34a);
        font-size: var(--font-sm);
    }
    .threshold-form {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
        gap: var(--space-3);
        margin-top: var(--space-3);
        max-width: 40rem;
    }
    .threshold-form label {
        display: flex;
        flex-direction: column;
        gap: 2px;
        font-size: var(--font-xs);
        color: var(--text-muted);
    }
    .threshold-form input {
        height: 34px;
        padding: 0 var(--space-2);
        border: 1px solid var(--border);
        border-radius: var(--radius-md);
        background: var(--surface);
        color: var(--text);
        font-size: var(--font-sm);
    }
    .threshold-form .actions {
        grid-column: 1 / -1;
        display: flex;
        gap: var(--space-2);
    }
    button.secondary {
        background: var(--surface-elevated);
    }
    .error {
        color: var(--danger, #dc2626);
    }
</style>
