<script lang="ts">
  import { pageTitle } from '$lib/branding';
  import { Music2, RotateCcw, Trash2, Cloud, Play, ChevronRight } from 'lucide-svelte';
  import { useQueryClient } from '@tanstack/svelte-query';
  import RowActionsMenu, { type RowAction } from '$lib/components/RowActionsMenu.svelte';
  import {
    createAdminQuarantineQuery,
    resolveQuarantine,
    deleteQuarantineFile,
    deleteQuarantineViaLidarr
  } from '$lib/api/admin';
  import { qk } from '$lib/api/queries';
  import { errMessage } from '$lib/api/errors';
  import { pushToast } from '$lib/stores/toast.svelte';
  import { playRadio } from '$lib/player/store.svelte';
  import { coverUrl } from '$lib/media/covers';
  import Modal from '$lib/components/Modal.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);

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

  async function onResolve(r: AdminQuarantineRow) {
    try {
      await resolveQuarantine(r.track_id);
      await invalidate();
    } catch (e) {
      pushToast(errMessage(e), 'error');
    }
  }

  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) {
      pushToast(errMessage(e), 'error');
    }
  }

  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 msg = errMessage(e);
      // Inline error keeps the modal open so the operator sees the failure
      // alongside the album they were about to remove.
      deleteLidarrError = msg;
      // Also surface as toast so it's visible after dismissing the modal.
      pushToast(msg, 'error');
    }
  }

  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>

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

<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}
        {@const primary: RowAction = {
          icon: RotateCcw,
          label: 'Resolve',
          ariaLabel: `Resolve ${r.track_title}`,
          onclick: () => onResolve(r)
        }}
        {@const secondary: RowAction[] = [
          {
            icon: Play,
            label: 'Play',
            ariaLabel: `Play ${r.track_title}`,
            onclick: () => onPlay(r)
          },
          {
            icon: Trash2,
            label: 'Delete file',
            ariaLabel: `Delete file for ${r.track_title}`,
            onclick: () => openDeleteFile(r),
            danger: true
          }
        ]}
        <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={coverUrl(r.album_id)}
                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">
            <RowActionsMenu {primary} {secondary} />

            {#if lidarrDisabled}
              <span
                aria-label={`Delete via Lidarr disabled for ${r.track_title}`}
                title="Local-only track — no Lidarr album to remove."
                class="hidden md: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="hidden md: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>

<Modal
  title="Delete file?"
  open={deleteFileRow !== null}
  onClose={cancelDeleteFile}
>
  {#if deleteFileRow}
    <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>
  {/if}
</Modal>

<Modal
  title="Delete via Lidarr?"
  open={deleteLidarrRow !== null}
  onClose={cancelDeleteLidarr}
>
  {#if deleteLidarrRow}
    <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>
  {/if}
</Modal>

<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>
