import Wreck from "@hapi/wreck";
import { withDb, AppDatabase } from "../db";
import { twilioClientFor } from "../common";
import { CallInstance } from "twilio/lib/rest/api/v2010/account/call";
import workerUtils from "../utils";

interface WebhookPayload {
  startTime: string;
  endTime: string;
  to: string;
  from: string;
  duration: string;
  callSid: string;
  recording: string;
  mimeType: string;
}

const getTwilioRecording = async (url: string) => {
  try {
    const { payload } = await Wreck.get(url);
    return { recording: payload as Buffer };
  } catch (error: any) {
    console.error(error.output);
    return { error: error.output };
  }
};

const formatPayload = (
  call: CallInstance,
  recording: Buffer
): WebhookPayload => {
  return {
    startTime: call.startTime.toISOString(),
    endTime: call.endTime.toISOString(),
    to: call.toFormatted,
    from: call.fromFormatted,
    duration: call.duration,
    callSid: call.sid,
    recording: recording.toString("base64"),
    mimeType: "audio/mpeg",
  };
};

const notifyWebhooks = async (
  db: AppDatabase,
  voiceLineId: string,
  call: CallInstance,
  recording: Buffer
) => {
  const webhooks = await db.webhooks.findAllByBackendId("voice", voiceLineId);
  if (webhooks && webhooks.length === 0) return;

  webhooks.forEach(({ id }) => {
    const payload = formatPayload(call, recording);
    workerUtils.addJob(
      "notify-webhook",
      {
        payload,
        webhookId: id,
      },
      {
        // this de-depuplicates the job
        jobKey: `webhook-${id}-call-${call.sid}`,
      }
    );
  });
};

interface TwilioRecordingTaskOptions {
  accountSid: string;
  callSid: string;
  recordingSid: string;
  voiceLineId: string;
}

const twilioRecordingTask = async (
  options: TwilioRecordingTaskOptions
): Promise<void> =>
  withDb(async (db: AppDatabase) => {
    const { voiceLineId, accountSid, callSid, recordingSid } = options;

    const voiceLine = await db.voiceLines.findById({ id: voiceLineId });
    if (!voiceLine) return;

    const provider = await db.voiceProviders.findByTwilioAccountSid(accountSid);
    if (!provider) return;

    const client = twilioClientFor(provider);
    const meta = await client.recordings(recordingSid).fetch();

    const mp3Url = "https://api.twilio.com/" + meta.uri.slice(0, -4) + "mp3";
    const { recording, error } = await getTwilioRecording(mp3Url);
    if (error) {
      throw new Error(`failed to get recording for call ${callSid}`);
    }

    const call = await client.calls(callSid).fetch();
    await notifyWebhooks(db, voiceLineId, call, recording!);
  });

export default twilioRecordingTask;
