import { join } from "path";
import { promisify } from "util";
import { writeFile } from "fs";
import * as Sentry from "@sentry/node";

import { copyFolder } from "../../helpers/CopyFolder";
import { removeDir } from "../../helpers/DeleteDirectory";
import path from "path";

import {
  isHoliday,
  isOutBusinessTime,
  isOutBusinessTimeSaturday,
  isWeekend
} from "../../helpers/TicketConfig";

import {
  format as _format,
  isWithinInterval,
  parse,
  subMinutes,
  isSaturday,
  isSunday,
  parseISO
} from "date-fns";
import ptBR from "date-fns/locale/pt-BR";

import { utcToZonedTime, zonedTimeToUtc } from "date-fns-tz";

import {
  Contact as WbotContact,
  Message as WbotMessage,
  MessageAck,
  Client
} from "whatsapp-web.js";

import Contact from "../../models/Contact";
import Ticket from "../../models/Ticket";
import Message from "../../models/Message";

import { getIO } from "../../libs/socket";
import CreateMessageService from "../MessageServices/CreateMessageService";
import { logger } from "../../utils/logger";
import CreateOrUpdateContactService from "../ContactServices/CreateOrUpdateContactService";
import FindOrCreateTicketService from "../TicketServices/FindOrCreateTicketService";
import ShowWhatsAppService from "../WhatsappService/ShowWhatsAppService";
import { debounce } from "../../helpers/Debounce";
import UpdateTicketService from "../TicketServices/UpdateTicketService";
import { date, name } from "faker";

import ShowQueueService from "../QueueService/ShowQueueService";
import ShowTicketMessage from "../TicketServices/ShowTicketMessage";
import BotIsOnQueue from "../../helpers/BotIsOnQueue";
import Queue from "../../models/Queue";

import fs from "fs";

import { StartWhatsAppSession } from "../../services/WbotServices/StartWhatsAppSession";
import { removeWbot } from "../../libs/wbot";
import { restartWhatsSession } from "../../helpers/RestartWhatsSession";

// test del
import data_ura from "./ura";
import msg_client_transfer from "./ura_msg_transfer";
import final_message from "./ura_final_message";
import SendWhatsAppMessage from "./SendWhatsAppMessage";
import Whatsapp from "../../models/Whatsapp";
import { splitDateTime } from "../../helpers/SplitDateTime";
//

import { updateTicketCacheByTicketId } from "../../helpers/TicketCache";
import {
  insertMessageContactCache,
  getLastId
} from "../../helpers/LastMessageIdByContactCache";
import autoRestore from "../../helpers/AutoRestore";
import { _restore } from "../../helpers/RestoreControll";
import sendWhatsAppMessageSocket from "../../helpers/SendWhatsappMessageSocket";
import {
  getWhatsappIds,
  setWhatsappId
} from "../../helpers/WhatsappIdMultiSessionControl";
import AppError from "../../errors/AppError";
import { setMessageAsRead } from "../../helpers/SetMessageAsRead";
import { getSettingValue } from "../../helpers/WhaticketSettings";

import { Op, json } from "sequelize";

import SettingTicket from "../../models/SettingTicket";
import mostRepeatedPhrase from "../../helpers/MostRepeatedPhrase";
import ListWhatsAppsNumber from "../WhatsappService/ListWhatsAppsNumber";
import {
  createObject,
  findByContain,
  findObject,
  get,
  getSimple,
  set
} from "../../helpers/RedisClient";
import FindOrCreateTicketServiceBot from "../TicketServices/FindOrCreateTicketServiceBot";
import ShowTicketService from "../TicketServices/ShowTicketService";
import ShowQueuesByUser from "../UserServices/ShowQueuesByUser";
import ListWhatsappQueuesByUserQueue from "../UserServices/ListWhatsappQueuesByUserQueue";
import CreateContactService from "../ContactServices/CreateContactService";
import { number } from "yup";
import AssociateContatctQueue from "../ContactServices/AssociateContatctQueue";
import ContactQueue from "../../models/ContactQueues";

var lst: any[] = getWhatsappIds();

interface Session extends Client {
  id?: number;
}

const writeFileAsync = promisify(writeFile);

const verifyContact = async (
  msgContact: any,
  whatsAppOfficial?: any
): Promise<Contact> => {
  let contactData: any = {};
  if (msgContact) {
    contactData = {
      name: msgContact.name || msgContact.pushname || msgContact.id.user,
      number: msgContact.id.user,
      profilePicUrl: msgContact.getProfilePicUrl,
      isGroup: msgContact.isGroup
    };
  } else {
    contactData = {
      name: whatsAppOfficial?.name
        ? whatsAppOfficial.name
        : whatsAppOfficial.number,
      number: whatsAppOfficial.number,
      isGroup: false
    };
  }

  const contact = CreateOrUpdateContactService(contactData);

  return contact;
};

