import { Injectable } from '@nestjs/common';
import { EventEmitter } from 'events';
import { v4 as uuidv4 } from 'uuid';
import { WebSocket } from 'ws';
import * as fs from 'fs';
import * as path from 'path';
import { Buffer } from 'buffer';
import { promisify } from 'util';

import { PassThrough } from 'stream';
// import * as ffmpeg from 'fluent-ffmpeg';
// import ffmpegPath from 'ffmpeg-static'
// ffmpeg.setFfmpegPath(ffmpegPath as unknown as string);
import ffmpeg = require('fluent-ffmpeg');
import { Readable, Writable } from 'stream';

export class StreamService extends EventEmitter {
  private ws: WebSocket;
  private expectedAudioIndex: number;
  private audioBuffer: Record<number, any>;
  private streamSid: string;

  constructor(ws: WebSocket) {
    super();
    this.ws = ws;
    this.expectedAudioIndex = 0;
    this.audioBuffer = {};
    this.streamSid = '';
  }

  // setWebSocket(ws: WebSocket) {
  //   this.ws = ws;
  // }

  setStreamSid(streamSid: string) {
    this.streamSid = streamSid;
    console.log('streamSid', streamSid);
  }

  async buffer(index: number | null, audio: any) {
    try {
      const buffer = Buffer.from(audio, 'base64');

      // audio = buffer;

      audio = await this.convertToMulaw(buffer);
      // let folder_path = path.resolve(__dirname + '/assets/' + 'data1.mp3');
      // fs.writeFileSync(folder_path, buffer);
      if (index === null) {
        this.sendAudio(audio);
      } else if (index === this.expectedAudioIndex) {
        this.sendAudio(audio);
        this.expectedAudioIndex++;

        while (this.audioBuffer.hasOwnProperty(this.expectedAudioIndex)) {
          const bufferedAudio = this.audioBuffer[this.expectedAudioIndex];
          this.sendAudio(bufferedAudio);
          this.expectedAudioIndex++;
        }
      } else {
        this.audioBuffer[index] = audio;
      }
    } catch (error) {
      throw error;
    }
  }

  private sendAudio(audio: any) {
    if (!this.ws || this.ws.readyState !== this.ws.OPEN) {
      console.warn('WebSocket is not open.');
      return;
    }

    this.ws.send(
      JSON.stringify({
        streamSid: this.streamSid,
        event: 'media',
        media: {
          payload: audio.toString('base64'),
        },
      })
    );

    const markLabel = uuidv4();
    this.ws.send(
      JSON.stringify({
        streamSid: this.streamSid,
        event: 'mark',
        mark: {
          name: markLabel,
        },
      })
    );

    this.emit('audiosent', markLabel);
  }

  async convertToMulaw(inputBuffer: Buffer) {
    try {
      let data = await this.convert_buffer(inputBuffer);

      return data;
    } catch (error) {
      throw error;
    }
  }

  async convert_buffer(audioBuffer: any) {
    try {
      const mp3Chunks = [];
      const mp3Stream = new Writable({
        write(chunk, encoding, callback) {
          mp3Chunks.push(chunk);
          callback();
        },
      });
      const convertToMp3 = promisify((inputStream: any, callback) => {
        ffmpeg()
          .input(inputStream)
          // .inputFormat('s16le')
          .audioCodec('pcm_mulaw')
          .audioFrequency(8000)
          // .audioChannels(1)
          .toFormat('mulaw')
          .on('end', callback)
          .pipe(mp3Stream, { end: true });
      });
      console.log('Convert Mp3---------', convertToMp3);
      const audioStream = new Readable();
      audioStream.push(audioBuffer);
      audioStream.push(null);
      await convertToMp3(audioStream);

      // Assuming your array of buffers is stored in a variable named `bufferArray`
      // console.log('mp3Chunks', mp3Chunks);

      const combinedBuffer = Buffer.concat(mp3Chunks);
      // console.log('combinedBuffer', combinedBuffer);

      return combinedBuffer;
    } catch (error) {
      throw error;
    }
  }
}
