<script lang="ts">
  import { onMount } from 'svelte';
  import { page } from '$app/stores';
  import { CookingPot, Globe, X } from 'lucide-svelte';
  import type { Snapshot } from './$types';
  import type { SearchHit } from '$lib/server/recipes/search-local';
  import type { WebHit } from '$lib/server/search/searxng';
  import { randomQuote } from '$lib/quotes';
  import SearchLoader from '$lib/components/SearchLoader.svelte';
  import { profileStore } from '$lib/client/profile.svelte';

  let query = $state('');
  let quote = $state('');
  let recent = $state<SearchHit[]>([]);
  let favorites = $state<SearchHit[]>([]);
  let hits = $state<SearchHit[]>([]);
  let webHits = $state<WebHit[]>([]);
  let searching = $state(false);
  let webSearching = $state(false);
  let webError = $state<string | null>(null);
  let searchedFor = $state<string | null>(null);
  let skipNextSearch = false;

  type SearchSnapshot = {
    query: string;
    hits: SearchHit[];
    webHits: WebHit[];
    searchedFor: string | null;
    webError: string | null;
  };

  export const snapshot: Snapshot<SearchSnapshot> = {
    capture: () => ({ query, hits, webHits, searchedFor, webError }),
    restore: (v) => {
      query = v.query;
      hits = v.hits;
      webHits = v.webHits;
      searchedFor = v.searchedFor;
      webError = v.webError;
      skipNextSearch = true;
    }
  };

  async function loadRecent() {
    const res = await fetch('/api/recipes/search');
    const body = await res.json();
    recent = body.hits;
  }

  async function loadFavorites(profileId: number) {
    const res = await fetch(`/api/recipes/favorites?profile_id=${profileId}`);
    if (!res.ok) {
      favorites = [];
      return;
    }
    const body = await res.json();
    favorites = body.hits;
  }

  onMount(() => {
    quote = randomQuote();
    // Restore query from URL so history.back() from preview/recipe
    // brings the user back to the same search results.
    const urlQ = ($page.url.searchParams.get('q') ?? '').trim();
    if (urlQ) query = urlQ;
    void loadRecent();
  });

  // Sync current query back into the URL as ?q=... via replaceState,
  // without spamming the history stack. Pushing a new entry happens only
  // when the user clicks a result or otherwise navigates away.
  $effect(() => {
    if (typeof window === 'undefined') return;
    const q = query.trim();
    const url = new URL(window.location.href);
    const current = url.searchParams.get('q') ?? '';
    if (q === current) return;
    if (q) url.searchParams.set('q', q);
    else url.searchParams.delete('q');
    history.replaceState(history.state, '', url.toString());
  });

  $effect(() => {
    const active = profileStore.active;
    if (!active) {
      favorites = [];
      return;
    }
    void loadFavorites(active.id);
  });

  let debounceTimer: ReturnType<typeof setTimeout> | null = null;

  async function runSearch(q: string) {
    try {
      const res = await fetch(`/api/recipes/search?q=${encodeURIComponent(q)}`);
      const body = await res.json();
      if (query.trim() !== q) return;
      hits = body.hits;
      searchedFor = q;
      if (body.hits.length === 0) {
        webSearching = true;
        try {
          const wres = await fetch(`/api/recipes/search/web?q=${encodeURIComponent(q)}`);
          if (query.trim() !== q) return;
          if (!wres.ok) {
            const err = await wres.json().catch(() => ({}));
            webError = err.message ?? `HTTP ${wres.status}`;
          } else {
            const wbody = await wres.json();
            webHits = wbody.hits;
          }
        } finally {
          if (query.trim() === q) webSearching = false;
        }
      }
    } finally {
      if (query.trim() === q) searching = false;
    }
  }

  $effect(() => {
    const q = query.trim();
    if (debounceTimer) clearTimeout(debounceTimer);
    if (skipNextSearch) {
      // Snapshot-Restore hat hits/webHits/searchedFor wiederhergestellt —
      // nicht erneut fetchen.
      skipNextSearch = false;
      return;
    }
    if (q.length <= 3) {
      hits = [];
      webHits = [];
      searchedFor = null;
      searching = false;
      webSearching = false;
      webError = null;
      return;
    }
    searching = true;
    webHits = [];
    webSearching = false;
    webError = null;
    debounceTimer = setTimeout(() => {
      void runSearch(q);
    }, 300);
  });

  function submit(e: SubmitEvent) {
    e.preventDefault();
    const q = query.trim();
    if (q.length <= 3) return;
    if (debounceTimer) clearTimeout(debounceTimer);
    searching = true;
    void runSearch(q);
  }

  async function dismissFromRecent(recipeId: number, e: MouseEvent) {
    e.preventDefault();
    e.stopPropagation();
    recent = recent.filter((r) => r.id !== recipeId);
    await fetch(`/api/recipes/${recipeId}`, {
      method: 'PATCH',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ hidden_from_recent: true })
    });
  }

  const activeSearch = $derived(query.trim().length > 3);
