import * as Hapi from "@hapi/hapi";
import Joi from "joi";
import * as Helpers from "../helpers/index.js";
import Boom from "@hapi/boom";

const getSignalService = (request) => request.services("app").signaldService;

export const GetAllSignalBotsRoute = Helpers.withDefaults({
  method: "get",
  path: "/api/signal/bots",
  options: {
    description: "Get all bots",
    async handler(request: Hapi.Request, _h: Hapi.ResponseToolkit) {
      const signalService = getSignalService(request);
      const bots = await signalService.findAll();

      if (bots) {
        // with the pino logger the first arg is an object of data to log
        // the second arg is a message
        // all other args are formated args for the msg
        request.logger.info({ bots }, "Retrieved bot(s) at %s", new Date());

        return { bots };
      }

      return _h.response().code(204);
    },
  },
});

export const GetBotsRoute = Helpers.noAuth({
  method: "get",
  path: "/api/signal/bots/{token}",
  options: {
    description: "Get one bot",
    async handler(request: Hapi.Request, _h: Hapi.ResponseToolkit) {
      const { token } = request.params;
      const signalService = getSignalService(request);

      const bot = await signalService.findByToken(token);

      if (bot) {
        // with the pino logger the first arg is an object of data to log
        // the second arg is a message
        // all other args are formated args for the msg
        request.logger.info({ bot }, "Retrieved bot(s) at %s", new Date());

        return bot;
      }

      throw Boom.notFound("Bot not found");
    },
  },
});

interface MessageRequest {
  phoneNumber: string;
  message: string;
}

export const SendBotRoute = Helpers.noAuth({
  method: "post",
  path: "/api/signal/bots/{token}/send",
  options: {
    description: "Send a message",
    async handler(request: Hapi.Request, _h: Hapi.ResponseToolkit) {
      const { token } = request.params;
      const { phoneNumber, message } = request.payload as MessageRequest;
      const signalService = getSignalService(request);

      const bot = await signalService.findByToken(token);

      if (bot) {
        request.logger.info({ bot }, "Sent a message at %s", new Date());

        await signalService.send(bot, phoneNumber, message as string);
        return _h
          .response({
            result: {
              recipient: phoneNumber,
              timestamp: new Date().toISOString(),
              source: bot.phoneNumber,
            },
          })
          .code(200); // temp
      }

      throw Boom.notFound("Bot not found");
    },
  },
});

interface ResetSessionRequest {
  phoneNumber: string;
}

export const ResetSessionBotRoute = Helpers.noAuth({
  method: "post",
  path: "/api/signal/bots/{token}/resetSession",
  options: {
    description: "Reset a session with another user",
    async handler(request: Hapi.Request, _h: Hapi.ResponseToolkit) {
      const { token } = request.params;
      const { phoneNumber } = request.payload as ResetSessionRequest;
      const signalService = getSignalService(request);

      const bot = await signalService.findByToken(token);

      if (bot) {
        await signalService.resetSession(bot, phoneNumber);
        return _h
          .response({
            result: {
              recipient: phoneNumber,
              timestamp: new Date().toISOString(),
              source: bot.phoneNumber,
            },
          })
          .code(200); // temp
      }

      throw Boom.notFound("Bot not found");
    },
  },
});

export const ReceiveBotRoute = Helpers.withDefaults({
  method: "get",
  path: "/api/signal/bots/{token}/receive",
  options: {
    description: "Receive messages",
    async handler(request: Hapi.Request, _h: Hapi.ResponseToolkit) {
      const { token } = request.params;
      const signalService = getSignalService(request);

      const bot = await signalService.findByToken(token);

      if (bot) {
        request.logger.info({ bot }, "Received messages at %s", new Date());

        return signalService.receive(bot);
      }

      throw Boom.notFound("Bot not found");
    },
  },
});

export const RegisterBotRoute = Helpers.withDefaults({
  method: "get",
  path: "/api/signal/bots/{id}/register",
  options: {
    description: "Register a bot",
    async handler(request: Hapi.Request, h: Hapi.ResponseToolkit) {
      const { id } = request.params;
      const signalService = getSignalService(request);
      const { code } = request.query;

      const bot = await signalService.findById(id);
      if (!bot) throw Boom.notFound("Bot not found");

      try {
        request.logger.info({ bot }, "Create bot at %s", new Date());
        await signalService.register(bot, code);
        return h.response(bot).code(200);
      } catch (error) {
        return h.response().code(error.code);
      }
    },
  },
});

interface BotRequest {
  phoneNumber: string;
  description: string;
}

export const CreateBotRoute = Helpers.withDefaults({
  method: "post",
  path: "/api/signal/bots",
  options: {
    description: "Register a bot",
    async handler(request: Hapi.Request, _h: Hapi.ResponseToolkit) {
      const { phoneNumber, description } = request.payload as BotRequest;
      const signalService = getSignalService(request);
      console.log("request.auth.credentials:", request.auth.credentials);

      const bot = await signalService.create(
        phoneNumber,
        description,
        request.auth.credentials.email as string
      );
      if (bot) {
        request.logger.info({ bot }, "Create bot at %s", new Date());
        return bot;
      }

      throw Boom.notFound("Bot not found");
    },
  },
});

export const RequestCodeRoute = Helpers.withDefaults({
  method: "get",
  path: "/api/signal/bots/{id}/requestCode",
  options: {
    description: "Register a bot",
    validate: {
      params: Joi.object({
        id: Joi.string().uuid().required(),
      }),
      query: Joi.object({
        mode: Joi.string().valid("sms", "voice").required(),
        captcha: Joi.string(),
      }),
    },
    async handler(request: Hapi.Request, h: Hapi.ResponseToolkit) {
      const { id } = request.params;
      const { mode, captcha } = request.query;
      const signalService = getSignalService(request);

      const bot = await signalService.findById(id);

      if (!bot) {
        throw Boom.notFound("Bot not found");
      }

      try {
        if (mode === "sms") {
          await signalService.requestSMSVerification(bot, captcha);
        } else if (mode === "voice") {
          await signalService.requestVoiceVerification(bot, captcha);
        }

        return h.response().code(200);
      } catch (error) {
        console.log(error);
        if (error.name === "CaptchaRequiredException") {
          return h.response().code(402);
        }

        if (error.code) {
          return h.response().code(error.code);
        }

        return h.response().code(500);
      }
    },
  },
});
