<script lang="ts">
  import { invoke, convertFileSrc } from '@tauri-apps/api/core';
  import { listen } from '@tauri-apps/api/event';
  import { open, save } from '@tauri-apps/plugin-dialog';
  import WaveformPlayer from '$lib/components/WaveformPlayer.svelte';
  import TranscriptEditor from '$lib/components/TranscriptEditor.svelte';
  import SpeakerManager from '$lib/components/SpeakerManager.svelte';
  import AIChatPanel from '$lib/components/AIChatPanel.svelte';
  import ProgressOverlay from '$lib/components/ProgressOverlay.svelte';
  import SettingsModal from '$lib/components/SettingsModal.svelte';
  import { segments, speakers } from '$lib/stores/transcript';
  import { settings, loadSettings } from '$lib/stores/settings';
  import type { Segment, Speaker } from '$lib/types/transcript';
  import { onMount, tick } from 'svelte';

  let waveformPlayer: WaveformPlayer;
  let audioUrl = $state('');
  let showSettings = $state(false);

  onMount(() => {
    loadSettings();

    // Global keyboard shortcuts
    function handleKeyDown(e: KeyboardEvent) {
      // Don't trigger shortcuts when typing in inputs
      const tag = (e.target as HTMLElement)?.tagName;
      if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;

      if (e.key === ' ' && !e.ctrlKey && !e.metaKey) {
        e.preventDefault();
        waveformPlayer?.togglePlayPause?.();
      } else if (e.key === 'o' && (e.ctrlKey || e.metaKey)) {
        e.preventDefault();
        handleFileImport();
      } else if (e.key === ',' && (e.ctrlKey || e.metaKey)) {
        e.preventDefault();
        showSettings = true;
      } else if (e.key === 'Escape') {
        showExportMenu = false;
        showSettings = false;
      }
    }

    // Close export dropdown on outside click
    function handleClickOutside(e: MouseEvent) {
      if (showExportMenu) {
        const target = e.target as HTMLElement;
        if (!target.closest('.export-dropdown')) {
          showExportMenu = false;
        }
      }
    }

    document.addEventListener('keydown', handleKeyDown);
    document.addEventListener('click', handleClickOutside);

    return () => {
      document.removeEventListener('keydown', handleKeyDown);
      document.removeEventListener('click', handleClickOutside);
    };
  });
  let isTranscribing = $state(false);
  let transcriptionProgress = $state(0);
  let transcriptionStage = $state('');
  let transcriptionMessage = $state('');

  // Speaker color palette for auto-assignment
  const speakerColors = ['#e94560', '#4ecdc4', '#ffe66d', '#a8e6cf', '#ff8b94', '#c7ceea', '#ffd93d', '#6bcb77'];

  function handleWordClick(timeMs: number) {
    console.log('[voice-to-notes] Word clicked, seeking to', timeMs, 'ms');
    waveformPlayer?.seekTo(timeMs);
  }

  async function handleFileImport() {
    const filePath = await open({
      multiple: false,
      filters: [{
        name: 'Audio/Video',
        extensions: ['mp3', 'wav', 'flac', 'ogg', 'm4a', 'aac', 'wma',
                     'mp4', 'mkv', 'avi', 'mov', 'webm'],
      }],
    });
    if (!filePath) return;

    // Convert file path to asset URL for wavesurfer
    audioUrl = convertFileSrc(filePath);
    waveformPlayer?.loadAudio(audioUrl);

    // Clear previous results
    segments.set([]);
    speakers.set([]);

    // Start pipeline (transcription + diarization)
    isTranscribing = true;
    transcriptionProgress = 0;
    transcriptionStage = 'Starting...';
    transcriptionMessage = 'Initializing pipeline...';

    // Flush DOM so the progress overlay renders before the blocking invoke
    await tick();

    // Listen for progress events from the sidecar
    const unlisten = await listen<{
      percent: number;
      stage: string;
      message: string;
    }>('pipeline-progress', (event) => {
      console.log('[voice-to-notes] Progress event:', event.payload);
      const { percent, stage, message } = event.payload;
      if (typeof percent === 'number') transcriptionProgress = percent;
      if (typeof stage === 'string') transcriptionStage = stage;
      if (typeof message === 'string') transcriptionMessage = message;
    });

    const unlistenSegment = await listen<{
      index: number;
      text: string;
      start_ms: number;
      end_ms: number;
      words: Array<{ word: string; start_ms: number; end_ms: number; confidence: number }>;
    }>('pipeline-segment', (event) => {
      const seg = event.payload;
      const newSeg: Segment = {
        id: `seg-${seg.index}`,
        project_id: '',
        media_file_id: '',
        speaker_id: null,
        start_ms: seg.start_ms,
        end_ms: seg.end_ms,
        text: seg.text,
        original_text: null,
        confidence: null,
        is_edited: false,
        edited_at: null,
        segment_index: seg.index,
        words: seg.words.map((w, widx) => ({
          id: `word-${seg.index}-${widx}`,
          segment_id: `seg-${seg.index}`,
          word: w.word,
          start_ms: w.start_ms,
          end_ms: w.end_ms,
          confidence: w.confidence,
          word_index: widx,
        })),
      };
      segments.update(segs => [...segs, newSeg]);
    });

    const unlistenSpeaker = await listen<{
      updates: Array<{ index: number; speaker: string }>;
    }>('pipeline-speaker-update', (event) => {
      const { updates } = event.payload;
      // Build speakers from unique labels
      const uniqueLabels = [...new Set(updates.map(u => u.speaker))].sort();
      const newSpeakers: Speaker[] = uniqueLabels.map((label, idx) => ({
        id: `speaker-${idx}`,
        project_id: '',
        label,
        display_name: null,
        color: speakerColors[idx % speakerColors.length],
      }));
      speakers.set(newSpeakers);

      // Update existing segments with speaker assignments
      const speakerLookup = new Map(newSpeakers.map(s => [s.label, s.id]));
      segments.update(segs =>
        segs.map((seg, i) => {
          const update = updates.find(u => u.index === i);
          if (update) {
            return { ...seg, speaker_id: speakerLookup.get(update.speaker) ?? null };
          }
          return seg;
        })
      );
    });

    try {
      const result = await invoke<{
        segments: Array<{
          text: string;
          start_ms: number;
          end_ms: number;
          speaker: string | null;
          words: Array<{
            word: string;
            start_ms: number;
            end_ms: number;
            confidence: number;
          }>;
        }>;
        language: string;
        duration_ms: number;
        speakers: string[];
        num_speakers: number;
      }>('run_pipeline', {
        filePath,
        model: $settings.transcription_model || undefined,
        device: $settings.transcription_device || undefined,
        language: $settings.transcription_language || undefined,
        skipDiarization: $settings.skip_diarization || undefined,
        hfToken: $settings.hf_token || undefined,
      });

      // Create speaker entries from pipeline result
      const newSpeakers: Speaker[] = (result.speakers || []).map((label, idx) => ({
        id: `speaker-${idx}`,
        project_id: '',
        label,
        display_name: null,
        color: speakerColors[idx % speakerColors.length],
      }));
      speakers.set(newSpeakers);

      // Build speaker label → id lookup
      const speakerLookup = new Map(newSpeakers.map(s => [s.label, s.id]));

      // Convert result to our store format
      const newSegments: Segment[] = result.segments.map((seg, idx) => ({
        id: `seg-${idx}`,
        project_id: '',
        media_file_id: '',
        speaker_id: seg.speaker ? (speakerLookup.get(seg.speaker) ?? null) : null,
        start_ms: seg.start_ms,
        end_ms: seg.end_ms,
        text: seg.text,
        original_text: null,
        confidence: null,
        is_edited: false,
        edited_at: null,
        segment_index: idx,
        words: seg.words.map((w, widx) => ({
          id: `word-${idx}-${widx}`,
          segment_id: `seg-${idx}`,
          word: w.word,
          start_ms: w.start_ms,
          end_ms: w.end_ms,
          confidence: w.confidence,
          word_index: widx,
        })),
      }));

      segments.set(newSegments);
    } catch (err) {
      console.error('Pipeline failed:', err);
      alert(`Pipeline failed: ${err}`);
    } finally {
      unlisten();
      unlistenSegment();
      unlistenSpeaker();
      isTranscribing = false;
    }
  }

  const exportFormats = [
    { name: 'SubRip Subtitle', ext: 'srt', format: 'srt' },
    { name: 'WebVTT', ext: 'vtt', format: 'vtt' },
    { name: 'Advanced SubStation Alpha', ext: 'ass', format: 'ass' },
    { name: 'Plain Text', ext: 'txt', format: 'txt' },
    { name: 'Markdown', ext: 'md', format: 'md' },
  ];

  let showExportMenu = $state(false);

  async function handleExport(format: string, ext: string, filterName: string) {
    showExportMenu = false;

    const outputPath = await save({
      filters: [{ name: filterName, extensions: [ext] }],
    });
    if (!outputPath) return;

    // Build speaker lookup: speaker_id → display_name
    const speakerMap: Record<string, string> = {};
    for (const s of $speakers) {
      speakerMap[s.label] = s.display_name || s.label;
    }

    // Build export segments from store
    const exportSegments = $segments.map(seg => {
      const speaker = $speakers.find(s => s.id === seg.speaker_id);
      return {
        text: seg.text,
        start_ms: seg.start_ms,
        end_ms: seg.end_ms,
        speaker: speaker?.label ?? null,
      };
    });

    try {
      await invoke('export_transcript', {
        segments: exportSegments,
        speakers: speakerMap,
        format,
        outputPath,
        title: 'Voice to Notes Transcript',
      });
      alert(`Exported to ${outputPath}`);
    } catch (err) {
      console.error('Export failed:', err);
      alert(`Export failed: ${err}`);
    }
  }