const verifyQuotedMessage = async (
  msg: any,
  _quotedMsg?: any
): Promise<Message | null> => {
  if (!msg.hasQuotedMsg) return null;

  // const wbotQuotedMsg = await msg.getQuotedMessage();

  const wbotQuotedMsg = _quotedMsg;

  const quotedMsg = await Message.findOne({
    where: { id: wbotQuotedMsg.id.id }
  });

  if (!quotedMsg) return null;

  return quotedMsg;
};

const verifyMediaMessage = async (
  msg: any,
  ticket: Ticket,
  contact: Contact,
  media: any,
  quotedMsg?: any
): Promise<Message | any> => {
  if (!media) {
    throw new Error("ERR_WAPP_DOWNLOAD_MEDIA");
  }
  let mediaAuthorized = true;
  let messageData = {
    id: msg.id.id,
    ticketId: ticket.id,
    contactId: msg.fromMe ? undefined : contact.id,
    body: msg.body || media.filename,
    fromMe: msg.fromMe,
    read: msg.fromMe,
    mediaUrl: media.filename,
    mediaType: media.mimetype.split("/")[0],
    quotedMsgId: quotedMsg,
    phoneNumberId: msg?.phoneNumberId,
    fromAgent: false
  };
  if(messageData.mediaType === 'video' || messageData.mediaType === 'audio' && getSettingValue('blockAudioVideoMedia')?.value === 'enabled'){
    mediaAuthorized = false;
  }
  if (msg?.fromMe) {
    messageData = { ...messageData, fromAgent: true };
  }

  if (!ticket?.phoneNumberId) {
    if (!media.filename) {
      const ext = media.mimetype.split("/")[1].split(";")[0];
      media.filename = `${new Date().getTime()}.${ext}`;
      messageData = {
        ...messageData,
        mediaUrl: media.filename,
        body: media.filename
      };
    }
    if(mediaAuthorized){
      try {
        await writeFileAsync(
          join(__dirname, "..", "..", "..", "..", "..", "public", media.filename),
          media.data,
          "base64"
          );
        } catch (err) {
          Sentry.captureException(err);
          logger.error(`There was an error: wbotMessageLitener.ts: ${err}`);
        }
    }
  }
  if(mediaAuthorized){
    await ticket.update({ lastMessage: msg.body || media.filename });
    const newMessage = await CreateMessageService({ messageData });
    return newMessage;
  }else{
    if (ticket.status !== "queueChoice") { 
    botSendMessage(
      ticket,
      `Atenção! Mensagem ignorada, tipo de mídia não suportado.`
      );
    }
      messageData.body = `Mensagem de *${messageData.mediaType}* ignorada, tipo de mídia não suportado.`;
      messageData.mediaUrl = '';
      await ticket.update({ lastMessage: `Mensagem de *${messageData.mediaType}* ignorada, tipo de mídia não suportado.`});
      const newMessage = await CreateMessageService({ messageData });
      console.log(`--------->>> Mensagem do tipo: ${messageData.mediaType}, ignorada!`)
  }
};

// const verifyMediaMessage = async (
//   msg: any,
//   ticket: Ticket,
//   contact: Contact,
//   media: any,
//   quotedMsg?: any
// ): Promise<Message | any> => {
//   // const quotedMsg = await verifyQuotedMessage(msg);

//   // const media = await msg.downloadMedia();

//   if (!media) {
//     throw new Error("ERR_WAPP_DOWNLOAD_MEDIA");
//   }

//   console.log(
//     "MEDIA.FILENAME: ",
//     media.fileName,
//     "  |  msg.fromMe: ",
//     msg.fromMe
//   );

//   if (!media.filename) {
//     console.log("No file name -----------------------------------------");

//     const ext = media.mimetype.split("/")[1].split(";")[0];
//     media.filename = `${new Date().getTime()}.${ext}`;
//   }

//   try {
//     // await writeFileAsync(
//     //   join(__dirname, "..", "..", "..", "public", media.filename),
//     //   media.data,
//     //   "base64"
//     // );

//     console.log("FROM wbotMessageListener.ts media.filename: ", media.filename);

//     await writeFileAsync(
//       join(__dirname, "..", "..", "..", "..", "..", "public", media.filename),
//       media.data,
//       "base64"
//     );
//   } catch (err) {
//     Sentry.captureException(err);
//     logger.error(`There was an error: wbotMessageLitener.ts: ${err}`);
//   }

//   const messageData = {
//     id: msg.id.id,
//     ticketId: ticket.id,
//     contactId: msg.fromMe ? undefined : contact.id,
//     body: msg.body || media.filename,
//     fromMe: msg.fromMe,
//     read: msg.fromMe,
//     mediaUrl: media.filename,
//     mediaType: media.mimetype.split("/")[0],
//     quotedMsgId: quotedMsg
//     // quotedMsgId: quotedMsg?.id
//   };

