/* eslint-disable max-len */
import React, {useRef, useEffect, useCallback, useState} from 'react';
import {v4 as uuidv4} from 'uuid';
import RealTimeHuman from '@bddh/starling-realtime-client';

import {checkPlayUnMute} from './utils.ts';

import './Demo.scss';

const DEFAULT_TEXT = '我是虚拟数字人，这是我的开场白，你可以在左侧输入框内输入想要让我播报的内容';
const OPENING_TTS_DELAY_MS = 200; // 开场白 TEXT_RENDER 延迟：等音频管线就绪后再发，避免第一个字被吞掉

enum ReadyState {
    UNINSTANTIATED = -1,
    CONNECTING = 0,
    OPEN = 1,
    CLOSING = 2,
    CLOSED = 3,
}

const Demo = () => {
    const [subTitle, setSubTitle] = useState('');
    const [audioMode, setAudioMode] = useState('btn');
    const [pullAudioFromRTC, setPullAudioFromRtc] = useState(false);
    const [isRecording, setIsRecording] = useState(false);
    const isRecordingRef = useRef(false);
    const [canTransfer, setCanTransfer] = useState(false);
    const [isMuted, setIsMuted] = useState(true);
    const canTransferRef = useRef(false);
    const hasSentGreetingRef = useRef(false);
    const enableInterruptRef = useRef(true);
    const humanInstanceRef = useRef(null);

    const [realTimeVideoReady, setRealTimeVideoReady] = useState(false);
    const [wsConnected, setWsConnected] = useState(false);
    // 浏览器限制导致的有声视频无法自动播放,所以这里进行静音后自动播放
    const [videoIsMuted, setVideoIsMuted] = useState(false);
    // 是否可以自动播放，检测完成状态
    const [checkOver, setCheckOver] = useState(false);
    const videoIsMutedRef = useRef(false);
    videoIsMutedRef.current = videoIsMuted;

    const handleDigitalHumanCallback = useCallback(async (data: any) => {
        const {status, content} = data;
        console.info(status, content);
        if (status === 'DH_LIB_MESSAGE') {
            switch (content.action) {
                case 'DOWN_SUBTITLE':
                    const {content: subtitleContent, completed, type} = JSON.parse(content.body);
                    if (type === 'QUERY') {
                        setSubTitle(subtitleContent); // 还在继续识别中completed：false
                        if (completed) {
                            console.info('完整询问内容：', subtitleContent);
                        }
                    }
                    if (type === 'REPLY') {
                        console.info('回答：', subtitleContent);
                    }
                    break;
                case 'AUDIO_QUERY_INTERRUPT':
                    console.info('停止发送音频');
                    break;
                case 'EMPTY_ASR_RESULT':
                    console.info('空ASR结果');
                    break;
                case 'ASR_ERROR':
                    console.info('ASR错误:', content);
                    break;
                default:
                    break;
            }
        }
        if (status === 'DH_LIB_FULL_STATUS') {
            const {type, action} = content;
            if (type === 'rtcState') {
                if (action === 'remotevideoon') {
                    setRealTimeVideoReady(true);
                    checkPlayUnMuteFun();
                }
                if (action === 'localVideoMuted' && content.body) {
                    setVideoIsMuted(true);
                }
            }
            if (type === 'wsState') {
                if (content.readyState === ReadyState.OPEN) {
                    setWsConnected(true);
                }
                else if (content.readyState === ReadyState.CLOSED || content.readyState === ReadyState.CLOSING) {
                    setWsConnected(false);
                }
            }
        }
    }, []);

    const checkPlayUnMuteFun = async () => {
        const result = await checkPlayUnMute(); // 500ms左右
        setVideoIsMuted(!result);
        setCheckOver(true);
    }

    const destroyCurrentInstance = useCallback(async () => {
        if (!humanInstanceRef.current) {
            return;
        }
        try {
            await humanInstanceRef.current.destroy();
        } catch (error) {
            console.error('重连前销毁旧实例失败:', error);
        } finally {
            humanInstanceRef.current = null;
        }
    }, []);

    // 不带拾音功能的连接
    const handleConnect = useCallback(async () => {
        await destroyCurrentInstance();
        hasSentGreetingRef.current = false;
        setAudioMode('');
        humanInstanceRef.current = new RealTimeHuman({
            // token: '填写应用对应token',
            wrapperId: 'human-wrapper',
            connectParams: {
                // ttsPer: '填写音色id',
                // figureId: '填写人像id',
                resolutionHeight: 1920,
                resolutionWidth: 1080,
                inactiveDisconnectSec: 300
            },
            renderParams: {
                closeLog: true,
                autoChromaKey: false,
                fullStatus: true
            },
            onDigitalHumanCallback: handleDigitalHumanCallback
        });
        humanInstanceRef.current.createServer();
    }, [destroyCurrentInstance, handleDigitalHumanCallback]);

    // 带拾音功能的连接
    const handleConnectWithAudio = useCallback(async (mode = 'btn' as 'btn' | 'rtc') => {
        // 先销毁旧实例，避免重复创建导致 DOM ID 冲突
        await destroyCurrentInstance();
        hasSentGreetingRef.current = false;
        setAudioMode(mode);
        setPullAudioFromRtc(() => {
            return mode === 'rtc';
        });
        humanInstanceRef.current = new RealTimeHuman({
            // token: '填写应用对应token',
            wrapperId: 'human-wrapper',
            connectParams: {
                // ttsPer: '填写音色id',
                // figureId: '填写人像id',
                resolutionHeight: 1920,
                resolutionWidth: 1080,
                inactiveDisconnectSec: 3000,
                pullAudioFromRtc: mode === 'rtc',
                pickAudioMode: mode === 'btn' ? 'pressButton' : 'free',
                enableInterrupt: enableInterruptRef.current,
                // asrDdc: false,
                // asrPunc: false,
                // asrItn: false,
                // asrSensitiveWord: false,
                // asrBoostingTable: '拾音,心情,实验'
                // asrVadPauseTime: 200,
            },
            renderParams: {
                closeLog: true,
                autoChromaKey: false,
                fullStatus: true
            },
            onDigitalHumanCallback: handleDigitalHumanCallback
        });
        humanInstanceRef.current.createServer();
    }, [destroyCurrentInstance, handleDigitalHumanCallback]);

    const handleDestroy = useCallback(async () => {
        setSubTitle('');
        isRecordingRef.current = false;
        setIsRecording(false);
        hasSentGreetingRef.current = false;
        canTransferRef.current = false;
        setCanTransfer(false);
        setRealTimeVideoReady(false);
        setWsConnected(false);
        setCheckOver(false);
        setVideoIsMuted(false);
        setIsMuted(true);
        try {
            await humanInstanceRef.current?.destroy();
        } catch (error) {
            console.error('销毁数字人实例失败:', error);
        } finally {
            humanInstanceRef.current = null;
        }
    }, []);

    const handleInterrupt = useCallback(async () => {
        console.info('发送打断');
        await humanInstanceRef.current?.interrupt();
        console.info('打断生效');
    }, []);

    const handleTextRender = useCallback(() => {
        humanInstanceRef.current.textRender({
            body: '你好，这是我的开场白自我介绍，我是数字人。今天天气不错，打算去哪里玩吗？我可以给你推荐一些网红打卡点，需要的话请告诉我哦！',
            onCallbackMsg: (res: any) => console.log(res)
        });
    }, []);

    const handleStreamTextRender = useCallback(() => {
        const commandId = uuidv4();
        humanInstanceRef.current?.textStreamRender({
            body: JSON.stringify({
                first: true,
                last: false,
                text: '你好，这是我的开场白自我介绍，我是数字人'
            }),
            requestId: commandId,
            onCallbackMsg: (res: any) => console.log(res)
        });

        humanInstanceRef.current?.textStreamRender({
            body: JSON.stringify({
                first: false,
                last: false,
                text: '你好，这是我的开场白自我介绍，我是数字人'
            }),
            requestId: commandId,
            // 测试流式驱动多次填写onCallbackMsg是否兼容
            onCallbackMsg: (res: any) => console.log(res)
        });

        humanInstanceRef.current?.textStreamRender({
            body: JSON.stringify({
                first: false,
                last: false,
                text: '你好，这是我的开场白自我介绍，我是数字人'
            }),
            requestId: commandId
        });

        setTimeout(() => {
            humanInstanceRef.current?.textStreamRender({
                body: JSON.stringify({
                    first: false,
                    last: false,
                    text: '今天天气,今天天气,今天天气今天天气,今天天气,今天天气今天天气,今天天气,今天天气'
                }),
                requestId: commandId
            });

            humanInstanceRef.current?.textStreamRender({
                body: JSON.stringify({
                    first: false,
                    last: true,
                    text: '很好很好很好很好很好很好'
                }),
                requestId: commandId
            });
        }, 500);
    }, []);

    const fetchData = async (url: string): Promise<ArrayBuffer> => {
        const response = await fetch(url, {
            method: 'GET'
        });

        if (!response.ok) {
            throw new Error(`HTTP error! Status: ${response.status}`);
        }

        // 将响应体解析为 ArrayBuffer
        const arrayBuffer = await response.arrayBuffer();
        return arrayBuffer;
    };

    const arrayBufferToBase64 = (buffer: any) => {
        let binary = '';
        const bytes = new Uint8Array(buffer);
        const len = bytes.byteLength;
        for (let i = 0; i < len; i++) {
            binary += String.fromCharCode(bytes[i]);
        }
        return window.btoa(binary);
    };

    const waitStop = async (time: number) => {
        return new Promise<void>(resolve => {
            setTimeout(() => resolve(), time || 100);
        });
    };

    const handleStreamAudioRender = useCallback(async () => {
        const buffer = await fetchData('https://meta-human-editor-prd.cdn.bcebos.com/open-api%2FaudioTest.pcm');
        const unitLen = 2048; // 模拟切段
        const requestId = uuidv4();
        const len = Math.ceil(buffer.byteLength / unitLen);
        for (let i = 0; i < len; ++i) {
            const arrayBuffer = buffer.slice(i * unitLen, (i + 1) * unitLen);
            const base64String = arrayBufferToBase64(arrayBuffer); // pcm转base64
            if (i % 10 === 0) {
                await waitStop(50);
            }
            humanInstanceRef.current?.audioStreamRender({
                requestId,
                body: JSON.stringify({
                    audio: base64String,
                    first: i === 0,
                    last: i === len - 1
                }),
                ...(i === 0 ? {onCallbackMsg: (res: any) => console.log(`audioRender ${JSON.stringify(res)}`)} : {})
            });
        }
    }, []);

    const handleMuteHuman = () => humanInstanceRef.current?.muteHuman();

    const handleUnMuteHuman = () => humanInstanceRef.current?.unMuteHuman();

    const handlePlayHuman = () => humanInstanceRef.current?.playHuman();

    const handlePauseHuman = () => humanInstanceRef.current?.pauseHuman();

    const changeMicrophoneState = useCallback((isMute: boolean) => {
        humanInstanceRef.current?.muteMicrophone(isMute);
        canTransferRef.current = !isMute;
        setCanTransfer(canTransferRef.current);
        setIsMuted(!canTransferRef.current);
    }, []);

    // 按键拾音开启
    const startRecord = useCallback(async () => {
        if (isRecordingRef.current) {
            return;
        }
        // 当参数enableInterrupt为true时，主动发送打断消息可更快打断播报，不发送打断消息也可打断
        // 当参数enableInterrupt为false，请注释下面打断代码。
        if (enableInterruptRef.current) {
            await humanInstanceRef.current?.interrupt();
        }
        isRecordingRef.current = true;
        setIsRecording(true);
        humanInstanceRef.current?.startRecord();
    }, []);

    // 按键拾音关闭
    const stopRecord = useCallback(() => {
        if (!isRecordingRef.current) {
            return;
        }
        isRecordingRef.current = false;
        setIsRecording(false);
        humanInstanceRef.current?.stopRecord();
    }, []);

    useEffect(() => {
        if (audioMode === 'rtc') {
            changeMicrophoneState(true); // 持续拾音默认麦克风是开启的。先静音麦克风，让开场白播放完毕
        }
        if (realTimeVideoReady && wsConnected && checkOver && !videoIsMutedRef.current && !hasSentGreetingRef.current) {
            hasSentGreetingRef.current = true;
            const commandId = uuidv4();
            const timer = setTimeout(() => {
                humanInstanceRef.current.sendMessage({
                    action: 'TEXT_RENDER',
                    body: DEFAULT_TEXT,
                    requestId: commandId
                }, ({action}) => {
                    console.log(`action====: ${action}`);
                    if (action === 'FINISHED' && audioMode === 'rtc') {
                        changeMicrophoneState(false); // 开场白播放完毕，开启麦克风
                    }
                });
            }, OPENING_TTS_DELAY_MS);
            return () => clearTimeout(timer);
        }
    }, [audioMode, realTimeVideoReady, wsConnected, checkOver, changeMicrophoneState]);

    useEffect(() => {
        // 处理原生键盘事件（避免类型不匹配React的合成事件）
        const onKeyDown = (e: KeyboardEvent) => {
            e.preventDefault(); // 阻止空格触发页面滚动
            // 如果是长按触发的重复事件，直接返回
            if (e.repeat) {
                return;
            }
            if (audioMode === 'btn' && (e.code === 'Space' || e.keyCode === 32)) {
                startRecord();
            }
        };
        const onKeyUp = (e: KeyboardEvent) => {
            if (e.repeat) {
                return;
            }
            if (audioMode === 'btn' && (e.code === 'Space' || e.keyCode === 32)) {
                stopRecord();
            }
        };
        if (audioMode === 'btn') {
            document.addEventListener('keydown', onKeyDown);
            document.addEventListener('keyup', onKeyUp);
        }
        return () => {
            document.removeEventListener('keydown', onKeyDown);
            document.removeEventListener('keyup', onKeyUp);
        };
    }, [audioMode, startRecord, stopRecord]);

    // 组件卸载时清理实例
    useEffect(() => {
        return () => {
            const inst = humanInstanceRef.current;
            humanInstanceRef.current = null;
            if (inst) {
                void inst.destroy().catch((error: unknown) => {
                    console.error('组件卸载销毁数字人实例失败:', error);
                });
            }
        };
    }, []);

    return (
        <div className="demo-wrapper">
            {audioMode &&
                <>
                    <h2>数字人拾音</h2>
                    <div style={{marginBottom: 16}}>
                        <div>当前拾音模式：{audioMode === 'rtc' ? '持续' : pullAudioFromRTC ? '按键拾音+rtc' : '按键拾音+ws'}</div>
                        {audioMode === 'btn' && (
                            <>
                                <button
                                    onTouchStart={e => {
                                        e.preventDefault();
                                        startRecord();
                                    }}
                                    onTouchEnd={e => {
                                        e.preventDefault();
                                        stopRecord();
                                    }}
                                    onContextMenu={e => e.preventDefault()} // 禁止长按弹出菜单（安卓/部分 WebView）
                                    onSelect={e => e.preventDefault()} // 有的浏览器会触发 selection
                                    style={{
                                        userSelect: 'none',
                                        WebkitUserSelect: 'none',
                                        WebkitTouchCallout: 'none', // iOS Safari 长按菜单
                                        touchAction: 'manipulation'
                                    }}
                                >{isRecording ? '正在拾音中............' : 'pc端按空格/移动端按此处说话'}
                                </button>
                            </>

                        )}
                        {audioMode === 'rtc' && isMuted &&(
                            <div>静音中</div>
                        )}
                        {audioMode === 'rtc' &&(
                            <button onClick={() => changeMicrophoneState(canTransfer)}>{canTransfer ? '拾音中(点击静音麦克风)...' : '开启麦克风'}</button>
                        )}
                    </div>
                </>
            }
            <div className="human-container">
                <div id="human-wrapper"></div>
                <div className="sub-title">{subTitle}</div>
            </div>
            <h2>数字人创建与销毁</h2>
            <button onClick={handleConnect}>connect</button>
            <button onClick={() => handleConnectWithAudio()}>connect 按键拾音+ws</button>
            <button onClick={() => handleConnectWithAudio('rtc')}>connect 持续拾音+rtc</button>
            <button onClick={handleDestroy}>destroy</button>
            <h2>数字人驱动</h2>
            <button onClick={handleTextRender}>文本驱动</button>
            <button onClick={handleStreamTextRender}>流式文本驱动</button>
            <button onClick={handleStreamAudioRender}>流式音频驱动</button>
            <button onClick={handleInterrupt}>打断</button>
            <h2>数字人指令控制</h2>
            <button onClick={handleMuteHuman}>静音数字人</button>
            <button onClick={handleUnMuteHuman}>解除静音数字人</button>
            <button onClick={handlePlayHuman}>数字人播放</button>
            <button onClick={handlePauseHuman}>数字人暂停</button>
        </div>
    );
};

export default Demo;
