import { randomUUID } from "crypto";
import { DynamoDBClient, GetItemCommand, DeleteItemCommand, PutItemCommand, UpdateItemCommand, GetItemOutput } from "@aws-sdk/client-dynamodb";
import { TwilioContext } from "../types/TwilioContext";
import { DoorConfig } from "../types/DoorConfig";
import { DynaBridge } from "dynabridge";
import { DoorConfigEntity, EditDoorConfigEntity } from "../schema/DoorConfig";
import { DoorAliasEntity } from "../schema/DoorAlias";
import { LockStatusEntity } from "../schema/LockStatus";

export const createDDBClient = (context: TwilioContext) => {
  return new DynamoDBClient({ 
    region: "us-east-1" ,
    credentials: {
      accessKeyId: context.AWS_ACCESS_KEY,
      secretAccessKey: context.AWS_SECRET_ACCESS_KEY,
    },
  });
};

export const createDynaBridgeClient = (context: TwilioContext) => {
  // register all entities here
  return new DynaBridge({
    doorConfig: DoorConfigEntity,
    editDoorConfig: EditDoorConfigEntity,
    doorAlias: DoorAliasEntity,
    lockStatus: LockStatusEntity,
  }, {
    serialize: (entity) => entity,
    deserialize: (entity) => {
      // convert all set to array
      const convertedObj = Object.fromEntries(
        Object.entries(entity).map(([key, value]) => {
          if (value instanceof Set) {
            return [key, Array.from(value)];
          }
          return [key, value];
        })
      );
      return convertedObj;
    }
  }, {
    region: "us-east-1" ,
    credentials: {
      accessKeyId: context.AWS_ACCESS_KEY,
      secretAccessKey: context.AWS_SECRET_ACCESS_KEY,
    },
  });
};

export const getLockStatusCommand = (door: string) => {
  return new GetItemCommand({
    TableName: "doorman",
    Key: {
      "PK": {
        S: door,
      },
      "SK": {
        S: "lock",
      }
    },
  });
};

export const getDoorAliasCommand = (buzzerNumber: string) => {
  return new GetItemCommand({
    TableName: "doorman",
    Key: {
      "PK": {
        S: buzzerNumber,
      },
      "SK": {
        S: "alias",
      }
    },
  });
};

export const getDoorConfigCommand = (door: string) => {
  return new GetItemCommand({
    TableName: "doorman",
    Key: {
      "PK": {
        S: `door-${door}`,
      },
      "SK": {
        S: "config",
      },
    },
  });
};

export const isLockOpen = (lock: GetItemOutput) => {
  // ttl is a UTC ms time for how long it is unlocked
  const ttl = lock.Item?.TTL?.N || 0;

  return parseInt("" + ttl) > Date.now();
};

export const clearLockStatusCommand = (lock: GetItemOutput) => {
  return new DeleteItemCommand({
    TableName: "doorman",
    Key: {
      "PK": {
        S: lock?.Item?.PK.S as string,
      },
      "SK": {
        S: "lock",
      }
    }
  });
};

export const setLockStatusCommand = (door: string, timeoutSeconds: number, fingerprintObj: any) => {
  return new PutItemCommand({
    TableName: "doorman",
    Item: {
      "PK": {
        S: door,
      },
      "SK": {
        S: "lock",
      },
      "TTL": {
        N: `${Date.now() + timeoutSeconds * 1000}`,
      },
      "fingerprint": {
        S: JSON.stringify(fingerprintObj),
      }
    }
  });
};

export const putDoorUpdateConfigCommand = (door: string, config: DoorConfig) => {
  return new PutItemCommand({
    TableName: "doorman",
    Item: {
      "PK": {
        S: "door-" + door,
      },
      "SK": {
        S: "config-update",
      },
      "buzzer": {
        S: config.buzzer,
      },
      "buzzerCode": {
        S: config.buzzerCode,
      },
      "discordUsers": {
        SS: config.discordUsers,
      },
      "fallbackNumbers": {
        SS: config.fallbackNumbers,
      },
      "pin": {
        S: config.pin,
      },
      "pressKey": {
        S: config.pressKey,
      },
      "timeout": {
        N: `${config.timeout}`,
      },
      "greeting": {
        S: config.greeting,
      },
      "approvalId": {
        S: randomUUID().toString(),
      }
    }
  });
};

export function ddbItemToJSON<T extends Record<string, any>>(ddbItem: GetItemOutput): T {
  let obj: any = {};

  if (!ddbItem.Item) {
    return obj;
  }

  const Item = ddbItem.Item;

  Object.keys(Item).forEach(key => {
    obj[key] = Object.values(Item[key])[0];
  });
  return obj;
};

export const getDoorConfigUpdateCommand = (door: string) => {
  return new GetItemCommand({
    TableName: "doorman",
    Key: {
      "PK": {
        S: `door-${door}`,
      },
      "SK": {
        S: "config-update",
      },
    },
  });
};

export const replaceDoorConfigWithUpdateItem = (newConfigItem: GetItemOutput & { Item: { approvalId?: { S: string } }}) => {
  const newItem = {
    ...newConfigItem.Item,
    SK: { S: "config" },
  };

  delete newItem.approvalId;

  return new PutItemCommand({
    TableName: "doorman",
    Item: newItem,
  });
};
