import { Readable } from "stream";
import ffmpeg from "fluent-ffmpeg";
import * as R from "remeda";

const requiredCodecs = ["mp3", "webm", "wav"];

export interface AudioConvertOpts {
  bitrate?: string;
  audioCodec?: string;
  format?: string;
}

const defaultAudioConvertOpts = {
  bitrate: "32k",
  audioCodec: "libmp3lame",
  format: "mp3",
};

/**
 * Converts an audio file to a different format. defaults to converting to mp3 with a 32k bitrate using the libmp3lame codec
 *
 * @param input the buffer containing the binary data of the input file
 * @param opts options to control how the audio file is converted
 * @return resolves to a buffer containing the binary data of the converted file
 **/
export const convert = (
  input: Buffer,
  opts?: AudioConvertOpts
): Promise<Buffer> => {
  const settings = { ...defaultAudioConvertOpts, ...opts };
  return new Promise((resolve, reject) => {
    const stream = Readable.from(input);
    let out = Buffer.alloc(0);
    const cmd = ffmpeg(stream)
      .audioCodec(settings.audioCodec)
      .audioBitrate(settings.bitrate)
      .toFormat(settings.format)
      .on("error", (err, stdout, stderr) => {
        console.error(err.message);
        console.log("FFMPEG OUTPUT");
        console.log(stdout);
        console.log("FFMPEG ERROR");
        console.log(stderr);
        reject(err);
      })
      .on("end", () => {
        resolve(out);
      });
    const outstream = cmd.pipe();
    outstream.on("data", (chunk: Buffer) => {
      out = Buffer.concat([out, chunk]);
    });
  });
};

/**
 * Check if ffmpeg is installed and usable. Checks for required codecs and a working ffmpeg installation.
 *
 * @return resolves to true if ffmpeg is installed and usable
 * */
export const selfCheck = (): Promise<boolean> => {
  return new Promise((resolve) => {
    ffmpeg.getAvailableFormats((err, codecs) => {
      if (err) {
        console.error("FFMPEG error:", err);
        resolve(false);
      }

      const preds = R.map(requiredCodecs, (codec) => (available: any) =>
        available[codec] && available[codec].canDemux && available[codec].canMux
      );

      resolve(R.allPass(codecs, preds));
    });
  });
};

export const assertFfmpegAvailable = async (): Promise<void> => {
  const r = await selfCheck();
  if (!r)
    throw new Error(
      `ffmpeg is not installed, could not be located, or does not support the required codecs: ${requiredCodecs}`
    );
};
