<script lang="ts">
  import { Music2, RotateCcw, Trash2, Cloud, Play, ChevronRight } from 'lucide-svelte';
  import { useQueryClient } from '@tanstack/svelte-query';
  import {
    createAdminQuarantineQuery,
    resolveQuarantine,
    deleteQuarantineFile,
    deleteQuarantineViaLidarr
  } from '$lib/api/admin';
  import { qk } from '$lib/api/queries';
  import { copyForCode } from '$lib/api/error-copy';
  import { playRadio } from '$lib/player/store.svelte';
  import type { AdminQuarantineRow, LidarrQuarantineReason } from '$lib/api/types';

  // Aggregated triage queue. One row per track, with per-row resolution
  // actions: Resolve (clears reports), Delete file (Bronze; modal-confirm),
  // Delete via Lidarr (Oxblood; typed-confirm "DELETE"). Mirrors
  // /admin/requests for shape — toast helper, copyForCode(), invalidateQueries.

  const client = useQueryClient();

  const queryStore = createAdminQuarantineQuery();
  const query = $derived($queryStore);
  const rows = $derived((query.data ?? []) as AdminQuarantineRow[]);

  // Sum of reports across all rows; rendered as the accent-tint pill in the
  // header. Count of *reports*, not unique tracks — that's the spec.
  const totalReports = $derived(
    rows.reduce((sum, r) => sum + r.report_count, 0)
  );

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

  function relativeTime(iso: string): string {
    const ms = Date.now() - new Date(iso).getTime();
    const days = Math.floor(ms / (24 * 3_600_000));
    if (days >= 1) return `${days}d ago`;
    const hours = Math.floor(ms / 3_600_000);
    if (hours >= 1) return `${hours}h ago`;
    const minutes = Math.floor(ms / 60_000);
    if (minutes >= 1) return `${minutes}m ago`;
    return 'just now';
  }

  // Row-level expand state: tracks which rows have their per-user report
  // details revealed. Keyed by track_id.
  let expanded = $state<Record<string, boolean>>({});

  function toggleExpanded(trackID: string) {
    expanded[trackID] = !expanded[trackID];
  }

  // Modal state. The modal-confirm (delete file) and the typed-confirm (delete
  // via Lidarr) both store the active row's track_id; null when closed.
  let deleteFileOpen = $state<string | null>(null);

  let deleteLidarrOpen = $state<string | null>(null);
  let deleteLidarrInput = $state<string>('');
  // Inline error inside the typed-confirm modal — the modal stays open with
  // feedback if Lidarr can't be reached or the album lookup fails.
  let deleteLidarrError = $state<string | null>(null);

  // Toast surface — same pattern as /admin/requests for the lidarr-unreachable
  // and other error codes from /resolve and /delete-file.
  let toast = $state<string | null>(null);
  let toastTimer: ReturnType<typeof setTimeout> | null = null;

  function showToast(msg: string) {
    if (toastTimer) clearTimeout(toastTimer);
    toast = msg;
    toastTimer = setTimeout(() => {
      toast = null;
    }, 5000);
  }

  async function invalidate() {
    await client.invalidateQueries({ queryKey: qk.adminQuarantine() });
  }

  async function onResolve(r: AdminQuarantineRow) {
    try {
      await resolveQuarantine(r.track_id);
      await invalidate();
    } catch (e) {
      const code = (e as { code?: string }).code ?? 'unknown';
      showToast(copyForCode(code));
    }
  }

  function openDeleteFile(r: AdminQuarantineRow) {
    deleteFileOpen = r.track_id;
  }

  function cancelDeleteFile() {
    deleteFileOpen = null;
  }

  async function confirmDeleteFile(r: AdminQuarantineRow) {
    deleteFileOpen = null;
    try {
      await deleteQuarantineFile(r.track_id);
      await invalidate();
    } catch (e) {
      const code = (e as { code?: string }).code ?? 'unknown';
      showToast(copyForCode(code));
    }
  }

  function openDeleteLidarr(r: AdminQuarantineRow) {
    deleteLidarrOpen = r.track_id;
    deleteLidarrInput = '';
    deleteLidarrError = null;
  }

  function cancelDeleteLidarr() {
    deleteLidarrOpen = null;
    deleteLidarrInput = '';
    deleteLidarrError = null;
  }

  async function confirmDeleteLidarr(r: AdminQuarantineRow) {
    // Trimmed equality matches the M5a Disconnect typed-confirm pattern.
    if (deleteLidarrInput.trim() !== 'DELETE') return;
    try {
      await deleteQuarantineViaLidarr(r.track_id);
      deleteLidarrOpen = null;
      deleteLidarrInput = '';
      deleteLidarrError = null;
      await invalidate();
    } catch (e) {
      const code = (e as { code?: string }).code ?? 'unknown';
      // Inline error keeps the modal open so the operator sees the failure
      // alongside the album they were about to remove.
      deleteLidarrError = copyForCode(code);
      // Also surface as toast so it's visible after dismissing the modal.
      showToast(copyForCode(code));
    }
  }

  async function onPlay(r: AdminQuarantineRow) {
    try {
      await playRadio(r.track_id);
    } catch {
      // Silent on failure — the play button is a convenience, not a primary
      // action. Toast surface is reserved for the destructive action errors.
    }
  }

  // Modal lookups guard against the row disappearing if the query refetches
  // while the modal is open.
  const deleteFileRow = $derived(
    deleteFileOpen ? rows.find((r) => r.track_id === deleteFileOpen) ?? null : null
  );
  const deleteLidarrRow = $derived(
    deleteLidarrOpen ? rows.find((r) => r.track_id === deleteLidarrOpen) ?? null : null
  );