//   await ticket.update({ lastMessage: msg.body || media.filename });
//   const newMessage = await CreateMessageService({ messageData });

//   return newMessage;
// };

const verifyMessage = async (
  msg: any,
  ticket: Ticket,
  contact: Contact,
  quotedMsg?: any
) => {
  let messageData = {
    id: msg.id.id,
    ticketId: ticket.id,
    contactId: msg.fromMe ? undefined : contact.id,
    body: msg.body,
    fromMe: msg.fromMe,
    fromAgent: false,
    mediaType: msg.type,
    read: msg.fromMe,
    quotedMsgId: quotedMsg,
    phoneNumberId: msg?.phoneNumberId
  };

  if (msg?.fromMe) {
    const botInfo = await BotIsOnQueue("botqueue");

    if (botInfo.isOnQueue) {
      const ura: any = await get({ key: "ura" });

      if (ura && !ura.includes(JSON.stringify(msg?.body))) {
        messageData = { ...messageData, fromAgent: true };
      }
    } else if (msg?.body?.trim().length > 0 && !/\u200e/.test(msg.body[0])) {
      messageData = { ...messageData, fromAgent: true };
    }
  }

  await ticket.update({ lastMessage: msg.body });

  if (!msg?.fromMe && msg?.vCards && msg?.vCards?.length > 0) {
    if (msg.vCards.length == 1) {
      messageData = { ...messageData, body: msg.vCards[0] };
    } else {
      messageData = { ...messageData, body: JSON.stringify(msg.vCards) };
    }
  } 

  await CreateMessageService({ messageData });
};

const verifyQueue = async (
  wbot: Session,
  msg: WbotMessage,
  ticket: Ticket,
  contact: Contact
) => {
  const { queues, greetingMessage } = await ShowWhatsAppService(wbot.id!);

  let selectedOption = null;
  let choosenQueue = null;

  //Habilitar esse caso queira usar o bot
  const botInfo = await BotIsOnQueue("botqueue");
  // const botInfo = { isOnQueue: false, botQueueId: 0, userIdBot: 0 };

  if (botInfo.isOnQueue) {
    choosenQueue = await ShowQueueService(botInfo.botQueueId);
  } else if (queues.length === 1) {
    selectedOption = 1;
    choosenQueue = queues[+selectedOption - 1];
  } else {
    selectedOption = msg?.body;

    if (selectedOption && selectedOption.trim().length > 0) {
      //////////////// EXTRAIR APENAS O NÚMERO ///////////////////
      selectedOption = selectedOption.match(/\d+/);
      ///////////////////////////////////

      if (selectedOption) choosenQueue = queues[+selectedOption - 1];
    }
  }

  if (choosenQueue) {
    // Atualizando o status do ticket para mostrar notificação para o atendente da fila escolhida pelo usuário. De queueChoice para pending
    if (queues.length > 1 && !botInfo.isOnQueue) {
      await ticket.update({ status: "pending" });
    }
    //

    await UpdateTicketService({
      ticketData: { queueId: choosenQueue.id },
      ticketId: ticket.id
    });

    let botOptions = "";

    // O bot abre a mensagem na fila para atender o usuario
    if (botInfo.isOnQueue) {
      await UpdateTicketService({
        ticketData: { status: "open", userId: botInfo.userIdBot },
        ticketId: ticket.id
      });

      const data = await get({ key: "ura", parse: true });

      await createObject({
        whatsappId: `${ticket.whatsappId}`,
        contactId: `${ticket.contactId}`,
        identifier: "ura",
        value: data[1].id,
        history: `|${data[1].id}`
      });

      botSendMessage(ticket, data[1].value);
      return;
    }
    //

    await assingContactByQueue(ticket, choosenQueue.id);

    let whatsapp: any = await whatsappInfo(ticket?.whatsappId);

    const outService = await outOfService(whatsapp?.number);

    if (outService.length > 0) {
      const { type, msg: msgOutService } = outService[0];
      console.log(`${type} message ignored on queue`);
      botSendMessage(ticket, `\u200e${msgOutService}`);
      return;
    }

    let body = "";
    const io = getIO();
    if (botOptions.length > 0) {
      body = `\u200e${choosenQueue.greetingMessage}\n\n${botOptions}\n${final_message.msg}`;
    } else {
      body = `\u200e${choosenQueue.greetingMessage}`;
    }

    io.emit('notifyPeding', {data: {ticket, queue: choosenQueue}});

    sendWhatsAppMessageSocket(ticket, body);
  } else {
    //test del transfere o  atendimento se entrar na ura infinita
    const repet: any = await mostRepeatedPhrase(ticket.id);

    if (repet.occurrences > 5) {
      await UpdateTicketService({
        ticketData: { status: "pending", queueId: queues[0].id },
        ticketId: ticket.id
      });

      await SendWhatsAppMessage({
        body: `Seu atendimento foi transferido para um agente!
        `,
        ticket,
        number: `${contact.number}@c.us`
      });
    } else {
      let options = "";

      queues.forEach((queue, index) => {
        options += `*${index + 1}* - ${queue.name}\n`;
      });

      const body = `\u200e${greetingMessage}\n${options}`;

      const { phoneNumberId } = ticket;

      const debouncedSentMessage = debounce(
        async () => {
          sendWhatsAppMessageSocket(ticket, body);
        },
        phoneNumberId ? 0 : 3000,
        ticket.id
      );

      debouncedSentMessage();
    }
  }
};

