import React, { useCallback, useEffect, useRef, useState } from 'react';
import { X, Loader } from 'lucide-react';
import { toast } from 'react-hot-toast';
import { useAppStore } from '../store';
import './CaptureWidget.css';

import { API as API_BASE } from '../api/client';
import { addTranscription } from '../pages/Transcriptions';

// Flip the system tray icon between default and red-dot. No-op when not
// running inside the Tauri shell (e.g. browser webui, Docker).
async function setTrayRecording(recording) {
  try {
    const { invoke } = await import('@tauri-apps/api/core');
    await invoke('set_tray_recording', { recording });
  } catch { /* not in Tauri */ }
}

const LS_CAPTURE_MODE = 'omni_capture_mode';

function formatElapsed(ms) {
  const secs = Math.floor(ms / 1000);
  const mins = Math.floor(secs / 60);
  const s = secs % 60;
  if (mins > 0) return `${mins}:${String(s).padStart(2, '0')}`;
  return `${s}s`;
}

/**
 * CaptureWidget — floating pill for dictation.
 *
 * Minimal status-only UI: pulsing dot + label + timer.
 * All interaction via global hotkey (hold-to-talk).
 * Records → transcribes → auto-pastes → auto-dismisses.
 */
export default function CaptureWidget({ onDismiss }) {
  const [state, setState] = useState('idle'); // idle | recording | transcribing | done | error
  const [transcript, setTranscript] = useState('');
  const [duration, setDuration] = useState(0);
  const [captureMode] = useState(() =>
    localStorage.getItem(LS_CAPTURE_MODE) || 'fast'
  );
  const [lastEngine, setLastEngine] = useState('');
  const [lastTime, setLastTime] = useState(0);
  const [partialText, setPartialText] = useState('');

  const mediaRecorderRef = useRef(null);
  const chunksRef = useRef([]);
  const streamRef = useRef(null);
  const timerRef = useRef(null);
  const wsRef = useRef(null);
  const wsPendingRef = useRef([]);
  const wsHadFinalRef = useRef(false);
  const fallbackTimerRef = useRef(null);
  const startTimeRef = useRef(0);

  // ── Hold-to-talk: listen for tray-dictate (start) and tray-dictate-stop (release) ──
  useEffect(() => {
    let unlistenStart, unlistenStop;
    (async () => {
      try {
        const { listen } = await import('@tauri-apps/api/event');
        unlistenStart = await listen('tray-dictate', () => {
          if (state === 'idle' || state === 'done' || state === 'error') {
            startRecording();
          }
        });
        unlistenStop = await listen('tray-dictate-stop', () => {
          if (state === 'recording') {
            stopRecording();
          }
        });
      } catch { /* not in Tauri */ }
    })();
    return () => {
      if (unlistenStart) unlistenStart();
      if (unlistenStop) unlistenStop();
    };
  }, [state]);

  // Keyboard fallback: ⌘+Shift+Space toggles in web mode
  useEffect(() => {
    const handler = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.code === 'Space') {
        e.preventDefault();
        if (state === 'idle' || state === 'done' || state === 'error') {
          startRecording();
        } else if (state === 'recording') {
          stopRecording();
        }
      }
    };
    window.addEventListener('keydown', handler);
    return () => window.removeEventListener('keydown', handler);
  }, [state]);

  // Timer while recording
  useEffect(() => {
    if (state === 'recording') {
      const t0 = Date.now();
      timerRef.current = setInterval(() => setDuration(Date.now() - t0), 100);
      return () => clearInterval(timerRef.current);
    }
    clearInterval(timerRef.current);
  }, [state]);

  // Apply transcription result → auto-paste → auto-dismiss
  const applyResult = useCallback(async (data) => {
    setTranscript(data.text || '');
    setLastEngine(data.engine || '');
    setLastTime(data.transcription_time_s || 0);
    setState('done');

    if (data.text) {
      addTranscription(data);
    }

    if (data.text) {
      try {
        await navigator.clipboard.writeText(data.text);
        try {
          const { invoke } = await import('@tauri-apps/api/core');
          await invoke('simulate_paste');
        } catch { /* not in Tauri */ }
      } catch { /* clipboard API may fail */ }

      // Auto-dismiss after 1.5s
      setTimeout(async () => {
        setState('idle');
        setTranscript('');
        setDuration(0);
        try {
          const { getCurrentWindow } = await import('@tauri-apps/api/window');
          await getCurrentWindow().hide();
        } catch { /* not in Tauri */ }
        if (onDismiss) onDismiss();
      }, 1500);
    } else {
      // No speech — auto-dismiss after 2.5s
      setTimeout(async () => {
        setState('idle');
        setTranscript('');
        setDuration(0);
        try {
          const { getCurrentWindow } = await import('@tauri-apps/api/window');
          await getCurrentWindow().hide();
        } catch { /* not in Tauri */ }
        if (onDismiss) onDismiss();
      }, 2500);
    }
  }, [onDismiss]);

  const startRecording = useCallback(async () => {
    try {
      const stream = await navigator.mediaDevices.getUserMedia({
        audio: { echoCancellation: true, noiseSuppression: true, sampleRate: 16000 }
      });
      streamRef.current = stream;
      chunksRef.current = [];
      wsPendingRef.current = [];
      wsHadFinalRef.current = false;
      if (fallbackTimerRef.current) {
        clearTimeout(fallbackTimerRef.current);
        fallbackTimerRef.current = null;
      }

      const mimeType = MediaRecorder.isTypeSupported('audio/webm;codecs=opus')
        ? 'audio/webm;codecs=opus'
        : 'audio/webm';

      // Open WebSocket BEFORE starting recorder
      try {
        const wsProto = window.location.protocol === 'https:' ? 'wss' : 'ws';
        const wsHost = window.location.host;
        const wsUrl = `${wsProto}://${wsHost}/ws/transcribe`;
        const ws = new WebSocket(wsUrl);
        ws.binaryType = 'arraybuffer';
        ws.onopen = () => {
          for (const buf of wsPendingRef.current) {
            try { ws.send(buf); } catch {}
          }
          wsPendingRef.current = [];
        };
        ws.onmessage = (evt) => {
          try {
            const msg = JSON.parse(evt.data);
            if (msg.type === 'partial') {
              setPartialText(msg.text || '');
            } else if (msg.type === 'final') {
              wsHadFinalRef.current = true;
              if (fallbackTimerRef.current) {
                clearTimeout(fallbackTimerRef.current);
                fallbackTimerRef.current = null;
              }
              applyResult(msg);
              try { ws.close(); } catch {}
            } else if (msg.type === 'error') {
              if (fallbackTimerRef.current) {
                clearTimeout(fallbackTimerRef.current);
                fallbackTimerRef.current = null;
              }
              try { ws.close(); } catch {}
              wsRef.current = null;
              if (!wsHadFinalRef.current) sendForTranscription();
            }
          } catch {}
        };
        ws.onerror = () => { wsRef.current = null; };
        ws.onclose = () => {
          wsRef.current = null;
          if (
            !wsHadFinalRef.current
            && mediaRecorderRef.current
            && mediaRecorderRef.current.state === 'inactive'
          ) {
            if (fallbackTimerRef.current) {
              clearTimeout(fallbackTimerRef.current);
              fallbackTimerRef.current = null;
            }
            sendForTranscription();
          }
        };
        wsRef.current = ws;
      } catch {
        wsRef.current = null;
      }

      const recorder = new MediaRecorder(stream, { mimeType });
      recorder.ondataavailable = (e) => {
        if (e.data.size > 0) {
          chunksRef.current.push(e.data);
          e.data.arrayBuffer().then(buf => {
            const ws = wsRef.current;
            if (ws && ws.readyState === WebSocket.OPEN) {
              ws.send(buf);
            } else {
              wsPendingRef.current.push(buf);
            }
          });
        }
      };
      recorder.onstop = () => {};
      recorder.start(250);
      mediaRecorderRef.current = recorder;
      startTimeRef.current = Date.now();
      setTrayRecording(true);
      setState('recording');
      setTranscript('');
      setPartialText('');
      setDuration(0);
    } catch {
      const isMac = navigator.platform?.includes('Mac');
      const isWindows = navigator.platform?.includes('Win');
      const hint = isMac
        ? 'macOS: open System Settings → Privacy & Security → Microphone and enable OmniVoice.'
        : isWindows
        ? 'Windows: open Settings → Privacy & security → Microphone and allow OmniVoice.'
        : 'Linux: check that your user is in the audio group and the WebView has mic access.';
      toast.error(`Microphone access denied. ${hint}`, { duration: 6000 });
      setTrayRecording(false);
      setState('error');
    }
  }, [applyResult]);

  const stopRecording = useCallback(() => {
    if (mediaRecorderRef.current && mediaRecorderRef.current.state !== 'inactive') {
      mediaRecorderRef.current.stop();
    }
    if (streamRef.current) {
      streamRef.current.getTracks().forEach(t => t.stop());
      streamRef.current = null;
    }
    // Signal EOF to WebSocket
    const ws = wsRef.current;
    if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) {
      const sendEof = () => { try { ws.send('EOF'); } catch {} };
      if (ws.readyState === WebSocket.OPEN) {
        sendEof();
      } else {
        ws.addEventListener('open', sendEof, { once: true });
      }
      // Fallback timer
      const recorded = startTimeRef.current ? Date.now() - startTimeRef.current : 0;
      const ms = Math.max(15000, recorded + 10000);
      if (fallbackTimerRef.current) clearTimeout(fallbackTimerRef.current);
      fallbackTimerRef.current = setTimeout(() => {
        fallbackTimerRef.current = null;
        if (!wsHadFinalRef.current) {
          try { wsRef.current?.close(); } catch {}
          wsRef.current = null;
          sendForTranscription();
        }
      }, ms);
    }
    setTrayRecording(false);
    setState('transcribing');
  }, []);

  const sendForTranscription = useCallback(async () => {
    if (wsHadFinalRef.current) return;

    const blob = new Blob(chunksRef.current, { type: 'audio/webm' });
    const formData = new FormData();
    formData.append('audio', blob, 'capture.webm');
    formData.append('mode', captureMode);

    try {
      const res = await fetch(`${API_BASE}/transcribe`, {
        method: 'POST',
        body: formData,
      });
      if (!res.ok) {
        const detail = await res.json().catch(() => ({}));
        throw new Error(detail.detail || `HTTP ${res.status}`);
      }
      const data = await res.json();
      if (wsHadFinalRef.current) return;
      await applyResult(data);
    } catch (err) {
      if (wsHadFinalRef.current) return;
      toast.error(`Transcription failed: ${err.message}`);
      setState('error');
      setTranscript('');
    }
  }, [captureMode, applyResult]);

  const dismiss = async () => {
    setState('idle');
    setTranscript('');
    setDuration(0);
    try {
      const { getCurrentWindow } = await import('@tauri-apps/api/window');
      await getCurrentWindow().hide();
    } catch { /* not in Tauri */ }
    if (onDismiss) onDismiss();
  };

  // Idle: render nothing — pill is hold-to-talk only (Whisper-Flow / Ghost-Pepper
  // style). The tray-dictate listener above stays mounted, so the shortcut still
  // triggers startRecording() which flips state out of 'idle' and remounts the
  // pill DOM with the slide-in animation.
  if (state === 'idle') return null;

  // ── Pill label ──
  let label = '';
  let emoji = '';
  if (state === 'recording') {
    emoji = '🎙️';
    label = partialText || 'Listening…';
  } else if (state === 'transcribing') {
    emoji = '📝';
    label = partialText || 'Transcribing…';
  } else if (state === 'done' && transcript) {
    emoji = '✅';
    label = 'Pasted';
  } else if (state === 'done' && !transcript) {
    emoji = '⚠️';
    label = 'No speech detected';
  } else if (state === 'error') {
    emoji = '❌';
    label = 'Mic access denied';
  }

  return (
    <div className={`capture-pill capture-pill--${state}`} role="status" aria-live="polite">
      {/* Pulsing status dot */}
      <span className="capture-pill__dot" />

      {/* Content */}
      <div className="capture-pill__content">
        <span className="capture-pill__label">
          {emoji} {label}
        </span>
      </div>

      {/* Timer */}
      {(state === 'recording' || state === 'transcribing') && (
        <span className="capture-pill__timer">
          {formatElapsed(duration)}
        </span>
      )}

      {/* Transcribing spinner */}
      {state === 'transcribing' && (
        <Loader size={14} className="capture-pill__spinner" />
      )}

      {/* Dismiss — only on done/error */}
      {(state === 'done' || state === 'error') && (
        <button className="capture-pill__dismiss" onClick={dismiss} aria-label="Dismiss">
          <X size={12} />
        </button>
      )}
    </div>
  );
}
