import React, { useState, useRef, useCallback, useEffect } from 'react';
import { TriggerBarSendIcon } from '../icons/TriggerBarSendIcon';
import { useVoiceRecorder, convertPcmToWav } from '../../hooks/useVoiceRecorder';
import { sendStreamPackage, generateUUID } from '../../services/asrService';
import type { OnRecognitionResult } from '../../services/asrService';
import styles from './index.module.less';

const WAVEFORM_BAR_COUNT = 30;
const WAVEFORM_BARS = Array.from({ length: WAVEFORM_BAR_COUNT }, (_, i) => ({
  delay: (i * 0.04) + (i % 3) * 0.02,
  duration: 0.4 + (i % 5) * 0.12,
}));

const SAMPLE_RATE = 16000;
const CHANNELS = 1;
const TARGET_PCM_SAMPLES = SAMPLE_RATE; // ~1 second of audio
const MIN_SEND_INTERVAL = 1000;

interface InputBarProps {
  onSend: (text: string) => void;
  disabled?: boolean;
  sending?: boolean;
  onStop?: () => void;
  keyboardOffset?: number;
  initialVoiceMode?: boolean;
}

export const InputBar: React.FC<InputBarProps> = ({
  onSend,
  disabled = false,
  sending = false,
  onStop,
  keyboardOffset = 0,
  initialVoiceMode = true,
}) => {
  const [text, setText] = useState('');
  const [isVoiceMode, setIsVoiceMode] = useState(initialVoiceMode);
  const [voicePressing, setVoicePressing] = useState(false);
  const [voiceError, setVoiceError] = useState<string | null>(null);
  const [realtimeText, setRealtimeText] = useState('');
  const inputRef = useRef<HTMLInputElement>(null);
  const voiceButtonRef = useRef<HTMLButtonElement>(null);
  const voiceCancelledRef = useRef(false);
  const voicePressingRef = useRef(false);
  const pendingStopRef = useRef(false);
  const recordStartedRef = useRef(false);
  const touchStartYRef = useRef(0);
  const isCancelGestureRef = useRef(false);

  // Streaming ASR state
  const streamRequestIdRef = useRef<string | null>(null);
  const streamSequenceIdRef = useRef(0);
  const isStreamingRef = useRef(false);
  const pcmBufferRef = useRef<Float32Array[]>([]);
  const streamTimerRef = useRef<number | null>(null);
  const lastSendTimeRef = useRef(0);
  const recognizedTextRef = useRef('');
  const partialTextRef = useRef('');
  const streamErrorRef = useRef(false);

  const CANCEL_SWIPE_THRESHOLD = 80;
  const shouldHandleMouseRef = useRef(
    typeof window === 'undefined' ? true : !('ontouchstart' in window),
  );

  const { isRecording, status, startRecord, stopRecord, cancelRecord } =
    useVoiceRecorder();
  const voiceButtonDisabled = disabled || status === 'processing';

  useEffect(() => {
    setIsVoiceMode(initialVoiceMode);
  }, [initialVoiceMode]);

  const handleSend = () => {
    const trimmed = text.trim();
    if (!trimmed || disabled) return;
    onSend(trimmed);
    setText('');
  };

  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault();
      handleSend();
    }
  };

  const toggleMode = () => {
    const next = !isVoiceMode;
    setIsVoiceMode(next);
    if (!next) {
      setTimeout(() => inputRef.current?.focus(), 100);
    }
  };

  const handleStreamResult: OnRecognitionResult = useCallback((result) => {
    if (!result.text || result.text.trim().length === 0) return;

    if (result.textType === 'partial') {
      partialTextRef.current = result.text;
    } else if (result.textType === 'final') {
      recognizedTextRef.current += result.text;
      partialTextRef.current = '';
    }
    setRealtimeText(recognizedTextRef.current + partialTextRef.current);
  }, []);

  const clearStreamState = useCallback(() => {
    if (streamTimerRef.current !== null) {
      clearTimeout(streamTimerRef.current);
      streamTimerRef.current = null;
    }
    pcmBufferRef.current = [];
    streamRequestIdRef.current = null;
    streamSequenceIdRef.current = 0;
    isStreamingRef.current = false;
    lastSendTimeRef.current = 0;
    recognizedTextRef.current = '';
    partialTextRef.current = '';
    streamErrorRef.current = false;
    setRealtimeText('');
  }, []);

  const sendBufferedPcmData = useCallback(async () => {
    if (!isStreamingRef.current || !streamRequestIdRef.current) {
      pcmBufferRef.current = [];
      return;
    }

    const now = Date.now();
    const timeSinceLastSend = now - lastSendTimeRef.current;
    if (timeSinceLastSend < MIN_SEND_INTERVAL && lastSendTimeRef.current > 0) {
      if (streamTimerRef.current === null) {
        const remainingDelay = MIN_SEND_INTERVAL - timeSinceLastSend;
        streamTimerRef.current = window.setTimeout(async () => {
          streamTimerRef.current = null;
          await sendBufferedPcmData();
        }, remainingDelay);
      }
      return;
    }

    const chunksToSend = [...pcmBufferRef.current];
    pcmBufferRef.current = [];
    if (chunksToSend.length === 0) return;

    try {
      const totalLength = chunksToSend.reduce((sum, chunk) => sum + chunk.length, 0);
      const mergedPcm = new Float32Array(totalLength);
      let offset = 0;
      for (const chunk of chunksToSend) {
        mergedPcm.set(chunk, offset);
        offset += chunk.length;
      }

      const audioArrayBuffer = convertPcmToWav(mergedPcm, SAMPLE_RATE, CHANNELS);
      if (!audioArrayBuffer || audioArrayBuffer.byteLength === 0) return;

      lastSendTimeRef.current = Date.now();
      streamSequenceIdRef.current += 1;
      const sequenceId = streamSequenceIdRef.current;

      await sendStreamPackage({
        audioData: audioArrayBuffer,
        requestId: streamRequestIdRef.current!,
        sequenceId,
        isFirstPackage: sequenceId === 1,
        isLastPackage: false,
        onResult: handleStreamResult,
      });
    } catch {
      streamErrorRef.current = true;
    }
  }, [handleStreamResult]);

  const onPcmData = useCallback((pcmData: Float32Array) => {
    if (!isStreamingRef.current || !streamRequestIdRef.current) return;

    pcmBufferRef.current.push(new Float32Array(pcmData));

    const totalSamples = pcmBufferRef.current.reduce((sum, chunk) => sum + chunk.length, 0);

    if (totalSamples >= TARGET_PCM_SAMPLES) {
      if (streamTimerRef.current !== null) {
        clearTimeout(streamTimerRef.current);
        streamTimerRef.current = null;
      }
      void sendBufferedPcmData();
    } else if (streamTimerRef.current === null) {
      streamTimerRef.current = window.setTimeout(async () => {
        streamTimerRef.current = null;
        await sendBufferedPcmData();
      }, MIN_SEND_INTERVAL);
    }
  }, [sendBufferedPcmData]);

  const doEndVoicePress = useCallback(async () => {
    voicePressingRef.current = false;
    recordStartedRef.current = false;
    setVoicePressing(false);
    const wasCancelled = voiceCancelledRef.current || isCancelGestureRef.current;
    isCancelGestureRef.current = false;

    if (wasCancelled) {
      cancelRecord();
      clearStreamState();
      return;
    }

    // Flush remaining PCM buffer as tail package before stopping recorder
    let tailAudioData: ArrayBuffer = new ArrayBuffer(0);
    if (isStreamingRef.current && streamRequestIdRef.current && pcmBufferRef.current.length > 0) {
      const chunks = [...pcmBufferRef.current];
      pcmBufferRef.current = [];
      const totalLength = chunks.reduce((sum, c) => sum + c.length, 0);
      if (totalLength > 0) {
        const merged = new Float32Array(totalLength);
        let off = 0;
        for (const c of chunks) { merged.set(c, off); off += c.length; }
        tailAudioData = convertPcmToWav(merged, SAMPLE_RATE, CHANNELS);
      }
    }

    try {
      const wavData = await stopRecord();

      if (isStreamingRef.current && streamRequestIdRef.current) {
        if (streamTimerRef.current !== null) {
          clearTimeout(streamTimerRef.current);
          streamTimerRef.current = null;
        }
        try {
          streamSequenceIdRef.current += 1;
          await sendStreamPackage({
            audioData: tailAudioData,
            requestId: streamRequestIdRef.current,
            sequenceId: streamSequenceIdRef.current,
            isFirstPackage: streamSequenceIdRef.current === 1,
            isLastPackage: true,
            onResult: handleStreamResult,
          });
        } catch {
          streamErrorRef.current = true;
        }
      }

      const streamedText = (recognizedTextRef.current + partialTextRef.current).trim();
      clearStreamState();

      if (streamedText) {
        onSend(streamedText);
        return;
      }

      if (!wavData || wavData.byteLength === 0 || !streamedText) {
        setVoiceError('未识别到语音内容，请长按说话');
        setTimeout(() => setVoiceError(null), 2500);
      }
    } catch (err: unknown) {
      clearStreamState();
      setVoiceError((err as Error)?.message || '语音识别失败');
      setTimeout(() => setVoiceError(null), 3000);
    }
  }, [stopRecord, cancelRecord, clearStreamState, handleStreamResult, onSend]);

  const beginVoicePress = useCallback(async () => {
    if (voicePressingRef.current) return;
    voicePressingRef.current = true;
    pendingStopRef.current = false;
    recordStartedRef.current = false;
    setVoicePressing(true);
    setVoiceError(null);
    setRealtimeText('');
    voiceCancelledRef.current = false;
    isCancelGestureRef.current = false;

    // Init streaming state
    streamRequestIdRef.current = generateUUID();
    streamSequenceIdRef.current = 0;
    isStreamingRef.current = true;
    pcmBufferRef.current = [];
    lastSendTimeRef.current = 0;
    recognizedTextRef.current = '';
    partialTextRef.current = '';
    streamErrorRef.current = false;

    try {
      await startRecord(onPcmData);
      recordStartedRef.current = true;
      if (pendingStopRef.current) {
        pendingStopRef.current = false;
        doEndVoicePress();
        return;
      }
    } catch (err: unknown) {
      voicePressingRef.current = false;
      pendingStopRef.current = false;
      recordStartedRef.current = false;
      setVoicePressing(false);
      clearStreamState();
      setVoiceError((err as Error)?.message || '录音启动失败');
    }
  }, [startRecord, doEndVoicePress, onPcmData, clearStreamState]);

  const endVoicePress = useCallback(async () => {
    if (!voicePressingRef.current) return;
    if (!recordStartedRef.current) {
      pendingStopRef.current = true;
      return;
    }
    doEndVoicePress();
  }, [doEndVoicePress]);

  const cancelVoicePress = useCallback(() => {
    voiceCancelledRef.current = true;
    voicePressingRef.current = false;
    pendingStopRef.current = false;
    recordStartedRef.current = false;
    setVoicePressing(false);
    isCancelGestureRef.current = false;
    cancelRecord();
    clearStreamState();
  }, [cancelRecord, clearStreamState]);

  const handleTouchStart = useCallback((e: React.TouchEvent) => {
    e.preventDefault();
    e.stopPropagation();
    touchStartYRef.current = e.touches[0]?.clientY ?? 0;
    isCancelGestureRef.current = false;
    void beginVoicePress();
  }, [beginVoicePress]);

  const handleTouchMove = useCallback((e: React.TouchEvent) => {
    if (!voicePressingRef.current) return;
    const currentY = e.touches[0]?.clientY ?? 0;
    const deltaY = touchStartYRef.current - currentY;
    isCancelGestureRef.current = deltaY > CANCEL_SWIPE_THRESHOLD;
  }, []);

  const handleTouchEnd = useCallback((e: React.TouchEvent) => {
    e.stopPropagation();
    if (isCancelGestureRef.current) {
      cancelVoicePress();
    } else {
      endVoicePress();
    }
  }, [endVoicePress, cancelVoicePress]);

  const handleMouseDown = useCallback(() => {
    const suppressed = !shouldHandleMouseRef.current;
    if (suppressed) return;
    void beginVoicePress();
  }, [beginVoicePress]);

  const handleMouseUp = useCallback(() => {
    const suppressed = !shouldHandleMouseRef.current;
    if (suppressed) return;
    void endVoicePress();
  }, [endVoicePress]);

  const voiceLabel = voiceError || '按住说话';

  const voiceAreaClass = [
    styles.voiceArea,
    voicePressing && (isRecording || recordStartedRef.current) ? styles.voiceRecording : '',
    voiceError ? styles.voiceError : '',
  ]
    .filter(Boolean)
    .join(' ');

  const showVoiceOverlay = voicePressing && (isRecording || recordStartedRef.current);

  return (
    <div
      className={styles.bar}
      style={{ paddingBottom: `calc(env(safe-area-inset-bottom) + ${keyboardOffset}px)` }}
    >
      {showVoiceOverlay && (
        <div className={styles.voiceOverlay}>
          <div className={styles.voiceOverlayListening}>
            {realtimeText || '正在聆听...'}
          </div>
          <div className={styles.voiceOverlayHint}>
            {isCancelGestureRef.current ? '松手取消' : '松手发送，上移取消'}
          </div>
          <div className={styles.waveformContainer}>
            {WAVEFORM_BARS.map((bar, i) => (
              <span
                key={i}
                className={styles.waveformBar}
                style={{
                  animationDelay: `${bar.delay}s`,
                  animationDuration: `${bar.duration}s`,
                }}
              />
            ))}
          </div>
        </div>
      )}

      <div className={styles.card}>
        {isVoiceMode ? (
          <button
            ref={voiceButtonRef}
            type="button"
            className={voiceAreaClass}
            disabled={voiceButtonDisabled}
            onTouchStart={handleTouchStart}
            onTouchMove={handleTouchMove}
            onTouchEnd={handleTouchEnd}
            onTouchCancel={e => { e.stopPropagation(); cancelVoicePress(); }}
            onMouseDown={handleMouseDown}
            onMouseUp={handleMouseUp}
            onMouseLeave={cancelVoicePress}
            onContextMenu={e => {
              e.preventDefault();
            }}
          >
            {voiceLabel}
          </button>
        ) : (
          <input
            ref={inputRef}
            className={styles.textInput}
            value={text}
            onChange={e => setText(e.target.value)}
            onKeyDown={handleKeyDown}
            placeholder="请输入问题..."
            disabled={disabled}
            enterKeyHint="send"
          />
        )}

        {sending ? (
          <button
            type="button"
            className={styles.stopBtn}
            onClick={onStop}
            aria-label="中止生成"
          >
            <StopIcon />
          </button>
        ) : isVoiceMode ? (
          <button
            type="button"
            className={styles.modeToggle}
            onClick={toggleMode}
            aria-label="切换键盘输入"
          >
            <KeyboardIcon />
          </button>
        ) : text.trim() ? (
          <button
            type="button"
            className={styles.modeToggle}
            onClick={handleSend}
            disabled={disabled}
            aria-label="发送"
          >
            <TriggerBarSendIcon />
          </button>
        ) : (
          <button
            type="button"
            className={styles.modeToggle}
            onClick={toggleMode}
            aria-label="切换语音输入"
          >
            <MicIcon />
          </button>
        )}
      </div>
    </div>
  );
};

