import { Server as SocketIO } from "socket.io";
import { Server } from "http";
import AppError from "../errors/AppError";
import { logger } from "../utils/logger";

import { createAdapter } from "@socket.io/redis-adapter";
import { createClient } from 'redis';

import { v4 as uuidv4 } from "uuid";
import ListUserParamiterService from "../services/UserServices/ListUserParamiterService";
import {
  addHours,
  addMinutes,
  addSeconds,
  intervalToDuration,
  add
} from "date-fns";

let io: SocketIO;

//test del
import createOrUpdateOnlineUserService from "../services/UserServices/CreateOrUpdateOnlineUserService";
import { splitDateTime } from "../helpers/SplitDateTime";
import format from "date-fns/format";
import ptBR from "date-fns/locale/pt-BR";
import ListUserOnlineOffline from "../services/UserServices/ListUsersOnlineOfflineService";
import {
  handleMessage,
  handleMsgAck
} from "../services/WbotServices/wbotMessageListener";
import { join } from "path";
import Whatsapp from "../models/Whatsapp";
import { get } from "../helpers/RedisClient"

let count: number = 0;
let listOnline: any[] = [];
let listOnlineAux: any[] = [];
let countOnline: number = 0;
let obj: any = { listOnline: [], uuid: null, listOnlineAux: [] };

let lstOnline: any[] = [];
let lstOnlineAux: any[] = [];
let lstTry: any[] = [];

let dateTime = splitDateTime(
  new Date(format(new Date(), "yyyy-MM-dd HH:mm:ss", { locale: ptBR }))
);


const pubClient = createClient({ url: 'redis://172.31.187.29:6379' });
const subClient = pubClient.duplicate();


pubClient.connect().catch(console.error);
subClient.connect().catch(console.error);


