// Discord-style live captions. Uses the browser's SpeechRecognition API to
// transcribe the LOCAL user's mic, then broadcasts each interim/final result
// to peers via the LiveKit DataChannel. Receivers store and display them.
//
// Privacy note: speech recognition runs in the browser. On Chromium-based
// runtimes (incl. Tauri's WebView2 on Windows) this calls into the browser's
// own engine, which today reaches Google's cloud — same trade-off as Discord.
// We ship a hard off switch and require an explicit user toggle.

const STORAGE_KEY = 'chatapp.liveCaptions.v1';

export interface LiveCaptionsSettings {
  enabled: boolean;
  /** BCP-47 language tag, e.g. "de-DE" or "en-US". Auto-detect uses navigator.language. */
  lang: string | null;
}

const DEFAULTS: LiveCaptionsSettings = {
  enabled: false,
  lang: null,
};

type Listener = (s: LiveCaptionsSettings) => void;
const listeners = new Set<Listener>();
let cached: LiveCaptionsSettings | null = null;

function read(): LiveCaptionsSettings {
  if (cached) return cached;
  try {
    const raw = window.localStorage.getItem(STORAGE_KEY);
    if (!raw) {
      cached = DEFAULTS;
      return cached;
    }
    const parsed = JSON.parse(raw) as Partial<LiveCaptionsSettings>;
    cached = {
      enabled: typeof parsed.enabled === 'boolean' ? parsed.enabled : DEFAULTS.enabled,
      lang:
        typeof parsed.lang === 'string' && parsed.lang.length > 0
          ? parsed.lang
          : DEFAULTS.lang,
    };
    return cached;
  } catch {
    cached = DEFAULTS;
    return cached;
  }
}

function write(s: LiveCaptionsSettings): void {
  cached = s;
  try {
    window.localStorage.setItem(STORAGE_KEY, JSON.stringify(s));
  } catch {
    /* quota / private mode */
  }
  for (const l of listeners) l(s);
}

export function getLiveCaptionsSettings(): LiveCaptionsSettings {
  return read();
}

export function updateLiveCaptionsSettings(
  patch: Partial<LiveCaptionsSettings>,
): LiveCaptionsSettings {
  const next = { ...read(), ...patch };
  write(next);
  return next;
}

export function subscribeLiveCaptionsSettings(listener: Listener): () => void {
  listeners.add(listener);
  return () => listeners.delete(listener);
}

// Browser feature-detect. Chromium ships under both names; Firefox lacks it
// outright. Returns the constructor or null.
type SpeechRecognitionCtor = new () => SpeechRecognitionLike;
interface SpeechRecognitionLike extends EventTarget {
  continuous: boolean;
  interimResults: boolean;
  lang: string;
  start: () => void;
  stop: () => void;
  abort: () => void;
  onresult: ((e: SpeechRecognitionEventLike) => void) | null;
  onerror: ((e: SpeechRecognitionErrorLike) => void) | null;
  onend: (() => void) | null;
}
interface SpeechRecognitionEventLike {
  resultIndex: number;
  results: ArrayLike<{
    isFinal: boolean;
    [index: number]: { transcript: string };
    length: number;
  }>;
}
interface SpeechRecognitionErrorLike {
  error: string;
}

export function getSpeechRecognitionCtor(): SpeechRecognitionCtor | null {
  const w = window as unknown as {
    SpeechRecognition?: SpeechRecognitionCtor;
    webkitSpeechRecognition?: SpeechRecognitionCtor;
  };
  return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;
}

export function isLiveCaptionsSupported(): boolean {
  return getSpeechRecognitionCtor() !== null;
}

export type {
  SpeechRecognitionLike,
  SpeechRecognitionEventLike,
  SpeechRecognitionErrorLike,
};
