<script lang="ts">
  import { page } from '$app/stores';
  import { goto } from '$app/navigation';
  import type { SearchHit } from '$lib/server/recipes/search-local';

  const PAGE_SIZE = 30;

  let query = $state(($page.url.searchParams.get('q') ?? '').trim());
  let hits = $state<SearchHit[]>([]);
  let loading = $state(false);
  let loadingMore = $state(false);
  let exhausted = $state(false);
  let searched = $state(false);
  let canWebSearch = $state(false);

  async function run(q: string) {
    loading = true;
    searched = true;
    canWebSearch = true;
    exhausted = false;
    const res = await fetch(
      `/api/recipes/search?q=${encodeURIComponent(q)}&limit=${PAGE_SIZE}`
    );
    const body = await res.json();
    hits = body.hits;
    exhausted = hits.length < PAGE_SIZE;
    loading = false;
    if (hits.length === 0) {
      // Kein lokaler Treffer → automatisch im Internet weitersuchen.
      // replaceState, damit die Zurück-Taste nicht zwischen leerer Liste und Web-Suche pingt.
      void goto(`/search/web?q=${encodeURIComponent(q)}`, { replaceState: true });
    }
  }

  async function loadMore() {
    if (loadingMore || exhausted || !query) return;
    loadingMore = true;
    try {
      const res = await fetch(
        `/api/recipes/search?q=${encodeURIComponent(query)}&limit=${PAGE_SIZE}&offset=${hits.length}`
      );
      const body = await res.json();
      const more = body.hits as SearchHit[];
      // Gegen Doppel-Treffer absichern (z.B. Race oder identisches bm25-Scoring).
      const seen = new Set(hits.map((h) => h.id));
      const deduped = more.filter((h) => !seen.has(h.id));
      hits = [...hits, ...deduped];
      if (more.length < PAGE_SIZE) exhausted = true;
    } finally {
      loadingMore = false;
    }
  }

  $effect(() => {
    const q = ($page.url.searchParams.get('q') ?? '').trim();
    query = q;
    if (q) void run(q);
  });

  function submit(e: SubmitEvent) {
    e.preventDefault();
    goto(`/search?q=${encodeURIComponent(query.trim())}`);
  }
</script>

<form class="search-bar" onsubmit={submit}>
  <input
    type="search"
    bind:value={query}
    placeholder="Rezept suchen…"
    autocomplete="off"
    inputmode="search"
    aria-label="Suchbegriff"
  />
  <button type="submit">Suchen</button>
</form>

{#if loading}
  <p class="muted">Suche läuft …</p>
{:else if searched && hits.length === 0}
  <section class="empty">
    <p>Kein lokales Rezept für „{query}" — suche jetzt im Internet …</p>
  </section>
{:else if hits.length > 0}
  <ul class="hits">
    {#each hits as r (r.id)}
      <li>
        <a href={`/recipes/${r.id}`} class="hit">
          {#if r.image_path}
            <img src={`/images/${r.image_path}`} alt="" loading="lazy" />
          {:else}
            <div class="placeholder">🥘</div>
          {/if}
          <div class="hit-body">
            <div class="title">{r.title}</div>
            <div class="meta">
              {#if r.source_domain}<span>{r.source_domain}</span>{/if}
              {#if r.avg_stars !== null}
                <span>★ {r.avg_stars.toFixed(1)}</span>
              {/if}
              {#if r.last_cooked_at}
                <span>Zuletzt: {new Date(r.last_cooked_at).toLocaleDateString('de-DE')}</span>
              {/if}
            </div>
          </div>
        </a>
      </li>
    {/each}
  </ul>
  {#if !exhausted}
    <div class="more-cta">
      <button class="more-btn" onclick={loadMore} disabled={loadingMore}>
        {loadingMore ? 'Lade …' : '+ weitere Ergebnisse'}
      </button>
    </div>
  {/if}
  {#if canWebSearch}
    <div class="web-cta">
      <a class="web-btn" href={`/search/web?q=${encodeURIComponent(query)}`}>
        🌐 Im Internet weitersuchen
      </a>
    </div>
  {/if}
{/if}

<style>
  .search-bar {
    display: flex;
    gap: 0.5rem;
    padding: 1rem 0;
    position: sticky;
    top: 0;
    background: #f8faf8;
  }
  input[type='search'] {
    flex: 1;
    padding: 0.8rem 1rem;
    font-size: 1.05rem;
    border: 1px solid #cfd9d1;
    border-radius: 10px;
    min-height: 48px;
  }
  button {
    padding: 0.8rem 1.2rem;
    background: #2b6a3d;
    color: white;
    border: 0;
    border-radius: 10px;
    min-height: 48px;
    cursor: pointer;
  }
  .muted {
    color: #888;
    text-align: center;
    margin-top: 2rem;
  }
  .empty {
    text-align: center;
    margin-top: 2rem;
  }
  .hits {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
  }
  .hit {
    display: flex;
    gap: 0.75rem;
    background: white;
    border: 1px solid #e4eae7;
    border-radius: 14px;
    overflow: hidden;
    text-decoration: none;
    color: inherit;
    min-height: 96px;
  }
  .hit img,
  .placeholder {
    width: 104px;
    min-height: 96px;
    object-fit: cover;
    background: #eef3ef;
    display: grid;
    place-items: center;
    font-size: 2rem;
  }
  .hit-body {
    flex: 1;
    padding: 0.75rem 0.9rem;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
  .title {
    font-weight: 600;
    font-size: 1rem;
    line-height: 1.25;
  }
  .meta {
    display: flex;
    gap: 0.6rem;
    margin-top: 0.25rem;
    color: #888;
    font-size: 0.8rem;
    flex-wrap: wrap;
  }
  .more-cta {
    margin-top: 1rem;
    text-align: center;
  }
  .more-btn {
    padding: 0.7rem 1.25rem;
    background: white;
    color: #2b6a3d;
    border: 1px solid #cfd9d1;
    border-radius: 10px;
    font-size: 0.95rem;
    min-height: 44px;
    cursor: pointer;
  }
  .more-btn:hover:not(:disabled) {
    background: #f4f8f5;
  }
  .more-btn:disabled {
    opacity: 0.6;
    cursor: progress;
  }
  .web-cta {
    margin-top: 1.25rem;
    text-align: center;
  }
  .web-btn {
    display: inline-block;
    padding: 0.8rem 1.25rem;
    background: #2b6a3d;
    color: white;
    text-decoration: none;
    border-radius: 10px;
    font-size: 1rem;
    min-height: 48px;
  }
</style>
