<script lang="ts">
  import { pageTitle } from '$lib/branding';
  import { Disc3, Album, Music2, Check, X, RotateCcw, Trash2, Cloud, ChevronRight } from 'lucide-svelte';
  import StageBadge from '$lib/components/StageBadge.svelte';
  import { stageState } from './stage-state';
  import { useQueryClient } from '@tanstack/svelte-query';
  import {
    createAdminRequestsQuery,
    createLidarrConfigQuery,
    createAdminQuarantineQuery,
    createScanStatusQuery,
    createCoverageQuery,
    approveRequest,
    rejectRequest,
    resolveQuarantine,
    deleteQuarantineFile,
    deleteQuarantineViaLidarr,
    refetchMissingCovers,
    triggerScan,
    researchMissingArt
  } from '$lib/api/admin';
  import { qk } from '$lib/api/queries';
  import { errCode } from '$lib/api/errors';
  import { pushToast } from '$lib/stores/toast.svelte';
  import { runMutation } from '$lib/util/runMutation';
  import type {
    AdminQuarantineRow,
    LidarrRequest,
    LidarrRequestKind,
    LidarrQuarantineReason
  } from '$lib/api/types';

  // Top-of-admin overview. Shows the connection card + the most recent
  // 5 of each actionable queue (requests, quarantine) with inline
  // handlers for the common case. The dedicated pages keep the full
  // modal flows (override, typed-confirm); the overview offers a faster
  // path for the common-case actions, with a two-click confirm on
  // destructive quarantine actions to avoid modal duplication.

  const PREVIEW_LIMIT = 5;

  const client = useQueryClient();

  const requestsStore = createAdminRequestsQuery('pending');
  const requests = $derived($requestsStore);

  const configStore = createLidarrConfigQuery();
  const config = $derived($configStore);

  const quarantineStore = createAdminQuarantineQuery();
  const quarantine = $derived($quarantineStore);

  const pendingCount = $derived(requests.data?.length ?? 0);
  const quarantineCount = $derived(quarantine.data?.length ?? 0);
  const lidarrConnected = $derived(config.data?.enabled === true);

  const requestPreview = $derived(
    (requests.data ?? []).slice(0, PREVIEW_LIMIT)
  );
  const quarantinePreview = $derived(
    (quarantine.data ?? []).slice(0, PREVIEW_LIMIT)
  );

  // ---- Two-click destructive confirm for quarantine actions ----
  // Map key is `${trackId}:${actionKey}`. First click sets a key; second
  // click within 3s fires the action; otherwise the key clears.
  let confirmingKey = $state<string | null>(null);
  let confirmTimer: ReturnType<typeof setTimeout> | null = null;

  function confirmKey(trackID: string, action: 'delete-file' | 'delete-lidarr'): string {
    return `${trackID}:${action}`;
  }
  function armConfirm(trackID: string, action: 'delete-file' | 'delete-lidarr') {
    confirmingKey = confirmKey(trackID, action);
    if (confirmTimer) clearTimeout(confirmTimer);
    confirmTimer = setTimeout(() => { confirmingKey = null; }, 3000);
  }
  function clearConfirm() {
    confirmingKey = null;
    if (confirmTimer) clearTimeout(confirmTimer);
  }

  // ---- Helpers ----
  function fallbackIcon(kind: LidarrRequestKind) {
    if (kind === 'artist') return Disc3;
    if (kind === 'album') return Album;
    return Music2;
  }
  function rowTitle(r: LidarrRequest): string {
    if (r.kind === 'artist') return r.artist_name;
    if (r.kind === 'album')  return r.album_title ?? '—';
    return r.track_title ?? '—';
  }
  function rowSubtitle(r: LidarrRequest): string {
    if (r.kind === 'artist') return 'Artist';
    if (r.kind === 'album')  return `Album · ${r.artist_name}`;
    return `Track · ${r.artist_name}`;
  }

  const REASON_LABELS: Record<LidarrQuarantineReason, string> = {
    bad_rip:    'Bad rip',
    wrong_file: 'Wrong file',
    wrong_tags: 'Wrong tags',
    duplicate:  'Duplicate',
    other:      'Other'
  };

  // ---- Action handlers ----
  async function invalidateRequests() {
    await Promise.all([
      client.invalidateQueries({ queryKey: qk.adminRequests('pending') }),
      client.invalidateQueries({ queryKey: qk.adminRequests('approved') }),
      client.invalidateQueries({ queryKey: qk.adminRequests('rejected') }),
      client.invalidateQueries({ queryKey: qk.adminRequests('completed') })
    ]);
  }

  async function onApprove(r: LidarrRequest) {
    await runMutation(async () => {
      await approveRequest(r.id);
      await invalidateRequests();
    });
  }
  async function onReject(r: LidarrRequest) {
    await runMutation(async () => {
      await rejectRequest(r.id);
      await invalidateRequests();
    });
  }

  async function invalidateQuarantine() {
    await Promise.all([
      client.invalidateQueries({ queryKey: qk.adminQuarantine() }),
      client.invalidateQueries({ queryKey: qk.adminQuarantineActions() })
    ]);
  }

  async function onResolve(row: AdminQuarantineRow) {
    await runMutation(async () => {
      await resolveQuarantine(row.track_id);
      await invalidateQuarantine();
    });
  }
  async function onDeleteFile(row: AdminQuarantineRow) {
    const key = confirmKey(row.track_id, 'delete-file');
    if (confirmingKey !== key) {
      armConfirm(row.track_id, 'delete-file');
      return;
    }
    clearConfirm();
    await runMutation(async () => {
      await deleteQuarantineFile(row.track_id);
      await invalidateQuarantine();
    });
  }
  async function onDeleteLidarr(row: AdminQuarantineRow) {
    const key = confirmKey(row.track_id, 'delete-lidarr');
    if (confirmingKey !== key) {
      armConfirm(row.track_id, 'delete-lidarr');
      return;
    }
    clearConfirm();
    await runMutation(async () => {
      await deleteQuarantineViaLidarr(row.track_id);
      await invalidateQuarantine();
    });
  }

  // ---- Library scan ----
  const scanStatusStore = $derived(createScanStatusQuery());
  const scanStatusQ = $derived($scanStatusStore);
  const scan = $derived(scanStatusQ.data);
  const scanInFlight = $derived(scan?.in_flight === true);

  // ---- Cover-art coverage gauge ----
  const coverageStore = $derived(createCoverageQuery());
  const coverageQ = $derived($coverageStore);
  const coverage = $derived(coverageQ.data);

  let triggering = $state(false);
  let triggerResult = $state<string | null>(null);

  async function onTriggerScan() {
    if (triggering || scanInFlight) return;
    triggering = true;
    triggerResult = null;
    try {
      await triggerScan();
      triggerResult = 'Scan started.';
      await client.invalidateQueries({ queryKey: qk.scanStatus() });
      await client.invalidateQueries({ queryKey: qk.coverage() });
    } catch (e) {
      const code = errCode(e);
      const status = (e as { status?: number })?.status;
      if (status === 409) {
        triggerResult = 'A scan is already running.';
      } else {
        triggerResult = `Failed: ${code}`;
      }
    } finally {
      triggering = false;
    }
  }

  function formatTime(iso: string | null | undefined): string {
    if (!iso) return '—';
    return new Date(iso).toLocaleString();
  }

  // ---- Cover art bulk refetch ----
  let bulkBusy = $state(false);
  let bulkResult = $state<string | null>(null);

  // ---- Re-search missing art ----
  let researchSaving = $state(false);

  async function onResearchMissingArt() {
    researchSaving = true;
    try {
      await researchMissingArt();
      pushToast('All previously-failed art will be re-attempted on the next scan.');
    } catch (e) {
      pushToast(`Re-search failed: ${errCode(e)}`, 'error');
    } finally {
      researchSaving = false;
    }
  }

  async function onBulkRefetch() {
    if (bulkBusy) return;
    bulkBusy = true;
    bulkResult = null;
    try {
      const { started } = await refetchMissingCovers();
      bulkResult = started
        ? 'Refetching all missing covers — local sources are fast, remote providers throttle per their limits.'
        : 'Could not start the cover refetch.';
      await client.invalidateQueries({ queryKey: qk.coverage() });
    } catch (e) {
      bulkResult = `Failed: ${errCode(e)}`;
    } finally {
      bulkBusy = false;
    }
  }
