<script lang="ts">
  import { pageTitle } from '$lib/branding';
  import { createLidarrSearchQuery } from '$lib/api/lidarr';
  import { createRequest } from '$lib/api/requests';
  import { qk } from '$lib/api/queries';
  import { useQueryClient } from '@tanstack/svelte-query';
  import DiscoverResultCard from '$lib/components/DiscoverResultCard.svelte';
  import DiscoverTabs from '$lib/components/DiscoverTabs.svelte';
  import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
  import SuggestionFeed from '$lib/components/SuggestionFeed.svelte';
  import Modal from '$lib/components/Modal.svelte';
  import type {
    LidarrRequestKind,
    LidarrSearchResult
  } from '$lib/api/types';

  const queryClient = useQueryClient();

  // Local input is the source of truth for the discover query — Shell's global
  // SearchInput targets /search, not /discover. We debounce by 250ms before
  // pushing into `debouncedQ`, which is what the query factory observes.
  let inputValue = $state('');
  let debouncedQ = $state('');
  let activeKind: LidarrRequestKind = $state('artist');

  let debounceTimer: ReturnType<typeof setTimeout> | null = null;
  $effect(() => {
    const v = inputValue;
    if (debounceTimer) clearTimeout(debounceTimer);
    debounceTimer = setTimeout(() => {
      debouncedQ = v.trim();
    }, 250);
    return () => {
      if (debounceTimer) clearTimeout(debounceTimer);
    };
  });

  const queryStore = $derived(createLidarrSearchQuery(debouncedQ, activeKind));
  const query = $derived($queryStore);
  const results = $derived((query.data ?? []) as LidarrSearchResult[]);

  // Track-kind confirm modal: nulled out = closed.
  let modalResult: LidarrSearchResult | null = $state(null);

  // In-session optimistic flip — once a Request goes through, we flip the
  // card to 'requested' immediately. Cleared only on full page reload, which
  // is fine for a short-lived discover session.
  let optimisticRequested: Set<string> = $state(new Set());

  function cardState(r: LidarrSearchResult) {
    if (r.in_library) return 'kept' as const;
    if (r.requested || optimisticRequested.has(r.mbid)) return 'requested' as const;
    return 'requestable' as const;
  }

  // Backend validates kind→required-MBID-fields, not the human strings, so
  // imperfect derivations of artist_name from secondary_text won't block the
  // request. We pass the data we have and let the server take care of the rest.
  function buildRequestParams(r: LidarrSearchResult, kind: LidarrRequestKind) {
    if (kind === 'artist') {
      return {
        kind,
        lidarr_artist_mbid: r.artist_mbid || r.mbid,
        artist_name: r.name
      };
    }
    if (kind === 'album') {
      return {
        kind,
        lidarr_artist_mbid: r.artist_mbid,
        lidarr_album_mbid: r.album_mbid || r.mbid,
        artist_name: r.secondary_text,
        album_title: r.name
      };
    }
    // track
    return {
      kind,
      lidarr_artist_mbid: r.artist_mbid,
      lidarr_album_mbid: r.album_mbid,
      lidarr_track_mbid: r.mbid,
      artist_name: r.secondary_text,
      album_title: r.secondary_text,
      track_title: r.name
    };
  }

  async function submitRequest(r: LidarrSearchResult) {
    try {
      await createRequest(buildRequestParams(r, activeKind));
      const next = new Set(optimisticRequested);
      next.add(r.mbid);
      optimisticRequested = next;
      // Surface the new request on /requests right away (+ start its poll) (#369).
      queryClient.invalidateQueries({ queryKey: qk.myRequests() });
    } catch {
      // Swallow for v1: error toasts land in a later UX pass. The card stays
      // in 'requestable' so the user can retry.
    }
  }

  function handleRequestClick(r: LidarrSearchResult) {
    if (activeKind === 'track') {
      modalResult = r;
    } else {
      submitRequest(r);
    }
  }

  function confirmModal() {
    if (modalResult) submitRequest(modalResult);
    modalResult = null;
  }

  function cancelModal() {
    modalResult = null;
  }

  const tabs: { kind: LidarrRequestKind; label: string }[] = [
    { kind: 'artist', label: 'Artists' },
    { kind: 'album', label: 'Albums' },
    { kind: 'track', label: 'Tracks' }
  ];
</script>

<svelte:head><title>{pageTitle('Discover')}</title></svelte:head>

<!--
  Page layout: a full-height flex column so the persistent chrome
  (DiscoverTabs, search input, facet tabs) stays anchored at the top
  while only the result grid scrolls. Main has overflow-y-auto, but
  with h-full + flex-col + min-h-0 on the scroll region, this page
  contains its own overflow and main's scroll never triggers.
-->
<div class="flex h-full flex-col gap-4">
  <DiscoverTabs />

  <input
    type="search"
    aria-label="Search Lidarr"
    placeholder="Search artists, albums, or tracks"
    bind:value={inputValue}
    class="w-full rounded-md border border-border bg-background px-3 py-2 text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent"
  />

  {#if debouncedQ === ''}
    <div class="min-h-0 flex-1 overflow-y-auto pb-4">
      <SuggestionFeed />
    </div>
  {:else}
    <header class="space-y-1">
      <h2 class="font-display text-2xl font-medium text-text-primary">
        Add music to the library
      </h2>
      <p class="text-text-secondary">
        Search Lidarr to add new artists, albums, or tracks.
      </p>
    </header>

    <nav aria-label="Result kind" class="border-b border-border">
      <ul class="flex gap-2">
        {#each tabs as tab (tab.kind)}
          <li>
            <button
              type="button"
              aria-pressed={activeKind === tab.kind}
              class="border-b-2 px-3 py-2 text-sm {activeKind === tab.kind
                ? 'border-accent text-text-primary'
                : 'border-transparent text-text-secondary hover:text-text-primary'}"
              onclick={() => (activeKind = tab.kind)}
            >
              {tab.label}
            </button>
          </li>
        {/each}
      </ul>
    </nav>

    <div class="min-h-0 flex-1 overflow-y-auto pb-4">
      {#if query.isError}
        <ApiErrorBanner error={query.error} onRetry={query.refetch} />
      {:else if query.isPending}
        <p class="text-text-secondary">Searching…</p>
      {:else if results.length === 0}
        <p class="text-text-secondary">Nothing to add for that search yet.</p>
      {:else}
        <div class="grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
          {#each results as r (r.mbid)}
            <DiscoverResultCard
              kind={activeKind}
              title={r.name}
              subtitle={r.secondary_text}
              imageUrl={r.image_url || undefined}
              state={cardState(r)}
              onRequest={() => handleRequestClick(r)}
            />
          {/each}
        </div>
      {/if}
    </div>
  {/if}
</div>

<Modal
  title="Add the album?"
  open={modalResult !== null}
  onClose={cancelModal}
>
  {#if modalResult}
    <p class="-mt-2 text-text-secondary">
      Requesting <em class="font-medium text-text-primary">{modalResult.name}</em>
      will add <em class="font-medium text-text-primary">{modalResult.secondary_text}</em>.
      Continue?
    </p>
    <div class="mt-5 flex justify-end gap-2">
      <button
        type="button"
        class="rounded-md bg-action-secondary px-3 py-1.5 text-sm text-action-fg"
        onclick={cancelModal}
      >
        Cancel
      </button>
      <button
        type="button"
        class="rounded-md bg-action-primary px-3 py-1.5 text-sm text-action-fg"
        onclick={confirmModal}
      >
        Add the album
      </button>
    </div>
  {/if}
</Modal>