const mediaTypeWhatsappOfficial = (mimetype: string): object => {
  const document = [
    "text/plain",
    "text/csv",
    "application/pdf",
    "application/vnd.ms-powerpoint",
    "application/msword",
    "application/vnd.ms-excel",
    "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    "application/vnd.openxmlformats-officedocument.presentationml.presentation",
    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
  ];

  const image = ["image/jpeg", "image/png"];

  const video = ["video/mp4", "video/3gp"];

  const sticker = ["image/webp"];

  const audio = [
    "audio/mp3",
    "audio/aac",
    "audio/mp4",
    "audio/mpeg",
    "audio/amr",
    "audio/ogg"
  ];

  const types = [
    { name: "document", values: document, mbMaxSize: 15 },
    { name: "image", values: image, mbMaxSize: 5 },
    { name: "video", values: video, mbMaxSize: 15 },
    { name: "sticker", values: sticker, mbMaxSize: 0.5 },
    { name: "audio", values: audio, mbMaxSize: 15 }
  ];

  for (let i in types) {
    if (types[i].values.includes(mimetype)) {
      return { type: types[i].name, mbMaxSize: types[i].mbMaxSize };
    }
  }

  return { type: null, mbsize: 0 };
};

const isValidMsg = (msg: any): boolean => {
  if (msg.from === "status@broadcast") return false;
  if (
    msg.type === "template" ||
    msg.type === "text" ||
    msg.type === "hsm" ||
    msg.type === "chat" ||
    msg.type === "audio" ||
    msg.type === "ptt" ||
    msg.type === "video" ||
    msg.type === "image" ||
    msg.type === "document" ||
    msg.type === "vcard" ||
    msg.type === "multi_vcard" ||
    msg.type === "sticker"
  )
    return true;
  return false;
};

const queuesOutBot = async (wbot: Session, botId: string | number) => {
  const { queues, greetingMessage } = await ShowWhatsAppService(wbot.id!);

  const indexQueue = queues.findIndex(q => q.id == botId);

  if (indexQueue != -1) {
    queues.splice(indexQueue, 1);
  }

  return { queues, greetingMessage };
};

const transferTicket = async (
  queueName: any,
  wbot: any,
  ticket: Ticket,
  sendGreetingMessage?: boolean
) => {
  const botInfo = await BotIsOnQueue("botqueue");
  const io = getIO();

  const queuesWhatsGreetingMessage = await queuesOutBot(
    wbot,
    botInfo.botQueueId
  );

  let queue: any;

  const queues = queuesWhatsGreetingMessage.queues;

  if (typeof queueName == "string") {
    queue = queues.find(
      (q: any) => q?.name?.toLowerCase() == queueName.trim().toLowerCase()
    );
  } else if (typeof queueName == "number") {
    queue = queues[queueName];
  }

  if (queue) {
    await assingContactByQueue(ticket, queue.id);

    await botTransferTicket(queue, ticket, sendGreetingMessage);
  }
  io.emit("notifyPeding", { data: { ticket, queue } });
};

const botTransferTicket = async (
  queues: Queue,
  ticket: Ticket,
  sendGreetingMessage?: boolean
) => {
  await ticket.update({ userId: null });

  await UpdateTicketService({
    ticketData: { status: "pending", queueId: queues.id },
    ticketId: ticket.id
  });

  if (sendGreetingMessage && queues?.greetingMessage?.length > 0) {
    botSendMessage(ticket, queues.greetingMessage);
  }
};

const botTransferTicketToUser = async (
  userId: number,
  ticket: Ticket,
  queueId?: number | undefined
) => {
  console.log("USER ID: ", userId);

  // await ticket.update({ userId: userId });

  await UpdateTicketService({
    ticketData: { status: "open", userId, queueId },
    ticketId: ticket.id
  });
};

const botSendMessage = (ticket: Ticket, msg: string) => {
  const { phoneNumberId } = ticket;

  const debouncedSentMessage = debounce(
    async () => {
      await SendWhatsAppMessage({ body: msg, ticket });
    },
    phoneNumberId ? 0 : 3000,
    ticket.id
  );

  debouncedSentMessage();
};

const _clear_lst = () => {
  console.log("THE lst.length: ", lst.length);

  if (lst.length <= 199) return;

  const chunk: any = Math.floor(lst.length / 2);

  lst = lst.slice(chunk, chunk + lst.length);

  let whatsappIdsSplited = lst.map(e => `${e.id}`).toString();

  setWhatsappId(whatsappIdsSplited, true);
};

