/**
 * 语音输入：Web Speech API + MediaRecorder
 *
 * 
 *
 * 浏览器方案：
 *   - 首选 Web Speech API (`SpeechRecognition` / `webkitSpeechRecognition`)：实时转写。
 *   - 不支持时回退到 MediaRecorder（仅录音，不转写）。
 *
 * 注：Web Speech API 在 Chrome / Edge 完整支持；Safari / Firefox 仅录音。
 */
import { Result } from "@/utils";

export interface SpeechRecognitionResultEvent {
  transcript: string;
  isFinal: boolean;
}

export interface SpeechRecognitionInstance {
  start(): void;
  stop(): void;
  abort(): void;
  lang: string;
  continuous: boolean;
  interimResults: boolean;
  onresult: ((event: { results: SpeechRecognitionResultEvent[][] }) => void) | null;
  onerror: ((event: { error: string }) => void) | null;
  onend: (() => void) | null;
  onstart?: (() => void) | null;
}

interface SpeechRecognitionConstructor {
  new (): SpeechRecognitionInstance;
}

declare global {
  interface Window {
    SpeechRecognition?: SpeechRecognitionConstructor;
    webkitSpeechRecognition?: SpeechRecognitionConstructor;
  }
}

/** 是否支持实时语音识别 */
export function isSpeechRecognitionSupported(): boolean {
  if (typeof window === "undefined") return false;
  return Boolean(window.SpeechRecognition || window.webkitSpeechRecognition);
}

/** 启动语音识别，返回 `SpeechRecognitionInstance` 由调用方管理生命周期。 */
export function createSpeechRecognition(): Result<SpeechRecognitionInstance> {
  if (typeof window === "undefined") {
    return Result.err(new Error("Speech recognition not available on server"));
  }
  const Ctor = window.SpeechRecognition ?? window.webkitSpeechRecognition;
  if (!Ctor) {
    return Result.err(new Error("Speech recognition not supported"));
  }
  const recognition = new Ctor();
  recognition.interimResults = true;
  recognition.continuous = false;
  recognition.lang = navigator?.language ?? "en-US";
  return Result.ok(recognition);
}

/* ============================================================
 * MediaRecorder 录音（语音消息 / 降级方案）
 * ============================================================ */

export interface MediaRecording {
  blob: Blob;
  url: string;
  durationMs: number;
}

export class MediaRecorderWrapper {
  private recorder: MediaRecorder | null = null;
  private chunks: BlobPart[] = [];
  private startedAt = 0;

  static isSupported(): boolean {
    return (
      typeof window !== "undefined" &&
      typeof window.MediaRecorder !== "undefined" &&
      !!navigator.mediaDevices?.getUserMedia
    );
  }

  async start(): Promise<Result<true>> {
    if (!MediaRecorderWrapper.isSupported()) {
      return Result.err(new Error("MediaRecorder not supported"));
    }
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
      const mimeType = MediaRecorder.isTypeSupported("audio/webm")
        ? "audio/webm"
        : "";
      this.recorder = mimeType ? new MediaRecorder(stream, { mimeType }) : new MediaRecorder(stream);
      this.chunks = [];
      this.startedAt = Date.now();
      this.recorder.ondataavailable = (e) => {
        if (e.data.size > 0) this.chunks.push(e.data);
      };
      this.recorder.start();
      return Result.ok(true);
    } catch (e) {
      return Result.err(e);
    }
  }

  stop(): Promise<Result<MediaRecording>> {
    return new Promise((resolve) => {
      if (!this.recorder) {
        resolve(Result.err(new Error("Recorder not started")));
        return;
      }
      this.recorder.onstop = () => {
        const durationMs = Date.now() - this.startedAt;
        const blob = new Blob(this.chunks, { type: this.recorder?.mimeType ?? "audio/webm" });
        const url = URL.createObjectURL(blob);
        this.cleanup();
        resolve(Result.ok({ blob, url, durationMs }));
      };
      this.recorder.stop();
    });
  }

  abort(): void {
    this.recorder?.stop();
    this.cleanup();
  }

  private cleanup(): void {
    this.recorder?.stream.getTracks().forEach((t) => t.stop());
    this.recorder = null;
    this.chunks = [];
  }
}
