import { Request, Response } from "express";
import { getIO } from "../libs/socket";
import { removeWbot } from "../libs/wbot";
import { StartWhatsAppSession } from "../services/WbotServices/StartWhatsAppSession";

import { removeDir } from "../helpers/DeleteDirectory";

import CreateWhatsAppService from "../services/WhatsappService/CreateWhatsAppService";
import DeleteWhatsAppService from "../services/WhatsappService/DeleteWhatsAppService";
import ListWhatsAppsService from "../services/WhatsappService/ListWhatsAppsService";
import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService";
import UpdateWhatsAppService from "../services/WhatsappService/UpdateWhatsAppService";

import AppError from "../errors/AppError";

import getNumberFromName from "../helpers/GetNumberSequence";
import phoneNumberStart from "../helpers/PhoneNumberStatusCode";

import path, { join } from "path";
import validatePhoneName from "../helpers/ValidatePhoneName";
import postData from "../helpers/AxiosPost";
import Whatsapp from "../models/Whatsapp";
import Message from "../models/Message";
import FindOrCreateTicketService from "../services/TicketServices/FindOrCreateTicketService";
import {
  handleMessage,
  handleMsgAck,
  verifyContact,
  verifyMessage
} from "../services/WbotServices/wbotMessageListener";
import Contact from "../models/Contact";
import CreateOrUpdateContactService from "../services/ContactServices/CreateOrUpdateContactService";
import GetDefaultWhatsApp from "../helpers/GetDefaultWhatsApp";
import ShowUserService from "../services/UserServices/ShowUserService";

import fs from "fs";
import receiveWhatsAppMediaOfficialAPI from "../helpers/ReceiveWhatsAppMediaOfficialAPI";

import whatsappOfficialAPI from "../helpers/WhatsappOfficialAPI";
import whatsappOfficialNumberInfo from "../helpers/WhatsappOfficialNumberInfo";
import { getSettingValue } from "../helpers/WhaticketSettings";
import ListWhatsAppsNumber from "../services/WhatsappService/ListWhatsAppsNumber";
import SettingTicket from "../models/SettingTicket";
import { Op } from "sequelize";
import { del, get, set } from "../helpers/RedisClient";

interface WhatsappData {
  name: string;
  queueIds: number[];
  url: string;
  urlApi: string;
  greetingMessage?: string;
  farewellMessage?: string;
  status?: string;
  isDefault?: boolean;
  isOfficial?: boolean;
  phoneNumberId?: string;
  number?: string;
  wabaId?: string;
}

let count: number = 0;

export const index = async (req: Request, res: Response): Promise<Response> => {
  let whatsapps = await ListWhatsAppsService();

  if (getSettingValue("whatsaAppCloudApi")?.value == "enabled") {
    // Atualizar isso quando tiver tempo
    if (count > 12) count = 0;
    if (count == 0) {
      for (let i in whatsapps) {
        const { id, wabaId, isOfficial } = whatsapps[i];

        if (isOfficial && wabaId) {
          try {
            const info = await whatsappOfficialNumberInfo(wabaId);
            if (info) {
              const whatsapp = await Whatsapp.findByPk(id);

              if (whatsapp) {
                whatsapp.update({
                  classification: info.quality_rating
                });
                whatsapps[i].classification = info.quality_rating;
              }
            }
          } catch (error) {
            console.log(
              "error on try update classification number from oficial whatsapp in WhatsappController.ts: ",
              error
            );
          }
        }
      }
    }
    console.log("count: ", count);
    count++;
  }

  return res.status(200).json(whatsapps);
};

export const whatsAppOfficialMatchQueue = async (
  req: Request,
  res: Response
): Promise<Response> => {
  const { userId, queueId }: any = req.query;

  let whatsapps = await GetDefaultWhatsApp({ userId, queueId });

  if (whatsapps && Array.isArray(whatsapps)) {
    const uniqueWhatsApps = whatsapps.filter(
      (whatsapp, index, self) =>
        index === self.findIndex(w => w.number === whatsapp.number)
    );
    whatsapps = uniqueWhatsApps;
  }

  return res.status(200).json(whatsapps);
};