const handleMessage = async (
  msg: any,
  wbot: any,
  whatsAppOfficial?: any
): Promise<void> => {
  if (!whatsAppOfficial) {
    _clear_lst();

    let index = lst.findIndex((x: any) => x.id == msg.id.id);

    console.log("INDEX: ", index);

    if (index == -1) {
      lst.push({ id: msg.id.id });

      setWhatsappId(msg.id.id);
    } else {
      console.log("IGNORED ID: ", msg.id.id);

      return;
    }
  }

  if (!isValidMsg(msg)) {
    return;
  }

  try {
    let msgContact: any = wbot.msgContact;
    // let groupContact: Contact | undefined;

    if (msg.fromMe) {
      // messages sent automatically by wbot have a special character in front of it
      // if so, this message was already been stored in database;
      if (/\u200e/.test(msg.body[0])) return;

      // media messages sent from me from cell phone, first comes with "hasMedia = false" and type = "image/ptt/etc"
      // in this case, return and let this message be handled by "media_uploaded" event, when it will have "hasMedia = true"

      if (
        !msg.hasMedia &&
        msg.type !== "chat" &&
        msg.type !== "vcard" &&
        msg.type !== "multi_vcard"
      )
        return;
    } else {
      console.log(`\n <<<<<<<<<< RECEIVING MESSAGE: 
      Parcial msg and msgContact info: 
      msgContact.name: ${msgContact.name}
      msgContact.pushname: ${msgContact.pushname}
      msgContact.shortName: ${msgContact.shortName}
      msg.id.fromMe: ${msg.id.fromMe}
      msg.id.id: ${msg.id.id}
      msg.body: ${msg.body}
      msg.type: ${msg.type}
      msg.from: ${msg.from}
      msg.to: ${msg.to}\n`);
    }
    const chat = wbot.chat;

    // if(chat.isGroup){

    //   console.log('This message is from a Group and will be ignored!')
    //   return
    // }

    // console.log('----------> chat: ', JSON.parse(JSON.stringify(chat)))

    // if (chat.isGroup) {
    //   let msgGroupContact;

    //   if (msg.fromMe) {
    //     msgGroupContact = await wbot.getContactById(msg.to);
    //   } else {
    //     msgGroupContact = await wbot.getContactById(msg.from);
    //   }

    //   groupContact = await verifyContact(msgGroupContact);
    // }

    const whatsapp = await ShowWhatsAppService(wbot.id!);

    const unreadMessages = msg.fromMe ? 0 : chat.unreadCount;

    const contact = !whatsAppOfficial
      ? await verifyContact(msgContact)
      : await verifyContact(null, {
          number: wbot.msgContact.number,
          name: wbot.msgContact.name
        });

    if (
      unreadMessages === 0 &&
      whatsapp.farewellMessage &&
      whatsapp.farewellMessage === msg.body
    )
      return;

    let ticket;

    const _botInfo = await BotIsOnQueue("botqueue");

    if (_botInfo.isOnQueue) {
      let ticket_obj: any = await FindOrCreateTicketServiceBot(
        contact,
        wbot.id!,
        unreadMessages
        // groupContact
      );

      ticket = ticket_obj.ticket;

      if (ticket_obj.created) {
        let queue = await ShowQueueService(_botInfo.botQueueId);

        await UpdateTicketService({
          ticketData: { queueId: queue.id },
          ticketId: ticket.id
        });

        ticket = await ShowTicketService(ticket.id);
      }
    } else {
      ticket = await FindOrCreateTicketService(
        contact,
        wbot.id!,
        unreadMessages
        // groupContact
      );
    } 

    if (getSettingValue("oneContactChatWithManyWhats")?.value == "disabled") {
      // Para responder para o cliente pelo mesmo whatsapp que ele enviou a mensagen
      if (wbot.id != ticket.whatsappId) {
        try {
          await ticket.update({ whatsappId: wbot.id });
        } catch (error: any) {
          console.error(
            "===> Error on wbotMessageListener.ts into handleMessage fuction file: \n",
            error
          );
          throw new AppError(error.message);
        }
      }
      //
    }

    if (msg.hasMedia) {
      await verifyMediaMessage(
        msg,
        ticket,
        contact,
        wbot.media,
        wbot.quotedMsg
      );
    } else {
      // console.log('>>>>>>> msg.fromMe: ',msg.fromMe )
      await verifyMessage(msg, ticket, contact, wbot.quotedMsg);
    }

    if (
      !ticket.queue &&
      !chat.isGroup &&
      !msg.fromMe &&
      !ticket.userId &&
      whatsapp.queues.length >= 1
    ) {
      await verifyQueue(wbot, msg, ticket, contact);
    }

    if (msg.type === "vcard" || msg.type === "multi_vcard") {
      await vcard(msg);
    }

    const botInfo = await BotIsOnQueue("botqueue");

    // Transfer to agent
    // O bot interage com o cliente e encaminha o atendimento para fila de atendende quando o usuário escolhe a opção falar com atendente
    if (
      !msg.fromMe &&
      ((ticket.status == "open" &&
        botInfo &&
        ticket.userId == +botInfo.userIdBot) ||
        ticket.status == "pending" ||
        ticket.status == "queueChoice")
    ) {
      const filteredUsers = await findByContain("user:*", "name", msg?.body);

      if (filteredUsers && filteredUsers.length > 0) {
        if (botInfo.isOnQueue) {
          transferTicket(filteredUsers[0].name, wbot, ticket, true);
          return;
        }

        const whatsappQueues = await ListWhatsappQueuesByUserQueue(
          +filteredUsers[0].id
        );

        const obj: any = whatsappQueues.find(
          (ob: any) => ob.whatsappId == wbot.id
        );

        if (obj) {
          await botTransferTicketToUser(
            +filteredUsers[0].id,
            ticket,
            +obj.queueId
          );

          await botSendMessage(
            ticket,
            `Você foi transferido para falar com o agente ${filteredUsers[0].name}, aguarde.`
          );
        }

        return;
      }
    }
    //

    if (
      botInfo.isOnQueue &&
      !msg.fromMe &&
      ticket.userId == botInfo.userIdBot
    ) {
      const repet: any = await mostRepeatedPhrase(ticket.id);

      console.log("repet.occurrences: ", repet.occurrences);

      if (repet.occurrences > 10) {
        await transferTicket(0, wbot, ticket);

        await SendWhatsAppMessage({
          body: `Seu atendimento foi transferido para um agente!\n\nPara voltar ao menu principal digite *0*
        `,
          ticket,
          number: `${contact.number}@c.us`
        });
      } else {
        console.log("MSG body: ", msg.body);

        if (msg.type != "chat") {
          botSendMessage(
            ticket,
            `Desculpe, nao compreendi!\nEnvie apenas texto quando estiver interagindo com o bot!\n _Digite *0* para voltar ao menu principal._`
          );
          return;
        }

        if (msg.type == "chat" && String(msg.body).length > 120) {
          botSendMessage(
            ticket,
            `Desculpe, nao compreendi!\nTexto acima de 120 caracteres!\n _Digite *0* para voltar ao menu principal._`
          );
          return;
        }

        const menuMsg: any = await menu(msg.body, wbot.id, contact.id);

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

        await botSendMessage(ticket, menuMsg.value);

        if (
          menuMsg?.transferToQueue &&
          menuMsg.transferToQueue.trim().length > 0
        ) {
          transferTicket(menuMsg.transferToQueue.trim(), wbot, ticket);
        }
      }

      return;
    } else if (
      !msg.fromMe &&
      msg.body == "0" &&
      ticket.status == "pending" &&
      ticket.queueId
    ) {
      if (botInfo.isOnQueue) {
        let choosenQueue = await ShowQueueService(botInfo.botQueueId);

        await UpdateTicketService({
          ticketData: {
            status: "open",
            userId: botInfo.userIdBot,
            queueId: choosenQueue.id
          },
          ticketId: ticket.id
        });
        const menuMsg: any = await menu(msg.body, wbot.id, contact.id);
        await botSendMessage(ticket, menuMsg.value);
      }

      return;
    } else if (
      !msg.fromMe &&
      msg.body == "#" &&
      ticket.status == "pending" &&
      ticket.queueId &&
      botInfo.isOnQueue
    ) {
      let choosenQueue = await ShowQueueService(botInfo.botQueueId);

      await UpdateTicketService({
        ticketData: {
          status: "open",
          userId: botInfo.userIdBot,
          queueId: choosenQueue.id
        },
        ticketId: ticket.id
      });

      const menuMsg: any = await menu(msg.body, wbot.id, contact.id);

      await botSendMessage(ticket, menuMsg.value);

      // return;
    }

    if (msg && !msg.fromMe && ticket.status == "pending") {
      await setMessageAsRead(ticket);
    }

    let ticketHasQueue = false;

    if (ticket?.queueId) {
      ticketHasQueue = true;
    }

    if (ticketHasQueue && ticket.status != "open") {
      let whatsapp: any = await whatsappInfo(ticket?.whatsappId);

      const outService = await outOfService(whatsapp.number);

      if (outService.length > 0) {
        const { type, msg: msgOutService } = outService[0];

        if (msg.fromMe && msgOutService == msg.body) {
          console.log(`${type} message ignored`);
          return;
        }

        botSendMessage(ticket, `\u200e${msgOutService}`);
        return;
      }
    }
  } catch (err) {
    Sentry.captureException(err);
    console.log("Error handling whatsapp message: Err: ", err);
    logger.error(`Error handling whatsapp message: Err: ${err}`);
  }
};