</script>

<div class="app-header">
  <h1>Voice to Notes</h1>
  <div class="header-actions">
    <button class="import-btn" onclick={handleFileImport} disabled={isTranscribing}>
      {#if isTranscribing}
        Processing...
      {:else}
        Import Audio/Video
      {/if}
    </button>
    <button class="settings-btn" onclick={() => showSettings = true} title="Settings">
      Settings
    </button>
    {#if $segments.length > 0}
      <div class="export-dropdown">
        <button class="export-btn" onclick={() => showExportMenu = !showExportMenu}>
          Export
        </button>
        {#if showExportMenu}
          <div class="export-menu">
            {#each exportFormats as fmt}
              <button class="export-option" onclick={() => handleExport(fmt.format, fmt.ext, fmt.name)}>
                {fmt.name} (.{fmt.ext})
              </button>
            {/each}
          </div>
        {/if}
      </div>
    {/if}
  </div>
</div>

<div class="workspace">
  <div class="main-content">
    <WaveformPlayer bind:this={waveformPlayer} {audioUrl} />
    <TranscriptEditor onWordClick={handleWordClick} />
  </div>
  <div class="sidebar-right">
    <SpeakerManager />
    <AIChatPanel />
  </div>
</div>

<ProgressOverlay
  visible={isTranscribing}
  percent={transcriptionProgress}
  stage={transcriptionStage}
  message={transcriptionMessage}
/>

<SettingsModal
  visible={showSettings}
  onClose={() => showSettings = false}
/>

<style>
  .app-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.5rem 1rem;
    background: #0f3460;
    color: #e0e0e0;
  }
  h1 {
    font-size: 1.25rem;
    margin: 0;
  }
  .import-btn {
    background: #e94560;
    border: none;
    color: white;
    padding: 0.5rem 1rem;
    border-radius: 6px;
    cursor: pointer;
    font-size: 0.875rem;
    font-weight: 500;
  }
  .import-btn:hover:not(:disabled) {
    background: #d63851;
  }
  .import-btn:disabled {
    opacity: 0.7;
    cursor: not-allowed;
    animation: pulse 1.5s ease-in-out infinite;
  }
  @keyframes pulse {
    0%, 100% { opacity: 0.7; }
    50% { opacity: 1; }
  }
  .header-actions {
    display: flex;
    gap: 0.5rem;
    align-items: center;
  }
  .settings-btn {
    background: none;
    border: 1px solid #4a5568;
    color: #e0e0e0;
    padding: 0.5rem 0.75rem;
    border-radius: 6px;
    cursor: pointer;
    font-size: 0.875rem;
  }
  .settings-btn:hover {
    background: rgba(255,255,255,0.05);
    border-color: #e94560;
  }
  .export-dropdown {
    position: relative;
  }
  .export-btn {
    background: #0f3460;
    border: 1px solid #4a5568;
    color: #e0e0e0;
    padding: 0.5rem 1rem;
    border-radius: 6px;
    cursor: pointer;
    font-size: 0.875rem;
    font-weight: 500;
  }
  .export-btn:hover {
    background: #1a4a7a;
  }
  .export-menu {
    position: absolute;
    top: 100%;
    right: 0;
    margin-top: 0.25rem;
    background: #16213e;
    border: 1px solid #4a5568;
    border-radius: 6px;
    overflow: hidden;
    z-index: 10;
    min-width: 220px;
  }
  .export-option {
    display: block;
    width: 100%;
    background: none;
    border: none;
    color: #e0e0e0;
    padding: 0.5rem 1rem;
    text-align: left;
    cursor: pointer;
    font-size: 0.8rem;
  }
  .export-option:hover {
    background: rgba(233, 69, 96, 0.2);
  }
  .workspace {
    display: flex;
    gap: 1rem;
    padding: 1rem;
    height: calc(100vh - 3rem);
    overflow: hidden;
    background: #0a0a23;
  }
  .main-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 1rem;
    min-width: 0;
  }
  .sidebar-right {
    width: 300px;
    display: flex;
    flex-direction: column;
    gap: 1rem;
    flex-shrink: 0;
  }
</style>