export const whatsAppOfficialMatchQueueUser = async (
  req: Request,
  res: Response
): Promise<Response> => {
  const { userId, queueId }: any = req.query;

  let whatsApps: any = await ListWhatsAppsService();
  let user: any = await ShowUserService(userId);

  // console.log(JSON.stringify(user, null, 2));

  let queuesConnected = whatsApps
    .filter((w: any) => w.status === "CONNECTED")
    .map((item: any) => {
      const { queues } = item;
      return {
        queues: queues.map((q: any) => {
          return { id: q.id };
        })
      };
    })
    .flatMap((item: any) => item.queues.map((queue: any) => queue.id));

  queuesConnected = [...new Set(queuesConnected)].map(q => {
    return { id: q };
  });

  const userQueues = user.queues.map((item: any) => {
    const { id, name, color } = item;
    return {
      id,
      name,
      color,
      disable: queuesConnected.find((queue: any) => queue.id === id)
        ? false
        : true
    };
  });

  return res.status(200).json(userQueues);
};

export const media = async (req: Request, res: Response) => {
  const { filename } = req.params;

  const filePath = join(__dirname, "..", "..", "..", "..", "public", filename);

  console.log("filePath: ", filePath);

  console.log(filename);

  if (!fs.existsSync(filePath)) {
    return res.status(404).json({ message: "File not folund!" });
  }

  // Set appropriate headers for the download.
  res.setHeader("Content-Disposition", `attachment; filename=${filename}`);
  res.sendFile(filePath);
};

export const weebhook = async (
  req: Request,
  res: Response
): Promise<Response> => {
  // console.log(JSON.stringify(req.body, null, 2));

  console.log("req.method: ", req.method);

  if (req.method == "GET") {
    /**
     * UPDATE YOUR VERIFY TOKEN
     *This will be the Verify Token value when you set up webhook
     **/
    const verify_token = process.env.VERIFY_TOKEN;

    // Parse params from the webhook verification request
    let mode = req.query["hub.mode"];
    let token = req.query["hub.verify_token"];
    let challenge = req.query["hub.challenge"];

    // Check if a token and mode were sent
    if (mode && token) {
      // Check the mode and token sent are correct
      if (mode === "subscribe" && token === verify_token) {
        // Respond with 200 OK and challenge token from the request
        console.log("WEBHOOK_VERIFIED");
        return res.status(200).send(challenge);
      } else {
        // Responds with '403 Forbidden' if verify tokens do not match
        return res.sendStatus(403);
      }
    }

    return res.sendStatus(500);
  }

  // MESSAGE
  if (req.body.object) {
    if (
      req.body.entry &&
      req.body.entry[0].changes &&
      req.body.entry[0].changes[0] &&
      req.body.entry[0].changes[0].value.messages &&
      req.body.entry[0].changes[0].value.messages[0]
    ) {
      const message = req.body.entry[0].changes[0].value.messages[0];
      const contact_from = message.from; // extract the phone number from the webhook payload
      const contact_to =
        req.body.entry[0].changes[0].value.metadata.display_phone_number;
      let type = message.type;

      const contact_to_exist = await get({
        key: "whatsapp:*",
        value: `${contact_to}`
      });

      if (contact_to_exist == null) {
        console.log(
          "WHATSAPP OFFICIAL",
          " | CONCTACT_FROM: ",
          contact_from,
          " | CONTACT_TO: ",
          contact_to
        );
        console.log(
          "NUMBER IGNORED. WHATSAPP NUMBER FROM ANOTHER OMNIHIT APPLICATION!"
        );
        return res.status(403).json({ error: "Unauthorized" });
      }

      let wbot = {};
      let msg = {};
      let contacts = req.body.entry[0].changes[0].value.contacts[0];

      msg = {
        ...msg,
        id: { id: message.id },
        fromMe: false,
        type: type,
        read: false,
        hasMedia: false
      };

      // NEW
      const whatsapp = await ShowWhatsAppService(null, {
        number: contact_to
      });

      if (type == "text") {
        if (!message?.text?.body) {
          return res.status(400).json({ error: "body not found" });
        }

        type = "chat";
        msg = {
          ...msg,
          body: message.text.body, // extract the message text from the webhook payload,
          type
        };
      } else {
        if (!message[message?.type]?.id) {
          return res.status(400).json({ error: "id not found" });
        }

        const mediaId = message[message.type].id;
        const mimetype = message[message.type].mime_type;

        let filename = await receiveWhatsAppMediaOfficialAPI(
          mediaId,
          whatsapp.phoneNumberId
        );

        if (!filename) throw new AppError("There was an error");

        msg = {
          ...msg,
          hasMedia: true
        };

        wbot = { ...wbot, media: { filename, mimetype } };
      }

      msg = { ...msg, phoneNumberId: whatsapp.phoneNumberId };

      console.log("from: ", contact_from);
      console.log("to: ", contact_to);
      console.log("msg type: ", type);

      wbot = {
        ...wbot,
        id: whatsapp.id,
        msgContact: {
          number: contact_from,
          name: contacts?.profile?.name
        },
        chat: { isGroup: false, unreadCount: 1 },
        quotedMsg: message && message?.context ? message.context.id : undefined
      };

      handleMessage(msg, wbot, true);

      return res.sendStatus(200);
    }
    // STATUS MESSAGE SENT
    else if (
      req.body.entry &&
      req.body.entry[0].changes &&
      req.body.entry[0].changes[0] &&
      req.body.entry[0].changes[0].value.statuses &&
      req.body.entry[0].changes[0].value.statuses[0]
    ) {
      const id = req.body.entry[0].changes[0].value.statuses[0].id;
      const ack = req.body.entry[0].changes[0].value.statuses[0].status;
      handleMsgAck(id, ack, true);
    }
  }

  return res.sendStatus(200);
};

