<script lang="ts">
  import { onDestroy, onMount } from 'svelte';
  import Screen from '../components/Screen.svelte';
  import { gateway } from '../lib/gateway';
  import { app, handleError, showToast } from '../lib/app.svelte';
  import { connection } from '../lib/connection.svelte';
  import { navigate } from '../lib/router.svelte';
  import { t, type MessageKey } from '../lib/i18n/index.svelte';
  import type { AccountRef, Variant } from '../lib/model';
  import {
    availableVariants,
    VARIANT_FLAG,
    VARIANT_RULES,
    supportsMultipleWordsToggle,
    multipleWordsForRequest,
  } from '../lib/variants';

  // The auto-match move clock (mirrors backend game.DefaultTurnTimeout = 24h).
  const AUTO_MATCH_HOURS = 24;

  // The offered variants are gated by the languages the sign-in service supports;
  // the auto-match list and the friend-invite picker both use this.
  const variants = $derived(availableVariants(app.session?.supportedLanguages));
  // "Multiple words per turn" off is the single-word rule; it is offered for Russian games
  // only (English is always standard and shows no toggle). Shared by both flows.
  let multipleWords = $state(false);
  const autoHasRussian = $derived(variants.some((v) => supportsMultipleWordsToggle(v.id)));
  const timeouts = [
    { secs: 300, key: 'time.minutes' as MessageKey, n: 5 },
    { secs: 1800, key: 'time.minutes' as MessageKey, n: 30 },
    { secs: 3600, key: 'time.hours' as MessageKey, n: 1 },
    { secs: 86400, key: 'time.hours' as MessageKey, n: 24 },
  ];

  const guest = $derived(app.profile?.isGuest ?? true);
  let mode = $state<'auto' | 'friends'>('auto');

  // --- auto-match ---
  let searching = $state(false);
  // matched guards the teardown: once a match arrives (immediately, via the match_found push, or
  // via the fallback poll) onDestroy must not dequeue the game we just got.
  let matched = $state(false);
  let poll: ReturnType<typeof setInterval> | null = null;

  function stop() {
    if (poll) {
      clearInterval(poll);
      poll = null;
    }
  }
  // startPoll is the matchmaking fallback used only while the live stream is down: with the stream
  // up the match_found push drives navigation. It polls lobby.poll every 2.5s.
  function startPoll() {
    if (poll) return;
    poll = setInterval(async () => {
      try {
        const p = await gateway.lobbyPoll();
        if (p.matched && p.game) {
          matched = true;
          searching = false;
          stop();
          navigate(`/game/${p.game.id}`);
        }
      } catch (e) {
        handleError(e);
      }
    }, 2500);
  }
  // cancelSearch leaves the auto-match pool on the backend too, so a cancelled quick-match
  // is actually dequeued — otherwise the account stays queued (blocking a re-queue) and the
  // reaper later substitutes a robot for a game the player abandoned.
  function cancelSearch() {
    stop();
    searching = false;
    void gateway.lobbyCancel().catch(() => {});
    navigate('/');
  }

  async function find(v: Variant) {
    searching = true;
    matched = false;
    try {
      const r = await gateway.lobbyEnqueue(v, multipleWordsForRequest(v, multipleWords));
      if (r.matched && r.game) {
        matched = true;
        searching = false;
        navigate(`/game/${r.game.id}`);
      }
    } catch (e) {
      searching = false;
      handleError(e);
    }
    // No immediate match: wait for the match_found push; the effect below polls only when the
    // stream is down.
  }

  // Poll for the match only while searching and the stream is down (the push cannot reach us);
  // stop once the stream is back or the search ends.
  $effect(() => {
    if (searching && !app.streamAlive) startPoll();
    else stop();
  });
  // match_found is handled globally (app.svelte navigates); end the search here too so onDestroy
  // does not cancel the match we just received.
  $effect(() => {
    if (app.lastEvent?.kind === 'match_found' && searching) {
      matched = true;
      searching = false;
    }
  });

  // --- friend game ---
  let friends = $state<AccountRef[]>([]);
  let selected = $state<string[]>([]);
  let friendFilter = $state('');
  // No default game type yet — the player must pick one (a smarter default from play
  // history / language would be a future refinement). '' renders the disabled placeholder option.
  let inviteVariant = $state<Variant | ''>('');
  let timeoutSecs = $state(86400);
  let hints = $state(1);

  const filteredFriends = $derived(
    friendFilter.trim()
      ? friends.filter((f) => f.displayName.toLowerCase().includes(friendFilter.trim().toLowerCase()))
      : friends,
  );

  onMount(async () => {
    if (guest) return;
    try {
      friends = await gateway.friendsList();
    } catch (e) {
      handleError(e);
    }
  });

  function toggle(id: string) {
    selected = selected.includes(id) ? selected.filter((x) => x !== id) : [...selected, id];
  }

  async function sendInvite() {
    if (selected.length === 0 || selected.length > 3 || !inviteVariant) return;
    try {
      await gateway.invitationCreate(selected, {
        variant: inviteVariant,
        turnTimeoutSecs: timeoutSecs,
        hintsAllowed: hints > 0,
        hintsPerPlayer: hints,
        dropoutTiles: 'remove',
        multipleWordsPerTurn: multipleWordsForRequest(inviteVariant, multipleWords),
      });
      showToast(t('new.invited'));
      navigate('/');
    } catch (e) {
      handleError(e);
    }
  }

  onDestroy(() => {
    stop();
    // Abandoned mid-search without a match (navigated away without Cancel): dequeue so we don't
    // linger. A received match (matched) must not be cancelled.
    if (searching && !matched) void gateway.lobbyCancel().catch(() => {});
  });
