import * as Hapi from "@hapi/hapi";
import Joi from "joi";
import * as Boom from "@hapi/boom";
import * as R from "remeda";
import * as Helpers from "../helpers/index.js";
import Twilio from "twilio";
import {
  crudRoutesFor,
  CrudControllerBase,
} from "@digiresilience/metamigo-common";
import { VoiceLineRecord, SavedVoiceLine } from "@digiresilience/metamigo-db";

const TwilioHandlers = {
  async freeNumbers(provider, request: Hapi.Request) {
    const { accountSid, apiKeySid, apiKeySecret } = provider.credentials;
    const client = Twilio(apiKeySid, apiKeySecret, {
      accountSid,
    });
    const numbers = R.pipe(
      await client.incomingPhoneNumbers.list({ limit: 100 }),
      R.filter((n) => n.capabilities.voice),
      R.map(R.pick(["sid", "phoneNumber"]))
    );
    const numberSids: any = R.map(numbers, R.prop("sid"));
    const voiceLineRepo = request.db().voiceLines;
    const voiceLines: SavedVoiceLine[] =
      await voiceLineRepo.findAllByProviderLineSids(numberSids);
    const voiceLineSids = new Set(R.map(voiceLines, R.prop("providerLineSid")));

    return R.pipe(
      numbers,
      R.reject((n) => voiceLineSids.has(n.sid as any)),
      R.map((n) => ({ id: n.sid, name: n.phoneNumber }))
    );
  },
};

export const VoiceProviderRoutes = Helpers.withDefaults([
  {
    method: "GET",
    path: "/api/voice/providers/{providerId}/freeNumbers",
    options: {
      description:
        "get a list of the incoming numbers for a provider account that aren't assigned to a voice line",
      validate: {
        params: {
          providerId: Joi.string().uuid().required(),
        },
      },
      async handler(request: Hapi.Request, _h: Hapi.ResponseToolkit) {
        const { providerId } = request.params;
        const voiceProvidersRepo = request.db().voiceProviders;
        const provider = await voiceProvidersRepo.findById(providerId);
        if (!provider) return Boom.notFound();
        switch (provider.kind) {
          case "TWILIO": {
            return TwilioHandlers.freeNumbers(provider, request);
          }

          default: {
            return Boom.badImplementation();
          }
        }
      },
    },
  },
]);

class VoiceLineRecordController extends CrudControllerBase(VoiceLineRecord) {}

const validator = (): Record<string, Hapi.RouteOptionsValidate> => ({
  create: {
    payload: Joi.object({
      providerType: Joi.string().required(),
      providerId: Joi.string().required(),
      number: Joi.string().required(),
      language: Joi.string().required(),
      voice: Joi.string().required(),
      promptText: Joi.string().optional(),
      promptRecording: Joi.binary()
        .encoding("base64")
        .max(50 * 1000 * 1000)
        .optional(),
    }).label("VoiceLineCreate"),
  },
  updateById: {
    params: {
      id: Joi.string().uuid().required(),
    },
    payload: Joi.object({
      providerType: Joi.string().optional(),
      providerId: Joi.string().optional(),
      number: Joi.string().optional(),
      language: Joi.string().optional(),
      voice: Joi.string().optional(),
      promptText: Joi.string().optional(),
      promptRecording: Joi.binary()
        .encoding("base64")
        .max(50 * 1000 * 1000)
        .optional(),
    }).label("VoiceLineUpdate"),
  },
  deleteById: {
    params: {
      id: Joi.string().uuid().required(),
    },
  },
  getById: {
    params: {
      id: Joi.string().uuid().required(),
    },
  },
});

export const VoiceLineRoutes = Helpers.withDefaults(
  crudRoutesFor(
    "voice-line",
    "/api/voice/voice-line",
    new VoiceLineRecordController("voiceLines", "id"),
    "id",
    validator()
  )
);

export * from "./twilio/index.js";