export const store = async (req: Request, res: Response): Promise<Response> => {
  let {
    name,
    status,
    isDefault,
    greetingMessage,
    farewellMessage,
    queueIds,
    url,
    urlApi,
    phoneNumberId,
    wabaId,
    isOfficial,
    number
  }: WhatsappData = req.body;

  if (req.user.profile !== "master") {
    throw new AppError("ERR_NO_PERMISSION", 403);
  }

  const invalid = checkWhatsAppData({
    urlApi,
    isOfficial,
    phoneNumberId,
    wabaId,
    number
  });

  if (invalid) {
    return res.status(400).json(invalid);
  }

  if (isOfficial) {
    urlApi = "";
    url = "";
  } else if (!isOfficial) {
    phoneNumberId = "";
    wabaId = "";
    number = "";
  }

  let invalidPhoneName = validatePhoneName(name);

  if (invalidPhoneName) {
    return res.status(200).json({ message: invalidPhoneName });
  }

  const { whatsapp, oldDefaultWhatsapp } = await CreateWhatsAppService({
    name,
    url,
    urlApi,
    status,
    isDefault,
    greetingMessage,
    farewellMessage,
    queueIds,
    phoneNumberId,
    wabaId,
    isOfficial,
    number
  });

  console.log("whatsapp.id: ", whatsapp.id);

  if (!isOfficial) {
    postData(`${whatsapp.urlApi}/api/session`, {
      app_name: process.env.APP_NAME,
      whatsappId: whatsapp.id,
      number: getNumberFromName(name),
      client_url: `${process.env.BACKEND_URL_RAW}:${process.env.PORT}`
    });
  } else {
    await set(
      `whatsapp:${whatsapp.id}`,
      JSON.stringify({
        number: whatsapp?.number,
        id: whatsapp?.id,
        greetingMessage: whatsapp?.greetingMessage,
        phoneNumberId: whatsapp?.phoneNumberId
      })
    );
  }

  const io = getIO();
  io.emit("whatsapp", {
    action: "update",
    whatsapp
  });

  if (oldDefaultWhatsapp) {
    io.emit("whatsapp", {
      action: "update",
      whatsapp: oldDefaultWhatsapp
    });
  }

  return res.status(200).json(whatsapp);
};

export const show = async (req: Request, res: Response): Promise<Response> => {
  const { whatsappId } = req.params;

  const whatsapp = await ShowWhatsAppService(whatsappId);

  return res.status(200).json(whatsapp);
};

