/**
 * 语音记账（plan.md「8.2 语音记账」）。
 *
 * 参考 BeeCount 的 VoiceBillingHelper（长按说话 / 自动检测两种模式）。
 *
 * 录音/语音转文字通过注入抽象（生产用 expo-av + AI Provider 的 audio/transcriptions，
 * 测试用 mock），转文字后调用 processNaturalLanguage 解析为交易草稿。
 */

import { processNaturalLanguage, type AiProvider, type AiBillResult } from '../domain/ai';
import { logger } from '../utils/logger';

/** 录音器抽象（生产用 expo-av，测试用 mock）。 */
export interface AudioRecorder {
  /** 请求录音权限，返回是否授权。 */
  requestPermission(): Promise<boolean>;
  /** 开始录音，返回录音句柄。 */
  start(): Promise<RecordingHandle>;
}

export interface RecordingHandle {
  /** 停止录音，返回音频 URI。 */
  stop(): Promise<string>;
}

/** 语音转文字抽象（生产用 AI Provider 的 audio/transcriptions，测试用 mock）。 */
export interface SpeechToText {
  transcribe(audioUri: string): Promise<string>;
}

export type VoiceTriggerMode = 'auto-detect' | 'press-to-talk';

export interface VoiceInputConfig {
  mode: VoiceTriggerMode;
  /** 自动检测模式的静音超时（毫秒，默认 2000）。 */
  silenceTimeoutMs: number;
  /** 最大录音时长（毫秒，默认 60000）。 */
  maxDurationMs: number;
  /** 振幅阈值（自动检测开始说话，0-1，默认 0.58 ≈ -25dB）。 */
  amplitudeThreshold: number;
}

export const DEFAULT_VOICE_CONFIG: VoiceInputConfig = {
  mode: 'press-to-talk',
  silenceTimeoutMs: 2000,
  maxDurationMs: 60_000,
  amplitudeThreshold: 0.58,
};

export interface VoiceBillingResult {
  /** 转写的文字。 */
  transcription: string;
  /** 解析结果（draft 表示识别为交易，text 表示自由文本）。 */
  parsed: { type: 'draft'; draft: AiBillResult } | { type: 'text'; text: string } | null;
}

/**
 * 语音记账协调器。
 *
 * 用法：
 *   const voice = new VoiceInput(recorder, stt, aiProvider);
 *   const result = await voice.processPressToTalk();
 */
export class VoiceInput {
  private currentRecording: RecordingHandle | null = null;

  constructor(
    private readonly recorder: AudioRecorder,
    private readonly stt: SpeechToText,
    private readonly aiProvider: AiProvider,
    private readonly config: VoiceInputConfig = DEFAULT_VOICE_CONFIG,
  ) {}

  /**
   * 按住说话模式：开始录音 → 停止 → 转写 → 解析。
   * 由 UI 调用 start/stop 控制录音时机。
   */
  async startRecording(): Promise<void> {
    const granted = await this.recorder.requestPermission();
    if (!granted) throw new Error('需要录音权限');
    this.currentRecording = await this.recorder.start();
    logger.info('voice', '开始录音');
  }

  /** 停止录音并处理。 */
  async stopAndProcess(): Promise<VoiceBillingResult> {
    if (!this.currentRecording) throw new Error('未在录音中');
    const audioUri = await this.currentRecording.stop();
    this.currentRecording = null;
    logger.info('voice', `录音完成: ${audioUri}`);

    const transcription = await this.stt.transcribe(audioUri);
    logger.info('voice', `转写结果: ${transcription}`);

    if (!transcription.trim()) {
      return { transcription: '', parsed: null };
    }

    const parsed = await processNaturalLanguage(transcription, this.aiProvider);
    return { transcription, parsed };
  }

  /**
   * 自动检测模式：录音 → 静音检测停止 → 转写 → 解析。
   * 内部管理 start/stop（基于振幅阈值 + 静音超时）。
   */
  async processAutoDetect(
    getAmplitude: () => number,
  ): Promise<VoiceBillingResult> {
    await this.startRecording();

    // 等待开始说话（振幅超阈值）
    const startDeadline = Date.now() + this.config.maxDurationMs;
    let speakingFrames = 0;
    while (Date.now() < startDeadline) {
      const amp = getAmplitude();
      if (amp >= this.config.amplitudeThreshold) {
        speakingFrames++;
        if (speakingFrames >= 5) break;  // 连续 5 帧判定开始说话
      } else {
        speakingFrames = 0;
      }
      await sleep(100);
    }

    // 等待静音（振幅低于阈值超过 silenceTimeoutMs）
    let lastLoudTime = Date.now();
    const stopDeadline = Date.now() + this.config.maxDurationMs;
    while (Date.now() < stopDeadline) {
      const amp = getAmplitude();
      if (amp >= this.config.amplitudeThreshold) {
        lastLoudTime = Date.now();
      } else if (Date.now() - lastLoudTime > this.config.silenceTimeoutMs) {
        break;  // 静音超时，停止
      }
      await sleep(100);
    }

    return this.stopAndProcess();
  }

  /** 取消当前录音。 */
  cancel(): void {
    this.currentRecording = null;
    logger.info('voice', '录音已取消');
  }
}

function sleep(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

/** 模拟录音器（测试用）。 */
export class MockAudioRecorder implements AudioRecorder {
  public permissionGranted = true;
  public startCount = 0;
  private stopHandler: (() => string) | null = null;

  requestPermission(): Promise<boolean> {
    return Promise.resolve(this.permissionGranted);
  }
  async start(): Promise<RecordingHandle> {
    this.startCount++;
    return {
      stop: async () => {
        return this.stopHandler?.() ?? 'mock://audio.wav';
      },
    };
  }
  /** 测试辅助：设置 stop 返回的 URI。 */
  setStopResult(uri: string): void {
    this.stopHandler = () => uri;
  }
}

/** 模拟语音转文字（测试用）。 */
export class MockSpeechToText implements SpeechToText {
  constructor(private response: string = '') {}
  async transcribe(): Promise<string> {
    return this.response;
  }
  /** 测试辅助。 */
  setResponse(text: string): void {
    this.response = text;
  }
}