export const initIO = (httpServer: Server): SocketIO => {

  io = new SocketIO(httpServer, {
    path: "/api-ws/socketio",
    wsEngine: require("eiows").Server,
    cors: {
      origin: "http://172.31.187.38:3331",
      methods: ["GET", "POST"],
      allowedHeaders: ["my-custom-header"],
      credentials: true
    },
    //  transports:['websocket', 'polling'],
    maxHttpBufferSize: 1e8,
    pingInterval: 25000,
    pingTimeout: 60000,
    adapter: createAdapter(pubClient, subClient)
  });


  io.on("connection", socket => {
    logger.info("Client Connected");

    socket.on("joinWhatsSession", (whatsappId: string) => {
      logger.info(`A client joined a joinWhatsSession channel: ${whatsappId}`);
      socket.join(`session_${whatsappId}`);
    });

    socket.on("message_from_client", () => {
      socket.emit("message_from_server", "Sent an event from the server!");
    });

    socket.on("message_create", async (data: any) => {
      handleMessage(data.msg, data);
    });

    socket.on("media_uploaded", async (data: any) => {
      handleMessage(data.msg, data);
    });

    socket.on("message_ack", async (data: any) => {
      handleMsgAck(data.id, data.ack);
    });

    socket.on("campaign_message_sent", async (data: any) => {
      // console.log("campaign_message_sent: ", data);
      const io = getIO();

      let campaign = {};

      if (data?.read) {
        campaign = {
          id: data.id,
          read: data.read
        };
      } else if (data?.sent) {
        campaign = {
          id: data.id,
          sent: data.sent
        };
      } else if (data?.status) {
        campaign = {
          id: data.id,
          status: data.status
        };
      }

      io.emit("campaign", {
        action: "update",
        campaign
      });
    });

    socket.on("online", (userId: any) => {
      // console.log('userId: ', userId)

      return
      obj.uuid = uuidv4();

      if (userId.logoutUserId) {
        let index = lstOnline.findIndex(
          (x: any) => x.id == userId.logoutUserId
        );

        if (index != -1) {
          lstOnline.splice(index, 1);
        }

        index = lstOnlineAux.findIndex((x: any) => x.id == userId.logoutUserId);

        if (index != -1) {
          lstOnlineAux.splice(index, 1);
        }

        return;
      }

      if (lstOnline.length == 0) {
        const index = listOnlineAux.findIndex((e: any) => e.id == userId);

        if (index == -1) {
          listOnlineAux.push({ id: userId });
        } else {
          return;
        }

        lstOnline.push({ id: userId, status: "online", try: 0 });

        lstOnlineAux.push({ id: userId });
        console.log(" 1 - PUSHED NEW USER ID 1: ", userId);

        obj.listOnline = lstOnline;
      } else {
        const indexAux = lstOnlineAux.findIndex((e: any) => e.id == userId);

        if (indexAux == -1) {
          lstOnlineAux.push({ id: userId });
        }

        const index = lstOnline.findIndex((e: any) => e.id == userId);

        if (index == -1) {
          lstOnline.push({ id: userId, status: "online", try: 0 });

          console.log("  2 - PUSHED NEW USER ID: ", userId);

          obj.listOnline = lstOnline;
        } else {
          if (countOnline > lstOnline.length - 1) {
            lstOnline.forEach((x: any) => {
              if (lstOnlineAux.map((e: any) => e.id).includes(x.id)) {
                x.try = 0;
                x.status = "online";
              }
            });

            var difference = lstOnline.filter(
              (x: any) => !lstOnlineAux.map((e: any) => e.id).includes(x.id)
            );

            if (difference.length > 0) {
              difference.forEach(e => {
                e.try += 1;

                if (e.try > 1) {
                  e.status = "waiting...";
                }

                if (e.try > 3) {
                  const index = lstOnline.findIndex((x: any) => x.id == e.id);

                  if (index != -1) {
                    lstOnline.splice(index, 1);
                  }
                }
              });
            }
            obj.listOnline = lstOnline;
            obj.listOnlineAux = lstOnlineAux;

            lstOnlineAux = [];
            listOnlineAux = [];

            countOnline = -1;
          }

          countOnline++;
        }
      }

      exports.ob = obj;
    });

    socket.on("joinChatBox", (ticketId: string) => {
      logger.info("A client joined a ticket channel");
      socket.join(ticketId);
    });

    socket.on("joinChatBox", (ticketId: string) => {
      logger.info("A client joined a ticket channel");
      socket.join(ticketId);
    });

    socket.on("joinNotification", () => {
      logger.info("A client joined notification channel");
      socket.join("notification");
    });

    socket.on("joinTickets", (status: string) => {
      logger.info(`A client joined to ${status} tickets channel.`);
      socket.join(status);
    });

    socket.on("disconnect", (data: any) => {
      logger.info(`Client disconnected socket: ${data}`);
    });

    socket.on("disconnecting", async () => {
      console.log("socket.rooms: ", socket.rooms); // the Set contains at least the socket ID

      let rooms = socket.rooms;

      console.log("rooms: ", rooms, " | rooms.size: ", rooms.size);

      if (rooms && rooms.size == 1) return;
      if (rooms && rooms.size == 2 && ![...rooms][1].startsWith("session_"))
        return;

      let whatsappIds = await get({
        key: "whatsapp:*",
        parse: true
      });

      // let whatsappIds: any = await Whatsapp.findAll({
      //   attributes: ["id"],
      //   raw: true
      // });

      if (whatsappIds && whatsappIds.length > 0) {
        // whatsappIds = whatsappIds.map((e: any) => `${e.id}`);

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

        if (
          rooms &&
          rooms.size == 2 &&
          [...rooms][1].startsWith("session_") &&
          whatsappIds.includes([...rooms][1].replace("session_", ""))
        ) {
          console.log([...rooms][1]);

          let whatsappId = [...rooms][1].replace("session_", "");

          const whatsapp = await Whatsapp.findByPk(whatsappId, {});

          if (whatsapp) {
            await whatsapp.update({ status: "OPENING" });

            io.emit("whatsappSession", {
              action: "update",
              session: whatsapp
            });
          }
        }
      }
    });
  });
  return io;
};

export const getIO = (): SocketIO => {
  if (!io) {
    throw new AppError("Socket IO not initialized");
  }
  return io;
};

function writeFileAsync(arg0: any, data: any, arg2: string) {
  throw new Error("Function not implemented.");
}

// exports.listOnlineUsers = listUserId
// exports.listUserId