export const update = async (
  req: Request,
  res: Response
): Promise<Response> => {
  const { whatsappId } = req.params;
  const whatsappData = req.body;

  let invalidPhoneName = validatePhoneName(whatsappData.name);

  if (invalidPhoneName) {
    return res.status(200).json({ message: invalidPhoneName });
  }

  const { urlApi, isOfficial, phoneNumberId, number, wabaId } = whatsappData;

  const invalid = checkWhatsAppData({
    urlApi,
    isOfficial,
    phoneNumberId,
    wabaId,
    number
  });

  if (invalid) {
    return res.status(400).json(invalid);
  }

  if (isOfficial) {
    whatsappData.urlApi = "";
    whatsappData.url = "";
  } else if (!isOfficial) {
    whatsappData.phoneNumberId = "";
    whatsappData.wabaId = "";
    whatsappData.number = "";
  }

  const { whatsapp, oldDefaultWhatsapp } = await UpdateWhatsAppService({
    whatsappData,
    whatsappId
  });

  if (!whatsappData?.isOfficial) {
    postData(`${whatsapp.urlApi}/api/session`, {
      app_name: process.env.APP_NAME,
      whatsappId: whatsapp.id,
      number: getNumberFromName(whatsapp.name),
      client_url: `${process.env.BACKEND_URL_RAW}:${process.env.PORT}`
    });
  } else {
    await set(
      `whatsapp:${whatsapp.id}`,
      JSON.stringify({
        number: whatsapp?.number,
        id: whatsapp?.id,
        greetingMessage: whatsapp?.greetingMessage,
        phoneNumberId: whatsapp?.phoneNumberId
      })
    );
  }

  const io = getIO();
  io.emit("whatsapp", {
    action: "update",
    whatsapp
  });

  if (oldDefaultWhatsapp) {
    io.emit("whatsapp", {
      action: "update",
      whatsapp: oldDefaultWhatsapp
    });
  }

  return res.status(200).json(whatsapp);
};

export const remove = async (
  req: Request,
  res: Response
): Promise<Response> => {
  if (req.user.profile !== "master") {
    throw new AppError("ERR_NO_PERMISSION", 403);
  }

  const { whatsappId } = req.params;

  const whatsapp: any = await Whatsapp.findByPk(whatsappId, { raw: true });

  if (!whatsapp?.isOfficial) {
    postData(`${whatsapp.urlApi}/api/session/del`, {
      app_name: process.env.APP_NAME,
      whatsappId: whatsappId
    });
  }

  await del(`whatsapp:${whatsappId}`);

  let whats = await ListWhatsAppsNumber(whatsappId);

  //  Remove tickets business hours config
  if (whats?.whatsapps?.length == 1) {
    const configIds = await SettingTicket.findAll({
      where: { number: whats?.whatsapps[0]?.number },
      raw: true,
      attributes: ["id"]
    });

    const whatsappTicketConfig = await SettingTicket.findOne({
      where: { number: whats.whatsapps[0].number }
    });

    if (whatsappTicketConfig) {
      try {
        await SettingTicket.destroy({
          where: {
            id: {
              [Op.in]: configIds.map(config => config.id)
            }
          }
        });
      } catch (error) {
        console.log(
          "Error on delete SettingTicket by number: ",
          whats?.whatsapps[0]?.number
        );
      }
    }
  }

  await DeleteWhatsAppService(whatsappId);

  removeDir(
    path.join(process.cwd(), ".wwebjs_auth", `session-bd_${whatsappId}`)
  );

  removeDir(
    path.join(
      process.cwd(),
      ".wwebjs_auth",
      "sessions",
      `session-bd_${whatsappId}`
    )
  );

  removeWbot(+whatsappId);

  const io = getIO();
  io.emit("whatsapp", {
    action: "delete",
    whatsappId: +whatsappId
  });

  return res.status(200).json({ message: "Whatsapp deleted." });
};

interface WhatsappDataValidate {
  urlApi?: string;
  isOfficial?: boolean;
  phoneNumberId?: string;
  wabaId?: string;
  number?: string;
}

const checkWhatsAppData = ({
  urlApi,
  isOfficial,
  phoneNumberId,
  wabaId,
  number
}: WhatsappDataValidate) => {
  if (isOfficial && (!phoneNumberId || phoneNumberId.trim() == "")) {
    return { message: "Phone number Id is required!" };
  } else if (isOfficial && (!wabaId || wabaId.trim() == "")) {
    return { message: "WABA ID is required!" };
  } else if (isOfficial && (!number || number.trim() == "")) {
    return { message: "Phone number is required!" };
  } else if (!isOfficial && (!urlApi || urlApi.trim() == "")) {
    return { message: "urlApi is required!" };
  }
};
