<script lang="ts">
  import { onDestroy } from 'svelte';
  import { invoke } from '@tauri-apps/api/core';
  import { Volume2, Square } from 'lucide-svelte';
  import { settings } from '$lib/stores/page.svelte.js';
  import { activeSpeaker, setActiveSpeaker } from '$lib/stores/speaker.svelte.ts';

  let {
    text,
    label = 'Read aloud',
    size = 12,
  }: { text: string; label?: string; size?: number } = $props();

  // A stable id per mounted button. `crypto.randomUUID` is available
  // in any runtime recent enough to host Tauri's webview.
  const instanceId =
    typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
      ? crypto.randomUUID()
      : `sp_${Math.random().toString(36).slice(2)}`;

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

  const speaking = $derived(activeSpeaker.id === instanceId);

  async function toggle() {
    if (speaking) {
      await stop();
    } else {
      await start();
    }
  }

  async function start() {
    // If a different button is active, cancel its speech first so we
    // don't get two synths talking over each other.
    if (activeSpeaker.id !== null && activeSpeaker.id !== instanceId) {
      try {
        await invoke('tts_stop');
      } catch {
        // Best-effort: nothing to gain from surfacing a stop error.
      }
    }
    setActiveSpeaker(instanceId);
    try {
      await invoke('tts_speak', {
        text,
        rate: settings.ttsRate,
        voice: settings.ttsVoice ?? null,
      });
    } catch {
      setActiveSpeaker(null);
      return;
    }
    scheduleRevert();
  }

  async function stop() {
    clearRevert();
    setActiveSpeaker(null);
    try {
      await invoke('tts_stop');
    } catch {
      // Best-effort.
    }
  }

  // No platform exposes a "speech finished" signal cheaply, so we
  // estimate duration from word count and revert the icon when it
  // elapses. 150 wpm is a comfortable pace for British-English; the
  // user's rate slider shortens or lengthens the estimate.
  function scheduleRevert() {
    clearRevert();
    const words = text.trim().split(/\s+/).filter(Boolean).length;
    const baseSeconds = Math.max(2, Math.min(600, (words / 150) * 60));
    const rate = settings.ttsRate > 0 ? settings.ttsRate : 1.0;
    const ms = Math.round((baseSeconds * 1000) / rate);
    revertTimer = setTimeout(() => {
      if (activeSpeaker.id === instanceId) {
        setActiveSpeaker(null);
      }
    }, ms);
  }

  function clearRevert() {
    if (revertTimer !== null) {
      clearTimeout(revertTimer);
      revertTimer = null;
    }
  }

  onDestroy(() => {
    clearRevert();
    if (activeSpeaker.id === instanceId) {
      setActiveSpeaker(null);
      invoke('tts_stop').catch(() => {});
    }
  });
</script>

<button
  type="button"
  class="p-0.5 text-text-tertiary hover:text-accent {speaking ? '!text-accent' : ''}"
  onclick={toggle}
  aria-label={speaking ? 'Stop reading aloud' : label}
  title={speaking ? 'Stop reading aloud' : label}
>
  {#if speaking}
    <Square {size} aria-hidden="true" />
  {:else}
    <Volume2 {size} aria-hidden="true" />
  {/if}
</button>