</script>

<section class="hero">
  <h1>Kochwas</h1>
  <p class="tagline" aria-live="polite">{quote || '\u00a0'}</p>
  <form onsubmit={submit}>
    <input
      type="search"
      bind:value={query}
      placeholder="Rezept suchen…"
      autocomplete="off"
      inputmode="search"
      aria-label="Suchbegriff"
    />
  </form>
</section>

{#if activeSearch}
  <section class="results">
    {#if searching && hits.length === 0}
      <SearchLoader scope="local" />
    {:else if hits.length > 0}
      <ul class="cards">
        {#each hits as r (r.id)}
          <li>
            <a href={`/recipes/${r.id}`} class="card">
              {#if r.image_path}
                <img src={`/images/${r.image_path}`} alt="" loading="lazy" />
              {:else}
                <div class="placeholder"><CookingPot size={36} /></div>
              {/if}
              <div class="card-body">
                <div class="title">{r.title}</div>
                {#if r.source_domain}
                  <div class="domain">{r.source_domain}</div>
                {/if}
              </div>
            </a>
          </li>
        {/each}
      </ul>
      <a class="web-more" href={`/search/web?q=${encodeURIComponent(query.trim())}`}>
        <Globe size={18} strokeWidth={2} /> Im Internet weitersuchen
      </a>
    {:else if searchedFor === query.trim()}
      <p class="muted no-local-msg">
        Keine lokalen Rezepte für „{searchedFor}" — Ergebnisse aus dem Internet:
      </p>
      {#if webSearching}
        <SearchLoader scope="web" />
      {:else if webError}
        <p class="error">Internet-Suche zurzeit nicht möglich: {webError}</p>
      {:else if webHits.length > 0}
        <ul class="cards">
          {#each webHits as w (w.url)}
            <li>
              <a class="card" href={`/preview?url=${encodeURIComponent(w.url)}`}>
                {#if w.thumbnail}
                  <img src={w.thumbnail} alt="" loading="lazy" />
                {:else}
                  <div class="placeholder"><CookingPot size={36} /></div>
                {/if}
                <div class="card-body">
                  <div class="title">{w.title}</div>
                  <div class="domain">{w.domain}</div>
                </div>
              </a>
            </li>
          {/each}
        </ul>
      {:else}
        <p class="muted">Auch im Internet nichts gefunden.</p>
      {/if}
    {/if}
  </section>
{:else}
  {#if profileStore.active && favorites.length > 0}
    <section class="listing">
      <h2>Deine Favoriten</h2>
      <ul class="cards">
        {#each favorites as r (r.id)}
          <li class="card-wrap">
            <a href={`/recipes/${r.id}`} class="card">
              {#if r.image_path}
                <img src={`/images/${r.image_path}`} alt="" loading="lazy" />
              {:else}
                <div class="placeholder"><CookingPot size={36} /></div>
              {/if}
              <div class="card-body">
                <div class="title">{r.title}</div>
                {#if r.source_domain}
                  <div class="domain">{r.source_domain}</div>
                {/if}
              </div>
            </a>
          </li>
        {/each}
      </ul>
    </section>
  {/if}
  {#if recent.length > 0}
    <section class="listing">
      <h2>Zuletzt hinzugefügt</h2>
      <ul class="cards">
        {#each recent as r (r.id)}
          <li class="card-wrap">
            <a href={`/recipes/${r.id}`} class="card">
              {#if r.image_path}
                <img src={`/images/${r.image_path}`} alt="" loading="lazy" />
              {:else}
                <div class="placeholder"><CookingPot size={36} /></div>
              {/if}
              <div class="card-body">
                <div class="title">{r.title}</div>
                {#if r.source_domain}
                  <div class="domain">{r.source_domain}</div>
                {/if}
              </div>
            </a>
            <button
              class="dismiss"
              aria-label="Aus Zuletzt-hinzugefügt entfernen"
              onclick={(e) => dismissFromRecent(r.id, e)}
            >
              <X size={16} strokeWidth={2.5} />
            </button>
          </li>
        {/each}
      </ul>
    </section>
  {/if}
{/if}

<style>
  .hero {
    text-align: center;
    padding: 3rem 0 1.5rem;
  }
  .hero h1 {
    font-size: clamp(2.2rem, 8vw, 3.5rem);
    margin: 0 0 0.5rem;
    color: #2b6a3d;
    letter-spacing: -0.02em;
  }
  .tagline {
    margin: 0 auto 1.5rem;
    max-width: 36rem;
    color: #6a7670;
    font-style: italic;
    font-size: 1rem;
    line-height: 1.35;
    min-height: 1.4rem;
  }
  form {
    display: flex;
    gap: 0.5rem;
  }
  input[type='search'] {
    flex: 1;
    padding: 0.9rem 1rem;
    font-size: 1.1rem;
    border: 1px solid #cfd9d1;
    border-radius: 10px;
    background: white;
    min-height: 48px;
  }
  input[type='search']:focus {
    outline: 2px solid #2b6a3d;
    outline-offset: 1px;
  }
  .results,
  .listing {
    margin-top: 1.5rem;
  }
  .listing h2 {
    font-size: 1.05rem;
    color: #444;
    margin: 0 0 0.75rem;
  }
  .muted {
    color: #888;
    text-align: center;
    padding: 1rem 0;
  }
  .no-local-msg {
    font-size: 0.95rem;
    padding: 0.25rem 0 1rem;
  }
  .error {
    color: #c53030;
    text-align: center;
    padding: 1rem 0;
  }
  .cards {
    list-style: none;
    padding: 0;
    margin: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 0.75rem;
  }
  .card-wrap {
    position: relative;
  }
  .card {
    display: block;
    background: white;
    border: 1px solid #e4eae7;
    border-radius: 14px;
    overflow: hidden;
    text-decoration: none;
    color: inherit;
    transition: transform 0.1s;
  }
  .card:active {
    transform: scale(0.98);
  }
  .card img,
  .placeholder {
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    background: #eef3ef;
    display: grid;
    place-items: center;
    color: #8fb097;
  }
  .card-body {
    padding: 0.6rem 0.75rem 0.75rem;
  }
  .title {
    font-weight: 600;
    font-size: 0.95rem;
    line-height: 1.25;
  }
  .domain {
    font-size: 0.8rem;
    color: #888;
    margin-top: 0.25rem;
  }
  .dismiss {
    position: absolute;
    top: 0.4rem;
    right: 0.4rem;
    width: 28px;
    height: 28px;
    border-radius: 999px;
    border: 0;
    background: rgba(255, 255, 255, 0.9);
    color: #444;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
    opacity: 0;
    transition: opacity 0.1s;
  }
  .card-wrap:hover .dismiss,
  .dismiss:focus-visible {
    opacity: 1;
  }
  @media (max-width: 640px) {
    .dismiss {
      opacity: 1; /* always visible on touch devices */
    }
  }
  .dismiss:hover {
    background: #fff;
    color: #c53030;
  }
  .web-more {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: 1rem;
    padding: 0.7rem 1.1rem;
    border: 1px solid #b7d6c2;
    border-radius: 10px;
    text-decoration: none;
    color: #2b6a3d;
    background: #eaf4ed;
    font-size: 0.95rem;
  }
  .web-more:hover {
    background: #d8e8df;
  }
</style>
