import { ServerlessEventObject, ServerlessFunctionSignature } from "@twilio-labs/serverless-runtime-types/types";
import { TwilioContext } from "../../../types/TwilioContext";
import { jsonMsgSuffix, sendMessageToUser } from "../../../utils/discord";
import { getMetricFromRegistry, withMetrics } from "../../../common/DoormanHandler";
import { NotifyMetrics, registerMetrics } from "../../../metrics/NotifyMetrics";
import { Counter, Summary } from "prom-client";
import { z } from "zod";
import zu from "zod_utilz";
import { setResponseJson } from "../../../utils/responseUtils";
import { UserAgentHeader } from "../../../utils/blockUserAgent";

export const NotifyRequestSchema = z.object({
  key: z.string(),

  // these must be arrays
  discordUser: zu.stringToJSON().refine(arg => arg instanceof Array),
  msg: zu.stringToJSON().refine(arg => arg instanceof Array),
  json: zu.stringToJSON().refine(arg => arg instanceof Array)
});

export type NotifyRequest = z.infer<typeof NotifyRequestSchema>;

export interface NotifyRequestTwilio extends ServerlessEventObject<NotifyRequest, UserAgentHeader> { }

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

  registerMetrics(metricsRegistry);

  const req = NotifyRequestSchema.parse(event);
  
  // secure notify endpoint
  if (req.key !== context.NOTIFY_SECRET_KEY) {
    getMetricFromRegistry<Counter>(metricsRegistry, NotifyMetrics.UNAUTHENTICATED_CALL).inc(1);
    setResponseJson(response, 401, {
      msg: "Unauthenticated call", event
    });
    return callback(null, response);
  }

  let users: string[] = req.discordUser as string[];
  let msgs: string[] = req.msg as string[];
  let jsons: string[] = req.json as string[];
  let promises = [];

  try {
    const recordNotifyLatency = getMetricFromRegistry<Summary>(metricsRegistry, NotifyMetrics.DISCORD_LATENCY)
        .startTimer();

    promises = msgs.map((msg, i) => 
      sendMessageToUser(
        context,
        users[i], 
        msg + jsonMsgSuffix(jsons[i])
      )
        .then(() => {
          recordNotifyLatency();
        })
        .catch((e: Error) => {
          recordNotifyLatency();
          getMetricFromRegistry<Counter>(metricsRegistry, NotifyMetrics.DISCORD_FAILURE).inc({ error: e.name }, 1);
        })
    );
  } catch (e) {
    console.error(e);
    setResponseJson(response, 500, { msg: e, event });
    return callback(null, response);
  }

  let timer = setTimeout(() => {
    console.log("Ungraceful finish: running out of time");
    getMetricFromRegistry<Counter>(metricsRegistry, NotifyMetrics.NOTIFY_TIMEOUT).inc(1);
    callback(null, response);
  }, 9500);

  await Promise.all(promises);
  clearTimeout(timer);
  return callback(null, response);
});
