
/**
 * Edit API for doors
 */

import { ServerlessEventObject, ServerlessFunctionSignature } from "@twilio-labs/serverless-runtime-types/types";
import { TwilioContext } from "../../../types/TwilioContext";
import { shouldBlockRequest, UserAgentHeader } from "../../../utils/blockUserAgent";
import { createDDBClient, ddbItemToJSON, getDoorConfigCommand, getDoorConfigUpdateCommand, putDoorUpdateConfigCommand, replaceDoorConfigWithUpdateItem } from "../../../utils/ddb";
import { DoorConfig, EditDoorConfig } from "../../../types/DoorConfig";
import { sendMessageToUser } from "../../../utils/discord";

export interface EditRequest extends ServerlessEventObject<{}, UserAgentHeader> {
  door?: string;
  approvalId?: string;
  newConfig?: string;
}

export const handler: ServerlessFunctionSignature<TwilioContext, EditRequest> = async function(context, event, callback) {
  const response = new Twilio.Response();

  if (shouldBlockRequest(event)) {
    response.setStatusCode(200);
    return callback(null, response);
  }

  let door = event.door;
  let approvalId = event.approvalId;
  let newConfigString = event.newConfig;

  const client = createDDBClient(context);

  // approve path
  if (door && approvalId) {
    const newConfigDdb = await client.send(getDoorConfigUpdateCommand(door));
    const newConfig = ddbItemToJSON<EditDoorConfig>(newConfigDdb);

    if (!newConfig || newConfig.approvalId !== approvalId) {
      response.setStatusCode(400);
      return callback(null, response);
    }

    await client.send(replaceDoorConfigWithUpdateItem(newConfigDdb as any));

    // send update to discord users
    const updateMessage = `Configuration change \`${approvalId}\` was approved @ Door "${door}"`;

    const discordPromises = newConfig.discordUsers.map((user) => {
      return sendMessageToUser(
        context,
        user,
        updateMessage,
      ).catch(e => console.error(e))
    });

    await Promise.all(discordPromises);

    response.setStatusCode(200);
    return callback(null, response);
  }

  if (!door || !newConfigString) {
    response.setStatusCode(400);
    return callback(null, response);
  }

  const newConfig: EditDoorConfig = JSON.parse(newConfigString);

  const configDdb = await client.send(getDoorConfigCommand(door));
  const config = ddbItemToJSON<DoorConfig>(configDdb);

  if (!config) {
    response.setStatusCode(404);
    return callback(null, response);
  }

  // set to old PIN if it is missing
  if (newConfig.pin === "") {
    newConfig.pin = config.pin;
  }

  const input = putDoorUpdateConfigCommand(door, newConfig);

  const update = await client.send(input);

  // newConfig.discordUsers = undefined;
  // newConfig.fallbackNumbers = undefined;
  // newConfig.status = undefined;

  const approvalUrl = `https://doorman.chromart.cc/api/door/edit?door=${door}&approvalId=${input?.input?.Item?.approvalId.S as string}`;
  console.log(approvalUrl);

  // send update to discord users
  const approvalMessage = `Configuration change requested @ Door "${door}" [click to approve it](${approvalUrl})\`\`\`${JSON.stringify(newConfig, null, 4)}\`\`\``;

  const discordPromises = config.discordUsers.map((user) => {
    return sendMessageToUser(
      context,
      user,
      approvalMessage,
    ).catch(e => console.error(e))
  });

  await Promise.all(discordPromises);

  response
    .setStatusCode(200)
    .appendHeader('Content-Type', 'application/json')
    .setBody({ msg: update });

  await client.destroy();
  return callback(null, response);
};
