import Taro from "@tarojs/taro";
import { decode } from "@/utils";
import { useRef, useEffect } from "react";

// 将多个 chunk 合并
function combineArrayBuffers(
  arrays: ArrayBuffer[],
  totalLength: number
): ArrayBuffer {
  const result = new Uint8Array(totalLength);
  let offset = 0;

  for (let i = 0; i < arrays.length; i++) {
    const array = new Uint8Array(arrays[i]);
    result.set(array, offset);
    offset += array.length;
  }

  return result.buffer;
}

// 播放片断时需要与 wav 头组合才能正常解析
function combineHeaderAndChunk(header: ArrayBuffer, chunk: ArrayBuffer) {
  // Create a new ArrayBuffer to hold both the header and the chunk
  const combinedBuffer = new ArrayBuffer(header.byteLength + chunk.byteLength);

  // Create a Uint8Array view of the combined buffer
  const combinedView = new Uint8Array(combinedBuffer);

  // Copy the header into the combined buffer
  combinedView.set(new Uint8Array(header), 0);

  // Copy the chunk data after the header
  combinedView.set(new Uint8Array(chunk), header.byteLength);

  return combinedBuffer;
}

const WAV_HEADER_LENGTH = 44; // wav 头长度
export type TPlayStatus = 'playing' | 'stop'

export class AudioStreamPlayer {
  private audioCtx: any;
  private source: AudioBufferSourceNode | null = null;
  private enablePlay = true; // 用于中断流式播放
  private chunks: ArrayBuffer[] = []; // 流式播放 chunks
  private requestTask: Taro.RequestTask<any> | null = null;
  private totalLength = WAV_HEADER_LENGTH;
  private playing = false;
  private wavHeader: ArrayBuffer | null = null;
  public isLastChunk = false;
  private playStatusChangedCallback?: (status: TPlayStatus) => void;

  constructor() {
    this.audioCtx = Taro.createWebAudioContext();
  }

  private changeStatus = (status: TPlayStatus) => {
    this.playStatusChangedCallback?.(status);
  }

  setFistChunk = (base64Str: string, _requestTask?: Taro.RequestTask<any>) => {
    if (_requestTask) {
      this.requestTask = _requestTask;
    }
    this.enablePlay = true;
    this.isLastChunk = false;
    
    let chunk = decode(base64Str);
    this.emptyQuene();
    // 第一个 chunk 内包含了头信息
    this.wavHeader = chunk.slice(0, WAV_HEADER_LENGTH);
    const firstChunkData = chunk.slice(WAV_HEADER_LENGTH);
    this.pushChunk2Quene(firstChunkData);
    this.changeStatus('playing');
  };

  pushBase64ToQuene = (base64Str: string) => {
    let buf = decode(base64Str);
    this.pushChunk2Quene(buf);
  };

  pushChunk2Quene = (chunk: ArrayBuffer) => {
    this.chunks.push(chunk);
    this.totalLength += chunk.byteLength;
  };

  // 标定是否为最后一个 chunk 
  setIsLastChunk = () => {
    this.isLastChunk = true;
  }

  private emptyQuene = () => {
    this.chunks = [];
    this.totalLength = WAV_HEADER_LENGTH;
  };

  playChunk = () => {
    if (!this.enablePlay) {
      this.emptyQuene();
      return;
    }
    if (this.playing) {
      return;
    }
    if (!this.chunks.length) {
      this.playing = false;
      return;
    }
    if (!this.wavHeader) {
      console.warn('No wav header available');
      return;
    }
    this.playing = true;

    let tmp = [...this.chunks];
    const _chunk = combineArrayBuffers(tmp, this.totalLength);
    const partChunks = combineHeaderAndChunk(this.wavHeader, _chunk);

    this.emptyQuene();
    // @ts-ignore
    this.audioCtx.decodeAudioData(partChunks,
      (decodedBuffer: AudioBuffer) => {
        this.source = this.audioCtx.createBufferSource();
        //@ts-ignore
        this.source.connect(this.audioCtx.destination);
        //@ts-ignore
        this.source.buffer = decodedBuffer;
        console.info("play start");
        //@ts-ignore
        this.source.onended = () => {
          console.info("play end");
          this.playing = false;
          if (!this.chunks.length && this.isLastChunk) {
            this.changeStatus('stop');
          } else {
            this.playChunk();
          }
        };
        this.source?.start(0);
      },
      (err: any) => {
        console.warn(err);
        this.playing = false;
      }
    );
  };

  onPlayerStatusChanged = (callback: (status: TPlayStatus) => void) => {
    this.playStatusChangedCallback = callback;
  }

  initPlay = ()=> {
    this.source?.stop();
    this.emptyQuene();
    this.enablePlay = false;
  }

  stopPlayChunk = () => {
    // 如果有请求任务取消
    // if (this.requestTask) {
    //   //@ts-ignore
    //   this.requestTask?.abort?.();
    //   //@ts-ignore
    //   this.requestTask?.offChunkReceived?.();
    // }
    this.source?.stop();
    this.emptyQuene();
    this.changeStatus('stop')
    this.enablePlay = false;
    
  };

  // 获取当前播放状态
  getPlayingStatus = (): boolean => {
    return this.playing;
  }

  stopPlay = ()=> {
    this.source?.stop();
    this.enablePlay = false;
  }

  // 销毁实例
  destroy = () => {
    this.stopPlayChunk();
    this.audioCtx = null;
    this.source = null;
    this.chunks = [];
    this.wavHeader = null;
    this.playStatusChangedCallback = undefined;
  }
}

// 为了保持向后兼容，提供一个 hook 风格的包装器
export const useAudioStreamPlayer = () => {
  const playerRef = useRef<AudioStreamPlayer | null>(null);
  
  // 确保只创建一次实例
  if (!playerRef.current) {
    playerRef.current = new AudioStreamPlayer();
  }
  
  // 组件卸载时清理资源
  useEffect(() => {
    return () => {
      if (playerRef.current) {
        playerRef.current.destroy();
        playerRef.current = null;
      }
    };
  }, []);
  
  return {
    initPlay: playerRef.current.initPlay,
    pushChunk2Quene: playerRef.current.pushChunk2Quene,
    pushBase64ToQuene: playerRef.current.pushBase64ToQuene,
    setIsLastChunk: playerRef.current.setIsLastChunk,
    playChunk: playerRef.current.playChunk,
    stopPlayChunk: playerRef.current.stopPlayChunk,
    stopPlay: playerRef.current.stopPlay,
    setFistChunk: playerRef.current.setFistChunk,
    onPlayerStatusChanged: playerRef.current.onPlayerStatusChanged,
    getPlayingStatus: playerRef.current.getPlayingStatus,
    destroy: playerRef.current.destroy,
    isLastChunk: playerRef.current.isLastChunk,
  };
};