const KeyboardIcon = () => (
  <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
    <rect x="6" y="12" width="36" height="24" rx="4" stroke="#1c1d1f" strokeWidth="1.6" />
    <rect x="11" y="18" width="6" height="6" rx="1.2" fill="#1c1d1f" />
    <rect x="21" y="18" width="6" height="6" rx="1.2" fill="#1c1d1f" />
    <rect x="31" y="18" width="6" height="6" rx="1.2" fill="#1c1d1f" />
    <rect x="11" y="28" width="6" height="6" rx="1.2" fill="#1c1d1f" />
    <rect x="21" y="28" width="6" height="6" rx="1.2" fill="#1c1d1f" />
    <rect x="31" y="28" width="6" height="6" rx="1.2" fill="#1c1d1f" />
  </svg>
);

const MicIcon = () => (
  <svg width="40" height="40" viewBox="0 0 40 40" fill="none">
    <rect x="15" y="7" width="10" height="16" rx="5" stroke="#1c1d1f" strokeWidth="1.8" />
    <path d="M9 20a11 11 0 0022 0" stroke="#1c1d1f" strokeWidth="1.8" strokeLinecap="round" />
    <path d="M20 33v4" stroke="#1c1d1f" strokeWidth="1.8" strokeLinecap="round" />
  </svg>
);

const StopIcon = () => (
  <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
    <path
      d="M28.8459 32.0769H19.1536C18.2968 32.0769 17.475 31.7366 16.8691 31.1307C16.2632 30.5248 15.9229 29.703 15.9229 28.8462V19.1539C15.9229 18.297 16.2632 17.4753 16.8691 16.8694C17.475 16.2635 18.2968 15.9231 19.1536 15.9231H28.8459C29.7028 15.9231 30.5245 16.2635 31.1304 16.8694C31.7363 17.4753 32.0767 18.297 32.0767 19.1539V28.8462C32.0767 29.703 31.7363 30.5248 31.1304 31.1307C30.5245 31.7366 29.7028 32.0769 28.8459 32.0769Z"
      fill="#515357"
    />
    <circle cx="24" cy="24" r="19.75" stroke="#515357" strokeWidth="2.5" />
  </svg>
);