</script>

<Screen title={t('new.title')} back="/">
  <div class="page">
    {#if searching}
      <div class="searching">
        <div class="spinner"></div>
        <p>{t('new.searching')}</p>
        <button class="cancel" onclick={cancelSearch}>{t('common.cancel')}</button>
      </div>
    {:else}
      {#if !guest}
        <div class="seg modes">
          <button class="opt" class:active={mode === 'auto'} onclick={() => (mode = 'auto')}>{t('new.auto')}</button>
          <button class="opt" class:active={mode === 'friends'} onclick={() => (mode = 'friends')}>{t('new.withFriends')}</button>
        </div>
      {/if}

      {#if mode === 'auto'}
        <p class="subtitle">{t('new.subtitle')}</p>
        {#if autoHasRussian}
          <label class="toggle">
            <span>{t('new.multipleWordsPerTurn')}</span>
            <input type="checkbox" bind:checked={multipleWords} />
          </label>
        {/if}
        <div class="variants">
          {#each variants as v (v.id)}
            <button class="variant" onclick={() => find(v.id)} disabled={!connection.online}>
              <span class="vmain">
                <span class="vname">{t(v.label)}</span>
                {#if VARIANT_FLAG[v.id]}
                  <span class="vflag">{VARIANT_FLAG[v.id]}</span>
                {:else}
                  <img class="vflag-img" src="flag-ussr.svg" alt="" />
                {/if}
              </span>
              <span class="vrules">{t(VARIANT_RULES[v.id])}</span>
            </button>
          {/each}
        </div>
        <p class="movelimit">{t('new.moveLimit', { n: AUTO_MATCH_HOURS })}</p>
      {:else if friends.length === 0}
        <p class="subtitle">{t('new.noFriends')}</p>
      {:else}
        <div class="fg">
          <div class="picked">
            <span class="ftitle">{t('new.pickFriends')} ({selected.length})</span>
            <input class="search" bind:value={friendFilter} placeholder={t('new.searchFriends')} />
          </div>
          <div class="friends-scroll">
            {#each filteredFriends as f (f.accountId)}
              <label class="friend">
                <input type="checkbox" checked={selected.includes(f.accountId)} onchange={() => toggle(f.accountId)} />
                <span>{f.displayName}</span>
              </label>
            {/each}
            {#if filteredFriends.length === 0}<p class="muted">—</p>{/if}
          </div>
          <div class="settings-row">
            <label class="field">
              <span>{t('new.gameType')}</span>
              <select bind:value={inviteVariant} class:placeholder={!inviteVariant}>
                <option value="" disabled>—</option>
                {#each variants as v (v.id)}<option value={v.id}>{t(v.label)}</option>{/each}
              </select>
            </label>
            <label class="field">
              <span>{t('new.moveTime')}</span>
              <select bind:value={timeoutSecs}>
                {#each timeouts as to (to.secs)}<option value={to.secs}>{t(to.key, { n: to.n })}</option>{/each}
              </select>
            </label>
            <label class="field">
              <span>{t('new.hintsPerPlayer')}</span>
              <select bind:value={hints}>
                {#each [0, 1, 2] as h (h)}<option value={h}>{h}</option>{/each}
              </select>
            </label>
          </div>
          {#if inviteVariant && supportsMultipleWordsToggle(inviteVariant)}
            <label class="toggle">
              <span>{t('new.multipleWordsPerTurn')}</span>
              <input type="checkbox" bind:checked={multipleWords} />
            </label>
          {/if}
          <button class="invite" disabled={selected.length === 0 || !inviteVariant || !connection.online} onclick={sendInvite}>{t('new.invite')}</button>
        </div>
      {/if}
    {/if}
  </div>
</Screen>

<style>
  .page {
    padding: var(--pad);
    display: flex;
    flex-direction: column;
    gap: 14px;
    height: 100%;
    box-sizing: border-box;
  }
  .subtitle {
    color: var(--text-muted);
    margin: 0;
  }
  .variants {
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
  /* A plaque per variant (like the lobby game cards): the name with its flag on the right,
     and a one-line rules summary below. */
  .variant {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 12px 14px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    border-radius: var(--radius);
    text-align: left;
    user-select: none;
  }
  .vmain {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
  }
  .vname {
    font-size: 1.05rem;
    font-weight: 600;
  }
  .vflag {
    font-size: 1.3rem;
    line-height: 1;
  }
  .vflag-img {
    width: 1.6rem;
    height: auto;
    border-radius: 2px;
  }
  .vrules {
    font-size: 0.8rem;
    color: var(--text-muted);
  }
  .movelimit {
    margin: 0;
    text-align: center;
    color: var(--text-muted);
    font-size: 0.85rem;
  }
  .seg {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
  }
  .modes {
    margin-bottom: 4px;
  }
  .opt {
    flex: 1;
    min-width: 64px;
    padding: 10px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    border-radius: var(--radius-sm);
    user-select: none;
  }
  .opt.active {
    background: var(--accent);
    color: var(--accent-text);
    border-color: var(--accent);
  }
  .fg {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
  .picked {
    display: flex;
    flex-direction: column;
    gap: 8px;
  }
  .ftitle {
    font-size: 0.9rem;
    color: var(--text-muted);
  }
  .search {
    min-width: 0;
    padding: 9px 11px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    border-radius: var(--radius-sm);
  }
  .friends-scroll {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 6px;
  }
  .friend {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    background: var(--surface);
    border-radius: var(--radius-sm);
  }
  .settings-row {
    display: flex;
    gap: 8px;
  }
  .field {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
  }
  .field span {
    font-size: 0.78rem;
    color: var(--text-muted);
  }
  .field select {
    width: 100%;
    box-sizing: border-box;
    min-width: 0;
    padding: 9px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    border-radius: var(--radius-sm);
  }
  .field select.placeholder {
    color: var(--text-muted);
  }
  .toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 11px;
    border: 1px solid var(--border);
    background: var(--surface);
    border-radius: var(--radius-sm);
    user-select: none;
  }
  .toggle span {
    font-size: 0.85rem;
    color: var(--text);
  }
  .muted {
    color: var(--text-muted);
    margin: 0;
  }
  .invite {
    flex: 0 0 auto;
    padding: 14px;
    border: 1px solid var(--accent);
    background: var(--accent);
    color: var(--accent-text);
    border-radius: var(--radius);
    font-weight: 600;
  }
  .invite:disabled {
    opacity: 0.5;
  }
  .searching {
    display: grid;
    place-items: center;
    gap: 14px;
    padding: 48px 0;
    color: var(--text-muted);
  }
  .spinner {
    width: 36px;
    height: 36px;
    border: 3px solid var(--border);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
  }
  @keyframes spin {
    to {
      transform: rotate(360deg);
    }
  }
  .cancel {
    padding: 8px 16px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    border-radius: var(--radius-sm);
  }
</style>