</script>

<div class="space-y-6">
  <header class="space-y-1">
    <div class="flex items-center gap-2">
      <h2 class="font-display text-2xl font-medium text-text-primary">Quarantine</h2>
      {#if totalReports > 0}
        <span
          class="inline-flex items-center rounded-full bg-accent-tint px-2 py-0.5 text-xs text-accent"
          data-testid="report-count-pill"
        >
          {totalReports}
        </span>
      {/if}
    </div>
    <p class="text-text-secondary">User-reported broken tracks waiting for triage.</p>
  </header>

  {#if query.isPending}
    <p class="text-text-secondary">Reading the queue…</p>
  {:else if query.isError}
    <p class="text-error">Couldn't load the quarantine queue.</p>
  {:else if rows.length === 0}
    <p class="text-text-secondary">Nothing to triage right now.</p>
  {:else}
    <ul class="divide-y divide-border rounded-lg border border-border bg-surface">
      {#each rows as r (r.track_id)}
        {@const isExpanded = !!expanded[r.track_id]}
        {@const lidarrDisabled = !r.lidarr_album_mbid}
        <li
          class="flex items-start gap-4 p-3"
          data-testid="admin-quarantine-row"
          data-track-id={r.track_id}
        >
          <!-- 56px album art with Slate fallback. -->
          <div
            class="flex h-14 w-14 shrink-0 items-center justify-center rounded-md bg-surface-hover"
            aria-hidden="true"
          >
            {#if r.album_id}
              <img
                src={`/api/albums/${r.album_id}/cover`}
                alt=""
                class="h-full w-full rounded-md object-cover"
                loading="lazy"
              />
            {:else}
              <Music2 size={24} strokeWidth={1} class="text-text-muted" />
            {/if}
          </div>

          <div class="min-w-0 flex-1 space-y-1">
            <div class="truncate text-base font-medium text-text-primary">
              {r.track_title}
            </div>
            <div class="truncate text-sm text-text-secondary">
              {r.artist_name} · {r.album_title} · {r.report_count} reports — latest {relativeTime(r.latest_at)}
            </div>

            <!-- Reason distribution pills -->
            <div class="flex flex-wrap gap-1.5 pt-1">
              {#each Object.entries(r.reason_counts) as [reason, count] (reason)}
                <span
                  class="reason-pill"
                  data-testid="reason-pill"
                >
                  {count}× {REASON_LABELS[reason as LidarrQuarantineReason] ?? reason}
                </span>
              {/each}
            </div>

            <!-- Reports details disclosure -->
            <button
              type="button"
              class="mt-1 inline-flex items-center gap-1 text-xs text-text-secondary hover:text-text-primary"
              aria-expanded={isExpanded}
              aria-label={`${isExpanded ? 'Hide' : 'Show'} reports for ${r.track_title}`}
              onclick={() => toggleExpanded(r.track_id)}
            >
              <ChevronRight
                size={12}
                strokeWidth={1.5}
                class="transition-transform {isExpanded ? 'rotate-90' : ''}"
              />
              {isExpanded ? 'Hide reports' : `Show ${r.reports.length} reports`}
            </button>

            {#if isExpanded}
              <ul
                class="mt-2 space-y-1 rounded-md border border-border bg-background p-2 text-xs text-text-secondary"
                data-testid="reports-details"
              >
                {#each r.reports as rep, idx (rep.user_id + ':' + rep.created_at + ':' + idx)}
                  <li>
                    <span class="text-text-primary">{rep.username}</span>
                    · {REASON_LABELS[rep.reason] ?? rep.reason}
                    {#if rep.notes}
                      · <span class="italic">{rep.notes}</span>
                    {/if}
                    · {relativeTime(rep.created_at)}
                  </li>
                {/each}
              </ul>
            {/if}
          </div>

          <!-- Action cluster -->
          <div class="flex shrink-0 items-center gap-2">
            <button
              type="button"
              aria-label={`Play ${r.track_title}`}
              class="inline-flex items-center justify-center rounded-md p-2 text-accent hover:bg-accent-tint"
              onclick={() => onPlay(r)}
            >
              <Play size={16} strokeWidth={2} />
            </button>

            <button
              type="button"
              aria-label={`Resolve ${r.track_title}`}
              class="inline-flex items-center gap-1 rounded-md border border-border bg-transparent px-3 py-1.5 text-sm text-text-secondary hover:text-text-primary hover:bg-surface-hover"
              onclick={() => onResolve(r)}
            >
              <RotateCcw size={14} strokeWidth={1.5} />
              Resolve
            </button>

            <button
              type="button"
              aria-label={`Delete file for ${r.track_title}`}
              class="inline-flex items-center gap-1 rounded-md bg-action-secondary px-3 py-1.5 text-sm text-action-fg"
              onclick={() => openDeleteFile(r)}
            >
              <Trash2 size={14} strokeWidth={2} />
              Delete file
            </button>

            {#if lidarrDisabled}
              <span
                aria-label={`Delete via Lidarr disabled for ${r.track_title}`}
                title="Local-only track — no Lidarr album to remove."
                class="inline-flex items-center gap-1 rounded-md bg-action-destructive px-3 py-1.5 text-sm text-action-fg opacity-50 cursor-not-allowed"
                data-testid="delete-lidarr-disabled"
              >
                <Trash2 size={14} strokeWidth={2} />
                <Cloud size={14} strokeWidth={2} />
                Delete via Lidarr
              </span>
            {:else}
              <button
                type="button"
                aria-label={`Delete via Lidarr for ${r.track_title}`}
                class="inline-flex items-center gap-1 rounded-md bg-action-destructive px-3 py-1.5 text-sm text-action-fg"
                onclick={() => openDeleteLidarr(r)}
              >
                <Trash2 size={14} strokeWidth={2} />
                <Cloud size={14} strokeWidth={2} />
                Delete via Lidarr
              </button>
            {/if}
          </div>
        </li>
      {/each}
    </ul>
  {/if}
</div>

{#if deleteFileRow}
  <!-- svelte-ignore a11y_click_events_have_key_events -->
  <!-- svelte-ignore a11y_no_static_element_interactions -->
  <div
    class="fixed inset-0 z-50 flex items-center justify-center"
    style="background: rgba(0,0,0,0.5);"
    onclick={cancelDeleteFile}
  >
    <div
      role="dialog"
      aria-modal="true"
      aria-labelledby="delete-file-title"
      class="w-full max-w-md rounded-xl border border-border bg-surface p-5 shadow-lg"
      onclick={(e) => e.stopPropagation()}
      tabindex="-1"
    >
      <h3 id="delete-file-title" class="font-display text-lg font-medium text-text-primary">
        Delete file?
      </h3>
      <p class="mt-2 text-sm text-text-secondary">
        Remove <em class="font-medium text-text-primary">{deleteFileRow.track_title}</em>
        from disk and clear {deleteFileRow.report_count} reports? Lidarr may auto-redownload depending on its monitor settings.
      </p>
      <div class="mt-5 flex justify-end gap-2">
        <button
          type="button"
          class="rounded-md border border-border px-3 py-1.5 text-sm text-text-secondary hover:text-text-primary"
          onclick={cancelDeleteFile}
        >
          Cancel
        </button>
        <button
          type="button"
          aria-label="Confirm delete file"
          class="inline-flex items-center gap-1 rounded-md bg-action-secondary px-3 py-1.5 text-sm text-action-fg"
          onclick={() => confirmDeleteFile(deleteFileRow!)}
        >
          <Trash2 size={14} strokeWidth={2} />
          Delete file
        </button>
      </div>
    </div>
  </div>
{/if}

{#if deleteLidarrRow}
  <!-- svelte-ignore a11y_click_events_have_key_events -->
  <!-- svelte-ignore a11y_no_static_element_interactions -->
  <div
    class="fixed inset-0 z-50 flex items-center justify-center"
    style="background: rgba(0,0,0,0.5);"
    onclick={cancelDeleteLidarr}
  >
    <div
      role="dialog"
      aria-modal="true"
      aria-labelledby="delete-lidarr-title"
      class="w-full max-w-md rounded-xl border border-border bg-surface p-5 shadow-lg"
      onclick={(e) => e.stopPropagation()}
      tabindex="-1"
    >
      <h3 id="delete-lidarr-title" class="font-display text-lg font-medium text-text-primary">
        Delete via Lidarr?
      </h3>
      <p class="mt-2 text-sm text-text-secondary">
        This will tell Lidarr to remove <em class="font-medium text-text-primary">{deleteLidarrRow.album_title}</em>
        (artist <em class="font-medium text-text-primary">{deleteLidarrRow.artist_name}</em>)
        and add it to the import-list exclusion. Affects all tracks on the album. Type <strong>DELETE</strong> to confirm.
      </p>
      <label class="mt-3 block">
        <span class="block text-sm text-text-secondary">Type DELETE to confirm</span>
        <input
          type="text"
          bind:value={deleteLidarrInput}
          placeholder="DELETE"
          class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent"
          aria-label="Type DELETE to confirm"
        />
      </label>
      {#if deleteLidarrError}
        <p class="mt-3 text-sm text-error" data-testid="delete-lidarr-error">
          {deleteLidarrError}
        </p>
      {/if}
      <div class="mt-5 flex justify-end gap-2">
        <button
          type="button"
          class="rounded-md border border-border px-3 py-1.5 text-sm text-text-secondary hover:text-text-primary"
          onclick={cancelDeleteLidarr}
        >
          Cancel
        </button>
        <button
          type="button"
          aria-label="Confirm delete via Lidarr"
          disabled={deleteLidarrInput.trim() !== 'DELETE'}
          class="inline-flex items-center gap-1 rounded-md bg-action-destructive px-3 py-1.5 text-sm text-action-fg disabled:cursor-not-allowed disabled:opacity-50"
          onclick={() => confirmDeleteLidarr(deleteLidarrRow!)}
        >
          <Trash2 size={14} strokeWidth={2} />
          <Cloud size={14} strokeWidth={2} />
          Delete via Lidarr
        </button>
      </div>
    </div>
  </div>
{/if}

{#if toast}
  <div
    role="status"
    aria-live="polite"
    class="fixed bottom-4 right-4 z-50 max-w-sm rounded-md border border-border bg-surface px-4 py-3 text-sm text-text-primary shadow-lg"
    data-testid="toast"
  >
    {toast}
  </div>
{/if}

<style>
  .reason-pill {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 11px;
    line-height: 14px;
    background: color-mix(in srgb, var(--fs-accent) 15%, transparent);
    color: var(--fs-accent);
  }
</style>
