import { Tooltip } from '@tingwujs/design';
import React, {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';

import { markColorsMap } from '../../../../../../../pages/docASR/utils/config';
import { formatHMSHover } from '../../../../../../../utils/misc';
import { iMediaMarkTokenProps } from '../../TWMedia.type';
import './AudioWave.less';

interface iAudioWaveProps {
  audioSpectrum: number[]; // 音频波形数据
  audioDuration: number; // 音频总时长
  playPercent?: number; // 播放百分比
  hasAgenda?: boolean; // 是否有摘要
  // 标记数据
  mediaTokens?: iMediaMarkTokenProps[];
  /** 操作相关 */
  changeToAudioTime: (time: number) => void; // 切换音频到指定时间
  changeAudioTargetTime: (params?: {
    targetTime: number; // 目标时间
    width: number; // 展示宽度
    offsetX: number; // 鼠标偏移量
  }) => void; // 切换音频目标时间
  onHitMark?: (isHit: boolean) => void; // 是否命中标记📌展示
}

// 音频波形图
const AudioWave: React.FC<iAudioWaveProps> = (props) => {
  const { audioSpectrum } = props;

  const audioWaveWrapperRef = useRef<HTMLDivElement | null>(null);
  const [svgSize, setSvgSize] = useState({ width: 0, height: 0 });
  const [drawData, setDraData] = useState<string>('');

  // 计算渲染宽高
  const computeRenderSize = useCallback(() => {
    if (!audioWaveWrapperRef.current) return;

    const audioWaveWrapper = audioWaveWrapperRef.current;
    const { width, height } = audioWaveWrapper.getBoundingClientRect();
    setSvgSize({ width, height });
  }, []);
  // 计算渲染数据
  const computeRenderDrawData = useCallback((_audioSpectrum: number[]) => {
    // 获取画板宽高
    if (!audioWaveWrapperRef.current) return;
    const { width, height } =
      audioWaveWrapperRef.current.getBoundingClientRect();

    // 音频数据比例转化
    const maxAudioData = Math.max(..._audioSpectrum);
    const audioProportionData = _audioSpectrum.map(
      (data) => Math.abs(data) / maxAudioData,
    );
    const { length } = audioProportionData;

    // 处理音频数据
    const drawStep = width / length;
    const waveData = audioProportionData.reduce(
      (result: Array<{ x: number; y: number }>, _, index, array) => {
        // 整段音频数据切分段，取每段最大数据作为曲线数据
        const partNum = width / 20;
        const partLength = Math.round(length / partNum);
        if (index % partLength === 0) {
          const partIndex = Math.floor(index / partLength);
          const part = array.slice(
            partIndex * partLength,
            (partIndex + 1) * partLength,
          );
          const maxData = Math.max(...part); // 冗余计算
          const maxIndex: any =
            Number(part.findIndex((item) => item === maxData)) +
            partIndex * partLength;

          return [
            ...result,
            {
              x: drawStep * maxIndex,
              y: (1 - maxData) * height,
            },
          ];
        }
        return result;
      },
      [],
    );

    // 移到波形起点
    let data = `M${0} ${waveData[0]?.y}`;
    // 计算曲线数据
    for (let i = 0; i < waveData.length - 2; i++) {
      const control = waveData[i + 1];
      const endData = waveData[i + 2];
      const end = {
        x: (control.x + endData.x) / 2,
        y: (control.y + endData.y) / 2,
      };
      data += ` Q${control.x} ${control.y} ${end.x} ${end.y}`;
    }
    // 闭合图形
    data += ` T${width} ${(1 - audioProportionData[length - 1]) * height}`;
    data += ` L${width} ${height}`;
    data += ` L${0} ${height}`;

    setDraData(data);
  }, []);
  // 标记渲染内容计算
  const { mediaTokens, audioDuration, onHitMark } = props;
  const computedMarkTokenList = useMemo(() => {
    if (!mediaTokens || !mediaTokens.length || !audioDuration) return [];
    return mediaTokens.map((item) => {
      const time = formatHMSHover(Number(item.startTime.toFixed(0)));
      const percent = ((item.startTime * 1000) / audioDuration) * 100;

      return {
        startTime: item.startTime,
        percent,
        title: time,
        content: item.text,
        color: markColorsMap.get(item.color)?.color || item.color, // 标记颜色太淡，需要重新计算
      };
    });
  }, [audioDuration, mediaTokens]);

  // 初始化(容器宽高)
  useEffect(() => {
    if (!audioWaveWrapperRef.current) return;
    const audioWaveWrapper = audioWaveWrapperRef.current;
    // 1. 计算一次宽高
    computeRenderSize();
    // 2. 容器宽高监听
    const resizeObserver = new ResizeObserver(computeRenderSize);
    resizeObserver.observe(audioWaveWrapper);

    return () => {
      resizeObserver.disconnect();
    };
  }, [computeRenderSize]);
  // 初始化(数据计算(变更监听))
  useEffect(() => {
    computeRenderDrawData(audioSpectrum);
  }, [computeRenderDrawData, audioSpectrum]);

  /** 操作相关 */
  const { changeAudioTargetTime } = props;
  // 鼠标 Hover 显示目标时间
  const hoverToChangeTargetTime = useCallback(
    (e: any) => {
      const { left, width } = e.currentTarget.getBoundingClientRect();
      let offsetX = e.clientX - left;
      if (offsetX < 0) {
        offsetX = 0;
      }

      const targetTime = (offsetX / width) * audioDuration;
      changeAudioTargetTime({
        targetTime,
        offsetX,
        width,
      });
    },
    [audioDuration, changeAudioTargetTime],
  );

  // 鼠标 Out 清除目标时间
  const outToClearTargetTime = useCallback(() => {
    changeAudioTargetTime();
  }, [changeAudioTargetTime]);

  const { changeToAudioTime } = props;
  const changeAudioCurtTime = useCallback(
    (e: any) => {
      const rect = e.currentTarget.getBoundingClientRect();
      const offsetX = e.clientX - rect.left;

      // 切换到指定时间
      const targetTime = (offsetX / rect.width) * audioDuration;
      changeToAudioTime(targetTime);
    },
    [audioDuration, changeToAudioTime],
  );

  // 是否有摘要
  const { hasAgenda = false, playPercent = 0 } = props;
  const percentHiddenBarWidth =
    playPercent > 0
      ? `calc(${100 - playPercent}%${hasAgenda ? ' - 2px' : ''})`
      : '100%';

  return (
    <div
      ref={audioWaveWrapperRef}
      className="AudioWaveWrapper"
      onMouseEnter={hoverToChangeTargetTime}
      onMouseMove={hoverToChangeTargetTime}
      onMouseLeave={outToClearTargetTime}
      onClick={changeAudioCurtTime}
    >
      {/* 波形颜色渲染 */}
      <div
        className="AudioWave-PercentHideBar"
        style={{
          width: percentHiddenBarWidth,
        }}
      />
      {/* 波形轮廓图 */}
      <svg
        viewBox={`0 0 ${svgSize.width} ${svgSize.height}`}
        width={svgSize.width}
        height={svgSize.height}
      >
        <path d={drawData} fill="var(--Fill_Brand_Background)" />
      </svg>

      {/* 标记气泡tip */}
      {computedMarkTokenList?.map(({ percent, title, content, color }) => (
        <Tooltip
          key={percent}
          overlayClassName={'AudioWave-MarkToken-Tooltip'}
          overlay={
            <>
              <div
                style={{
                  transform: 'scale(0.8)',
                  transformOrigin: 'left',
                }}
              >
                {title}
              </div>
              <div className="arrow" />
              <div title={content}>
                {content.length > 15
                  ? `${content.slice(0, 15)}...`
                  : content || ''}
              </div>
            </>
          }
          getPopupContainer={(triggerNode) => triggerNode.parentElement as any}
        >
          <span
            className="AudioWave-MarkToken-Item"
            onMouseEnter={() => {
              changeAudioTargetTime();
              onHitMark && onHitMark(true);
            }}
            onMouseMove={() => {
              changeAudioTargetTime();
            }}
            onMouseLeave={() => {
              onHitMark && onHitMark(false);
            }}
            style={{
              left: `${percent}%`,
              backgroundColor: color,
            }}
          />
        </Tooltip>
      ))}
    </div>
  );
};

const memoAudioWave = React.memo(AudioWave);
export { memoAudioWave as AudioWave };
