'use client';

import { useCallback, useRef, useState } from 'react';
import { io, type Socket } from 'socket.io-client';
import { env } from '@/lib/env';
import { useAuthStore } from '@/stores/auth-store';
import type { CoachState } from './types';
import { downsampleTo16k, floatToPcm16, bytesToBase64 } from './audio-utils';

/**
 * useRealtimeCoach — 连实时教练网关 + 麦克风采集。
 *   - socket.io 连 /pac/v1/realtime/coach(token 走 query)
 *   - getUserMedia → ScriptProcessor 采 PCM16 16k → emit audio:frame(静音门控 VAD)
 *   - AnalyserNode 接原始麦克风 → analyserRef 给波形读真实电平(有声才动)
 *   - 字幕式:text:delta 累积到 streaming;text:done 推入 lines(只留末 2 句)
 * v1:麦克风单路整体当患者语音。
 */
export function useRealtimeCoach(planId: string) {
  const [state, setState] = useState<CoachState>({ status: 'idle' });
  const socketRef = useRef<Socket | null>(null);
  const streamRef = useRef<MediaStream | null>(null);
  const ctxRef = useRef<AudioContext | null>(null);
  const procRef = useRef<ScriptProcessorNode | null>(null);
  const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
  /** 波形读真实麦克风电平用 */
  const analyserRef = useRef<AnalyserNode | null>(null);

  const cleanup = useCallback(() => {
    if (timerRef.current) clearInterval(timerRef.current);
    procRef.current?.disconnect();
    void ctxRef.current?.close().catch(() => undefined);
    streamRef.current?.getTracks().forEach((t) => t.stop());
    socketRef.current?.disconnect();
    timerRef.current = null;
    procRef.current = null;
    ctxRef.current = null;
    streamRef.current = null;
    socketRef.current = null;
    analyserRef.current = null;
  }, []);

  const start = useCallback(async (provider: 'qwen' | 'gemini' = 'qwen') => {
    const token = useAuthStore.getState().accessToken;
    if (!token) {
      setState({ status: 'error', message: '未鉴权 — 请从宿主系统重新打开' });
      return;
    }
    setState({ status: 'connecting' });

    const socket = io(`${env.apiBaseUrl}/pac/v1/realtime/coach`, {
      query: { token },
      transports: ['websocket'],
      forceNew: true,
    });
    socketRef.current = socket;

    socket.on('connect', () => socket.emit('coach:start', { planId, provider }));
    socket.on('coach:ready', ({ skills }: { skills: string[] }) => {
      const startedAt = Date.now();
      setState({ status: 'active', lines: [], streaming: '', skills, elapsedSeconds: 0 });
      timerRef.current = setInterval(() => {
        setState((s) =>
          s.status === 'active'
            ? { ...s, elapsedSeconds: Math.floor((Date.now() - startedAt) / 1000) }
            : s,
        );
      }, 1000);
    });
    socket.on('text:delta', ({ text }: { text: string }) =>
      setState((s) => (s.status === 'active' ? { ...s, streaming: s.streaming + text } : s)),
    );
    socket.on('text:done', () =>
      setState((s) => {
        if (s.status !== 'active') return s;
        const done = s.streaming.trim();
        if (!done) return { ...s, streaming: '' };
        return { ...s, lines: [...s.lines, done].slice(-3), streaming: '' };
      }),
    );
    socket.on('coach:error', ({ message }: { message: string }) => {
      setState({ status: 'error', message });
      cleanup();
    });
    socket.on('disconnect', () =>
      setState((s) => (s.status === 'active' ? { status: 'idle' } : s)),
    );

    try {
      await startMic(socket);
    } catch {
      setState({ status: 'error', message: '麦克风开启失败 — 请允许麦克风权限' });
      cleanup();
    }
  }, [planId, cleanup]);

  const startMic = async (socket: Socket) => {
    const stream = await navigator.mediaDevices.getUserMedia({
      // 开 WebRTC 降噪/回声消除/自动增益,过滤稳态背景噪音
      audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true },
    });
    streamRef.current = stream;
    const ctx = new AudioContext();
    ctxRef.current = ctx;
    const source = ctx.createMediaStreamSource(stream);

    // 波形:接原始麦克风,反映真实电平(不受 VAD 门控影响)
    const analyser = ctx.createAnalyser();
    analyser.fftSize = 256;
    analyser.smoothingTimeConstant = 0.7;
    source.connect(analyser);
    analyserRef.current = analyser;

    const proc = ctx.createScriptProcessor(4096, 1, 1);
    procRef.current = proc;
    // 静音门控(VAD):RMS 低于阈值不发帧 → 没人说话不触发模型。
    // 提高阈值降噪;并要求连续 ONSET 帧超阈值才"开口"(杀掉键盘/咳嗽等短噪声尖峰)。
    const SILENCE_RMS = 0.03;
    const ONSET_FRAMES = 4; // 需连续 ~340ms 持续有声才"开口" → 拍手/键盘等瞬时尖峰凑不齐,被拒
    const HANGOVER_FRAMES = 6;
    let hangover = 0;
    let loudStreak = 0;
    proc.onaudioprocess = (e) => {
      if (socket.disconnected) return;
      const f32 = e.inputBuffer.getChannelData(0);
      let sum = 0;
      for (let i = 0; i < f32.length; i++) sum += f32[i]! * f32[i]!;
      const rms = Math.sqrt(sum / f32.length);
      if (rms > SILENCE_RMS) {
        loudStreak++;
        if (loudStreak >= ONSET_FRAMES) hangover = HANGOVER_FRAMES;
      } else {
        loudStreak = 0;
      }
      if (hangover <= 0) return;
      hangover--;
      const pcm = floatToPcm16(downsampleTo16k(f32, ctx.sampleRate));
      socket.emit('audio:frame', { frame: bytesToBase64(pcm) });
    };
    source.connect(proc);
    proc.connect(ctx.destination);
  };

  const stop = useCallback(() => {
    socketRef.current?.emit('coach:stop');
    cleanup();
    setState({ status: 'idle' });
  }, [cleanup]);

  return { state, start, stop, analyserRef };
}