const menu = async (userTyped: string, whatsappId: any, contactId: any) => {
  let lastId = await findObject(whatsappId, contactId, "ura");
  const data: any = await get({ key: "ura", parse: true });

  console.log("lastId[0]: ", lastId[0]);

  if (!lastId[0]) {
    await createObject({
      whatsappId,
      contactId,
      identifier: "ura",
      value: data[1].id,
      history: `|${data[1].id}`
    });
  }

  lastId = await findObject(whatsappId, contactId, "ura");
  console.log("LAST ID: ", lastId);
  let option: any;

  if (
    lastId &&
    (lastId.length == 4 || lastId.length == 5) &&
    lastId[3] &&
    lastId[3].trim().length > 0
  ) {
    option = data.find(
      (o: any) =>
        o.idmaster == lastId[3] &&
        o.value.toLowerCase() == userTyped.toLowerCase()
    );

    if (!option && userTyped != "0" && userTyped != "#") {
      if (!existSubMenu()) {
        const response = await mainOptionsMenu(userTyped);
        if (response) return response;
        else {
          let uraOptionSelected = await findObject(
            whatsappId,
            contactId,
            "ura"
          );

          uraOptionSelected = uraOptionSelected[4].split("|");

          if (uraOptionSelected.length == 1) {
            await createObject({
              whatsappId,
              contactId,
              identifier: "ura",
              value: data[1].id,
              history: `|${data[1].id}`
            });

            return data[1];
          } else if (uraOptionSelected.length > 1) {
            const id = uraOptionSelected[uraOptionSelected.length - 1];

            console.log(" ID FROM THE MENU/SUBMENU: ", id);

            const history = await historyUra(whatsappId, contactId, id);

            await createObject({
              whatsappId,
              contactId,
              identifier: "ura",
              value: id,
              history
            });

            let response: any = data.find((o: any) => o.id == id);

            return response;
          }
        }
      }
    }
    //

    if (option) {
      let response: any = data.find((o: any) => o.idmaster == option.id);
      // console.log(" RESPONSE OPTION: ", response, " | OPTION: ", option);

      let history: any = await historyUra(whatsappId, contactId, response.id);

      await createObject({
        whatsappId,
        contactId,
        identifier: "ura",
        value: response.id,
        history
      });

      return response;
    } else if (userTyped == "0") {
      await createObject({
        whatsappId,
        contactId,
        identifier: "ura",
        value: data[1].id,
        history: `|${data[1].id}`
      });

      return data[1];
    } else if (userTyped == "#") {
      return await backUra(whatsappId, contactId, data);
    }
  }

  function existSubMenu() {
    let existSubMenu = data.find((o: any) => o.idmaster == lastId[3]);

    if (existSubMenu) true;

    return false;
  }

  async function mainOptionsMenu(userTyped: any) {
    let currentMenu = await findObject(whatsappId, contactId, "ura");

    const menuValues = data
      .filter((m: any) => m.idmaster == currentMenu[3])
      .map((m: any) => m.value);

    let menuOption = data.find(
      (o: any) =>
        o.value.toLowerCase() == userTyped.toLowerCase() &&
        menuValues.includes(userTyped.toLowerCase())
    );

    if (menuOption) {
      let response = data.find((o: any) => o.idmaster == menuOption.id);
      if (response) {
        let history = await historyUra(whatsappId, contactId, response.id);

        await createObject({
          whatsappId,
          contactId,
          identifier: "ura",
          value: response.id,
          history
        });

        return response;
      }
    }
  }
};

