<script lang="ts">
  import { createEventDispatcher } from 'svelte';
  import { voiceStatus } from '$lib/stores/game';
  import { startRecording, blobToBase64 } from '$lib/voice';

  const dispatch = createEventDispatcher<{
    send: { audio_b64: string; mime_type: string };
  }>();

  let stopFn: (() => void) | null = null;

  async function onPressStart(e: MouseEvent | TouchEvent) {
    e.preventDefault();
    if ($voiceStatus !== 'idle') return;

    try {
      const { stop, result } = await startRecording();
      stopFn = stop;
      voiceStatus.set('recording');

      result.then(async ({ blob, mimeType }) => {
        const audio_b64 = await blobToBase64(blob);
        dispatch('send', { audio_b64, mime_type: mimeType });
      });
    } catch {
      voiceStatus.set('idle');
      console.error('Microphone not available');
    }
  }

  function onPressEnd(e: MouseEvent | TouchEvent) {
    e.preventDefault();
    if ($voiceStatus !== 'recording') return;
    stopFn?.();
    stopFn = null;
    voiceStatus.set('processing');
    // On mobile, prevent the browser from refocusing the last active input
    // which would reopen the virtual keyboard
    if (e instanceof TouchEvent) {
      (document.activeElement as HTMLElement | null)?.blur();
    }
  }

  async function onKeyDown(e: KeyboardEvent) {
    if (e.code !== 'Space' || !e.ctrlKey) return;
    e.preventDefault();
    if ($voiceStatus !== 'idle') return;
    try {
      const { stop, result } = await startRecording();
      stopFn = stop;
      voiceStatus.set('recording');
      result.then(async ({ blob, mimeType }) => {
        const audio_b64 = await blobToBase64(blob);
        dispatch('send', { audio_b64, mime_type: mimeType });
      });
    } catch {
      voiceStatus.set('idle');
      console.error('Microphone not available');
    }
  }

  function onKeyUp(e: KeyboardEvent) {
    if (e.code !== 'Space' || !e.ctrlKey) return;
    e.preventDefault();
    if ($voiceStatus !== 'recording') return;
    stopFn?.();
    stopFn = null;
    voiceStatus.set('processing');
  }
</script>

<svelte:window on:keydown={onKeyDown} on:keyup={onKeyUp} />

<button
  class="voice-btn"
  class:recording={$voiceStatus === 'recording'}
  class:processing={$voiceStatus === 'processing'}
  disabled={$voiceStatus === 'processing'}
  on:mousedown={onPressStart}
  on:mouseup={onPressEnd}
  on:mouseleave={onPressEnd}
  on:touchstart|nonpassive={onPressStart}
  on:touchend|nonpassive={onPressEnd}
  on:touchcancel={onPressEnd}
  aria-label="Hold to speak"
>
  {#if $voiceStatus === 'recording'}
    <span class="pulse-ring"></span>
    <span class="pulse-ring ring2"></span>
    <span class="mic-icon">🔴</span>
  {:else if $voiceStatus === 'processing'}
    <span class="spinner"></span>
  {:else}
    <span class="mic-icon">🎙️</span>
  {/if}

  <span class="btn-label">
    {#if $voiceStatus === 'recording'}Listening…
    {:else if $voiceStatus === 'processing'}Processing…
    {:else}Hold / Ctrl+Space
    {/if}
  </span>
</button>

<style>
  .voice-btn {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    width: 60px;
    height: 60px;
    border-radius: 50%;
    background: var(--surface2);
    border: 2px solid var(--border);
    color: var(--text);
    transition: all 0.15s;
    flex-shrink: 0;
    overflow: visible;
    touch-action: none;
    -webkit-touch-callout: none;
    user-select: none;
    -webkit-user-select: none;
  }

  .voice-btn:not(:disabled):active,
  .voice-btn.recording {
    transform: scale(0.96);
    border-color: var(--danger);
    background: rgba(248, 81, 73, 0.15);
  }

  .voice-btn.processing {
    border-color: var(--player);
    opacity: 0.7;
  }

  .mic-icon {
    font-size: 1.4rem;
    line-height: 1;
    margin-top: 10px;
  }

  .btn-label {
    position: absolute;
    bottom: -22px;
    font-size: 0.65rem;
    color: var(--text-muted);
    white-space: nowrap;
    pointer-events: none;
  }

  /* Pulse rings for recording */
  .pulse-ring {
    position: absolute;
    inset: -8px;
    border-radius: 50%;
    border: 2px solid var(--danger);
    opacity: 0;
    animation: pulse-out 1.2s ease-out infinite;
    pointer-events: none;
  }

  .ring2 { animation-delay: 0.4s; }

  @keyframes pulse-out {
    0%   { inset: -4px; opacity: 0.7; }
    100% { inset: -20px; opacity: 0; }
  }

  /* Processing spinner */
  .spinner {
    position: absolute;
    inset: 6px;
    border-radius: 50%;
    border: 3px solid var(--border);
    border-top-color: var(--player);
    animation: spin 0.7s linear infinite;
  }

  @keyframes spin { to { transform: rotate(360deg); } }
</style>