</script>

<svelte:head><title>{pageTitle('Admin · Overview')}</title></svelte:head>

<div class="space-y-8">
  <header>
    <h2 class="font-display text-2xl font-medium text-text-primary">Overview</h2>
  </header>

  <!-- Top stats -->
  <div class="grid gap-4 sm:grid-cols-3">
    <a
      href="/admin/requests"
      class="block rounded-xl border border-border bg-surface p-5 transition-colors hover:bg-surface-hover"
    >
      <div class="text-sm text-text-secondary">Pending requests</div>
      <div class="font-display mt-1 text-3xl font-medium text-text-primary">{pendingCount}</div>
    </a>
    <a
      href="/admin/quarantine"
      class="block rounded-xl border border-border bg-surface p-5 transition-colors hover:bg-surface-hover"
    >
      <div class="text-sm text-text-secondary">Quarantined tracks</div>
      <div class="font-display mt-1 text-3xl font-medium text-text-primary">{quarantineCount}</div>
    </a>
    <a
      href="/admin/integrations"
      class="block rounded-xl border border-border bg-surface p-5 transition-colors hover:bg-surface-hover"
    >
      <div class="text-sm text-text-secondary">Lidarr</div>
      <div class="font-display mt-1 text-2xl font-medium text-text-primary">
        {lidarrConnected ? 'Connected' : 'Unset'}
      </div>
    </a>
  </div>

  <!-- Pending requests preview -->
  <section class="space-y-3">
    <header class="flex items-baseline justify-between">
      <h3 class="font-display text-xl font-medium text-text-primary">Pending requests</h3>
      {#if pendingCount > PREVIEW_LIMIT}
        <a href="/admin/requests" class="text-sm text-accent hover:underline">View all {pendingCount} →</a>
      {/if}
    </header>

    {#if requests.isPending}
      <p class="text-sm text-text-secondary">Loading…</p>
    {:else if requestPreview.length === 0}
      <p class="text-sm text-text-secondary">No pending requests.</p>
    {:else}
      <ul class="divide-y divide-border rounded-xl border border-border bg-surface">
        {#each requestPreview as r (r.id)}
          {@const Icon = fallbackIcon(r.kind)}
          <li class="flex items-center gap-3 p-3">
            <Icon size={20} strokeWidth={1.5} class="shrink-0 text-text-muted" />
            <div class="min-w-0 flex-1">
              <div class="truncate text-sm font-medium text-text-primary">{rowTitle(r)}</div>
              <div class="truncate text-xs text-text-secondary">{rowSubtitle(r)}</div>
            </div>
            <button
              type="button"
              onclick={() => onApprove(r)}
              aria-label={`Approve ${rowTitle(r)}`}
              class="flex h-8 items-center gap-1 rounded-md bg-action-primary px-3 text-sm text-action-fg hover:opacity-90"
            >
              <Check size={14} strokeWidth={1.5} /> Approve
            </button>
            <button
              type="button"
              onclick={() => onReject(r)}
              aria-label={`Reject ${rowTitle(r)}`}
              class="flex h-8 items-center gap-1 rounded-md border border-border px-3 text-sm text-text-secondary hover:text-text-primary hover:bg-surface-hover"
            >
              <X size={14} strokeWidth={1.5} /> Reject
            </button>
          </li>
        {/each}
      </ul>
    {/if}
  </section>

  <!-- Library scan -->
  <section class="rounded border border-border bg-surface p-4">
    <div class="flex items-start justify-between gap-4">
      <div>
        <h2 class="font-display text-lg font-medium">Library scan</h2>
        <p class="mt-1 text-sm text-text-secondary">
          Walks the music tree, fills missing MusicBrainz IDs from tags, and refetches album covers.
        </p>
      </div>
      <button
        type="button"
        onclick={onTriggerScan}
        disabled={triggering || scanInFlight}
        class="rounded-md bg-action-primary px-4 py-2 text-sm font-medium text-action-fg hover:opacity-90 disabled:opacity-50"
      >
        {scanInFlight ? 'Scanning…' : triggering ? 'Starting…' : 'Run scan'}
      </button>
    </div>

    {#if !scan || !scan.id}
      <p class="mt-3 text-sm text-text-muted">No scan has run yet.</p>
    {:else}
      <div class="mt-3 grid gap-3 sm:grid-cols-4">
        <!-- Library stage -->
        <div class="rounded border border-border bg-bg p-3">
          <div class="flex items-baseline justify-between gap-2">
            <div class="text-xs font-medium uppercase tracking-wide text-text-muted">Library walk</div>
            <StageBadge state={stageState(scan, 'library')} />
          </div>
          {#if scan.library}
            <dl class="mt-2 space-y-0.5 text-sm">
              <div class="flex justify-between"><dt class="text-text-secondary">Scanned</dt><dd>{scan.library.scanned}</dd></div>
              <div class="flex justify-between"><dt class="text-text-secondary">Added</dt><dd>{scan.library.added}</dd></div>
              <div class="flex justify-between"><dt class="text-text-secondary">Updated</dt><dd>{scan.library.updated}</dd></div>
              <div class="flex justify-between"><dt class="text-text-secondary">Skipped</dt><dd>{scan.library.skipped}</dd></div>
              <div class="flex justify-between"><dt class="text-text-secondary">Errored</dt><dd>{scan.library.errored}</dd></div>
            </dl>
          {:else if stageState(scan, 'library') === 'pending'}
            <p class="mt-2 text-sm text-text-muted">Waiting for previous stage…</p>
          {/if}
        </div>

        <!-- MBID backfill stage -->
        <div class="rounded border border-border bg-bg p-3">
          <div class="flex items-baseline justify-between gap-2">
            <div class="text-xs font-medium uppercase tracking-wide text-text-muted">MBID backfill</div>
            <StageBadge state={stageState(scan, 'mbid_backfill')} />
          </div>
          {#if scan.mbid_backfill}
            <dl class="mt-2 space-y-0.5 text-sm">
              <div class="flex justify-between"><dt class="text-text-secondary">Processed</dt><dd>{scan.mbid_backfill.processed}</dd></div>
              <div class="flex justify-between"><dt class="text-text-secondary">Healed</dt><dd>{scan.mbid_backfill.healed}</dd></div>
              <div class="flex justify-between"><dt class="text-text-secondary">Skipped</dt><dd>{scan.mbid_backfill.skipped}</dd></div>
              <div class="flex justify-between"><dt class="text-text-secondary">Duplicates</dt><dd>{scan.mbid_backfill.duplicates ?? 0}</dd></div>
            </dl>
          {:else if stageState(scan, 'mbid_backfill') === 'pending'}
            <p class="mt-2 text-sm text-text-muted">Waiting for previous stage…</p>
          {/if}
        </div>

        <!-- Cover enrichment stage -->
        <div class="rounded border border-border bg-bg p-3">
          <div class="flex items-baseline justify-between gap-2">
            <div class="text-xs font-medium uppercase tracking-wide text-text-muted">Cover enrichment</div>
            <StageBadge state={stageState(scan, 'cover_enrich')} />
          </div>
          {#if scan.cover_enrich}
            <dl class="mt-2 space-y-0.5 text-sm">
              <div class="flex justify-between"><dt class="text-text-secondary">Processed</dt><dd>{scan.cover_enrich.processed}</dd></div>
              <div class="flex justify-between"><dt class="text-text-secondary">Succeeded</dt><dd>{scan.cover_enrich.succeeded}</dd></div>
              <div class="flex justify-between"><dt class="text-text-secondary">Failed</dt><dd>{scan.cover_enrich.failed}</dd></div>
            </dl>
          {:else if stageState(scan, 'cover_enrich') === 'pending'}
            <p class="mt-2 text-sm text-text-muted">Waiting for previous stage…</p>
          {/if}
        </div>

        <!-- Artist art enrichment stage -->
        <div class="rounded border border-border bg-bg p-3">
          <div class="flex items-baseline justify-between gap-2">
            <div class="text-xs font-medium uppercase tracking-wide text-text-muted">Artist art</div>
            <StageBadge state={stageState(scan, 'artist_art_enrich')} />
          </div>
          {#if scan.artist_art_enrich}
            <dl class="mt-2 space-y-0.5 text-sm">
              <div class="flex justify-between"><dt class="text-text-secondary">Processed</dt><dd>{scan.artist_art_enrich.processed}</dd></div>
              <div class="flex justify-between"><dt class="text-text-secondary">Succeeded</dt><dd>{scan.artist_art_enrich.succeeded}</dd></div>
              <div class="flex justify-between"><dt class="text-text-secondary">Failed</dt><dd>{scan.artist_art_enrich.failed}</dd></div>
            </dl>
          {:else if stageState(scan, 'artist_art_enrich') === 'pending'}
            <p class="mt-2 text-sm text-text-muted">Waiting for previous stage…</p>
          {/if}
        </div>
      </div>

      <div class="mt-3 text-xs text-text-muted">
        Started {formatTime(scan.started_at)}
        {#if scan.finished_at}· Finished {formatTime(scan.finished_at)}{/if}
        {#if scan.error_message}· <span class="text-oxblood">Error: {scan.error_message}</span>{/if}
      </div>
    {/if}

    {#if triggerResult}
      <p class="mt-2 text-sm">{triggerResult}</p>
    {/if}
  </section>

  <!-- Cover art bulk refetch -->
  <section class="rounded border border-border bg-surface p-4">
    <h2 class="font-display text-lg font-medium">Cover art</h2>
    <p class="mt-1 text-sm text-text-secondary">
      Refetch cover art for albums that are missing one or where the previous attempt failed.
    </p>
    <div class="mt-3 flex flex-wrap items-center gap-4">
      <button
        type="button"
        onclick={onBulkRefetch}
        disabled={bulkBusy}
        class="flex h-8 items-center gap-1 rounded-md bg-action-primary px-4 text-sm text-action-fg hover:opacity-90 disabled:opacity-50"
      >
        {bulkBusy ? 'Queueing…' : 'Refetch missing covers'}
      </button>
      <button
        type="button"
        onclick={onResearchMissingArt}
        disabled={researchSaving}
        class="rounded-md border border-border px-3 py-1.5 text-sm text-text-secondary hover:bg-surface-hover hover:text-text-primary disabled:opacity-50"
      >
        {researchSaving ? 'Bumping…' : 'Re-search missing art'}
      </button>

      {#if coverage}
        {#if coverage.total === 0}
          <span class="text-sm text-text-muted">Library is empty.</span>
        {:else}
          <div class="flex items-center gap-3 text-sm">
            <span>{coverage.with_art.toLocaleString()} with art</span>
            <span class="text-text-muted">·</span>
            <span
              class={coverage.pending_no_mbid > 0 ? 'cursor-help' : ''}
              title={coverage.pending_no_mbid > 0
                ? `${coverage.pending_no_mbid.toLocaleString()} of these have no MBID and won't be helped by another scan — fix tags or merge duplicate albums.`
                : undefined}
            >
              {coverage.pending.toLocaleString()} pending
            </span>
            <span class="text-text-muted">·</span>
            <span>{coverage.settled.toLocaleString()} settled</span>
          </div>
        {/if}
      {/if}
    </div>
    {#if bulkResult}
      <p class="mt-2 text-sm">{bulkResult}</p>
    {/if}
  </section>

  <!-- Quarantine preview -->
  <section class="space-y-3">
    <header class="flex items-baseline justify-between">
      <h3 class="font-display text-xl font-medium text-text-primary">Quarantine</h3>
      {#if quarantineCount > PREVIEW_LIMIT}
        <a href="/admin/quarantine" class="text-sm text-accent hover:underline">View all {quarantineCount} →</a>
      {/if}
    </header>

    {#if quarantine.isPending}
      <p class="text-sm text-text-secondary">Loading…</p>
    {:else if quarantinePreview.length === 0}
      <p class="text-sm text-text-secondary">No quarantined tracks.</p>
    {:else}
      <ul class="divide-y divide-border rounded-xl border border-border bg-surface">
        {#each quarantinePreview as row (row.track_id)}
          {@const fileKey = confirmKey(row.track_id, 'delete-file')}
          {@const lidarrKey = confirmKey(row.track_id, 'delete-lidarr')}
          {@const fileArmed = confirmingKey === fileKey}
          {@const lidarrArmed = confirmingKey === lidarrKey}
          <li class="flex items-center gap-3 p-3">
            <Music2 size={20} strokeWidth={1.5} class="shrink-0 text-text-muted" />
            <div class="min-w-0 flex-1">
              <div class="truncate text-sm font-medium text-text-primary">{row.track_title}</div>
              <div class="truncate text-xs text-text-secondary">
                {row.artist_name} · {row.album_title}
                · {row.report_count} {row.report_count === 1 ? 'report' : 'reports'}
                {#if row.reports[0]}
                  · {REASON_LABELS[row.reports[0].reason] ?? row.reports[0].reason}
                {/if}
              </div>
            </div>
            <button
              type="button"
              onclick={() => onResolve(row)}
              aria-label={`Resolve ${row.track_title}`}
              class="flex h-8 items-center gap-1 rounded-md border border-border px-3 text-sm text-text-secondary hover:text-text-primary hover:bg-surface-hover"
            >
              <RotateCcw size={14} strokeWidth={1.5} /> Resolve
            </button>
            <button
              type="button"
              onclick={() => onDeleteFile(row)}
              aria-label={fileArmed ? `Confirm delete file ${row.track_title}` : `Delete file ${row.track_title}`}
              class="flex h-8 items-center gap-1 rounded-md px-3 text-sm transition-colors {fileArmed
                ? 'bg-action-secondary text-action-fg'
                : 'border border-border text-text-secondary hover:text-text-primary hover:bg-surface-hover'}"
            >
              <Trash2 size={14} strokeWidth={1.5} /> {fileArmed ? 'Confirm?' : 'Delete file'}
            </button>
            <button
              type="button"
              onclick={() => onDeleteLidarr(row)}
              aria-label={lidarrArmed ? `Confirm delete via Lidarr ${row.track_title}` : `Delete via Lidarr ${row.track_title}`}
              class="flex h-8 items-center gap-1 rounded-md px-3 text-sm transition-colors {lidarrArmed
                ? 'bg-action-destructive text-action-fg'
                : 'border border-border text-text-secondary hover:text-text-primary hover:bg-surface-hover'}"
            >
              <Cloud size={14} strokeWidth={1.5} /> {lidarrArmed ? 'Confirm?' : 'Delete via Lidarr'}
            </button>
            <a
              href="/admin/quarantine"
              aria-label="Open in quarantine queue"
              class="text-text-secondary hover:text-text-primary"
            >
              <ChevronRight size={16} strokeWidth={1.5} />
            </a>
          </li>
        {/each}
      </ul>
    {/if}
  </section>
</div>