const handleMsgAck = async (
  msg_id: any,
  ack: any,
  whatsAppOfficial?: boolean
) => {
  if (whatsAppOfficial) {
    if (ack == "sent") ack = 1;
    else if (ack == "delivered") ack = 2;
    else if (ack == "read") ack = 3;
  } else await new Promise(r => setTimeout(r, 4000));

  const io = getIO();

  try {
    const messageToUpdate = await Message.findByPk(msg_id, {
      include: [
        "contact",
        {
          model: Message,
          as: "quotedMsg",
          include: ["contact"]
        }
      ]
    });
    if (!messageToUpdate) {
      console.log(
        `Not found the MESSAGE ID ${msg_id}to change the ACK into the Message table`
      );
      return;
    }

    console.log("messageToUpdate.ack: ", messageToUpdate.ack, " | ack: ", ack);

    if (messageToUpdate.ack > ack) return;

    await messageToUpdate.update({ ack });

    io.to(messageToUpdate.ticketId.toString()).emit("appMessage", {
      action: "update",
      message: messageToUpdate
    });
  } catch (err) {
    Sentry.captureException(err);
    logger.error(`Error handling message ack. Err: ${err}`);
  }
};

const wbotMessageListener = (wbot: Session): void => {
  wbot.on("message_create", async msg => {
    handleMessage(msg, wbot);
  });

  wbot.on("media_uploaded", async msg => {
    handleMessage(msg, wbot);
  });

  wbot.on("message_ack", async (msg, ack) => {
    handleMsgAck(msg, ack);
  });
};

