/**
 * Try to unlock the door with auth mode
 */

import { ServerlessEventObject, ServerlessFunctionSignature } from "@twilio-labs/serverless-runtime-types/types";
import { TwilioContext } from "../../../types/TwilioContext";
import { UserAgentHeader } from "../../../utils/blockUserAgent";
import { clearLockStatusCommand, createDDBClient, ddbItemToJSON, getDoorConfigCommand, getLockStatusCommand, isLockOpen, setLockStatusCommand } from "../../../utils/ddb";
import { DoorConfig } from "../../../types/DoorConfig";
import { AuthMethod } from "../../../types/AuthMethod";
import { Lock } from "../../../types/Lock";
import { DoorStatus } from "../../../types/DoorStatus";
import { getMetricFromRegistry, withMetrics } from "../../../common/DoormanHandler";
import { AuthMetrics, registerMetrics } from "../../../metrics/AuthMetrics";
import { Counter } from "prom-client";

export interface AuthRequest extends ServerlessEventObject<{}, UserAgentHeader> {
  door?: string;
  key?: string;
  ip: string;
  timeout?: string;
}

export const handler: ServerlessFunctionSignature<TwilioContext, AuthRequest> = withMetrics('auth', async (context, event, callback, metricsRegistry) => {
  const response = new Twilio.Response();

  registerMetrics(metricsRegistry);

  let door = event.door;
  let pin = event.key;

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

  const client = createDDBClient(context);

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

  if (!config) {
    getMetricFromRegistry<Counter>(metricsRegistry, AuthMetrics.DOOR_CONFIG_NOT_FOUND).inc({ door }, 1);
    response.setStatusCode(404);
    return callback(null, response);
  }

  let correctPin = config.pin;
  let discordUsers = config.discordUsers;

  let method: AuthMethod | undefined;

  if (correctPin === pin) {
    method = AuthMethod.PIN;
  }

  if (discordUsers.includes(pin)) {
    method = AuthMethod.DISCORD;
  }

  if (!method) {
    response.setStatusCode(401);
    return callback(null, response);
  }

  getMetricFromRegistry<Counter>(metricsRegistry, AuthMetrics.AUTH_METHOD)
    .inc({ method }, 1);

  const fingerprint = { 
    method,
    userAgent: event.request.headers['user-agent'],
    ip: event.ip,
  };

  // take timeout from the query string
  const timeout = event.timeout ? parseInt(event.timeout) : config.timeout;

  // check lock status if locked, then unlock. If unlocked then lock
  await client.send(getLockStatusCommand(door))
    .then(async (lockDdb) => {
      const isOpen = isLockOpen(lockDdb);

      if (isOpen) {
        const lock: Lock = ddbItemToJSON<Lock>(lockDdb);
        const fingerprint = JSON.parse(lock.fingerprint);

        response
          .setStatusCode(200)
          .appendHeader('Content-Type', 'application/json')
          .setBody({
            status: DoorStatus.CLOSED,
            fingerprint,
          });

        await client.send(clearLockStatusCommand(lockDdb));
        return;
      }

      await client.send(setLockStatusCommand(door, timeout, fingerprint))
        .then(async (item) => {    
          response
            .setStatusCode(200)
            .appendHeader('Content-Type', 'application/json')
            .setBody({ msg: `Opened the door "${door}" for ${timeout}s` });
        }).catch((e) => {
          console.log(e);
          response
            .setStatusCode(500)
            .appendHeader('Content-Type', 'application/json')
            .setBody({ err: e });
        });
    });

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