const outOfService = async (number: string) => {
  // MESSAGE TO HOLIDAY
  const holiday: any = await isHoliday(number);

  let objs: any = [];

  if (holiday && holiday.set) {
    objs.push({ type: "holiday", msg: holiday.msg });
  }

  // MESSAGE TO SATURDAY BUSINESS TIME
  const businessTimeSaturday = await isOutBusinessTimeSaturday(number);

  if (businessTimeSaturday && businessTimeSaturday.set) {
    objs.push({ type: "saturdayBusinessTime", msg: businessTimeSaturday.msg });
  }

  // MESSAGES TO SATURDAY OR SUNDAY
  const weekend: any = await isWeekend(number);

  if (weekend && weekend.set) {
    objs.push({ type: "weekend", msg: weekend.msg });
  }

  // MESSAGE TO BUSINESS TIME
  const businessTime = await isOutBusinessTime(number);

  if (businessTime && businessTime.set) {
    objs.push({ type: "businessTime", msg: businessTime.msg });
  }

  return objs;
};

export {
  wbotMessageListener,
  handleMessage,
  handleMsgAck,
  lst,
  verifyMessage,
  verifyMediaMessage,
  verifyContact,
  isValidMsg,
  mediaTypeWhatsappOfficial,
  botSendMessage
};
async function assingContactByQueue(ticket: Ticket, queueId: any) {
  if (getSettingValue("contactByqueues")?.value == "enabled") {
    const contact = await Contact.findByPk(ticket.contact.id);

    if (contact) {
      let queueIds = await ContactQueue.findAll({
        where: { contactId: contact.id }
      });
      queueIds = queueIds.map((q: any) => q.queueId);

      await AssociateContatctQueue(contact, [...queueIds, queueId]);
    }
  }
}

async function vcard(msg: any) {
  let array: any[] = [];
  let contact: any;
  let obj: any[] = [];

  try {
    const multi_vcard = msg?.vCards?.length === 0 ? false : true;

    if (multi_vcard) {
      array = msg?.vCards;
      contact = [];
    } else {
      array = msg.body.split("\n");
      contact = "";
    }

    for (let index = 0; index < array.length; index++) {
      const v = array[index];
      const values = v.split(":");
      for (let ind = 0; ind < values.length; ind++) {
        if (values[ind].indexOf("+") !== -1) {
          obj.push({ number: values[ind] });
        }
        if (values[ind].indexOf("FN") !== -1) {
          if (multi_vcard)
            contact.push({ name: values[ind + 1].split("\n")[0] });
          else contact = values[ind + 1];
        }
      }
    }

    for (const i in obj) {
      let data: any = {};

      if (multi_vcard) {
        data = {
          name: contact[i].name,
          number: obj[i].number.replace(/\D/g, "")
        };
      } else {
        data = {
          name: contact,
          number: obj[i].number.replace(/\D/g, "")
        };
      }

      const cont = await CreateContactService(data);
    }
  } catch (error) {
    console.log(error);
  }
}

async function backUra(whatsappId: any, contactId: any, data: any) {
  let uraOptionSelected = await findObject(whatsappId, contactId, "ura");

  uraOptionSelected = uraOptionSelected[4].split("|").filter(Boolean);

  let id = uraOptionSelected[0];

  let history = `|${uraOptionSelected[0]}`;

  if (uraOptionSelected.length > 1) {
    const idRemove = uraOptionSelected[uraOptionSelected.length - 1];

    history = await historyUra(whatsappId, contactId, idRemove, true);

    const lstIds = history.split("|").filter(Boolean);

    id = lstIds[lstIds.length - 1];
  }

  await createObject({
    whatsappId,
    contactId,
    identifier: "ura",
    value: id,
    history
  });

  let response: any = data.find((o: any) => o.id == id);

  return response;
}

async function historyUra(
  whatsappId: any,
  contactId: any,
  id: any,
  remove?: boolean
) {
  let uraOptionSelected = await findObject(whatsappId, contactId, "ura");
  let history = "";

  console.log("SELECED OPTION uraOptionSelected: ", uraOptionSelected);

  if (remove) {
    return uraOptionSelected[4]?.replace(`|${id}`, "");
  }

  if (uraOptionSelected && uraOptionSelected.length == 5) {
    if (!uraOptionSelected[4]?.includes(`${id}`))
      history += `${uraOptionSelected[4]}|${id}`;
    else history = `${uraOptionSelected[4]}`;
  } else {
    history = `|${id}`;
  }
  return history;
}

async function whatsappInfo(whatsappId: string | number) {
  return await Whatsapp.findByPk(whatsappId);
}
