const path = require("path");
require("dotenv").config({
  path: path.resolve(
    process.cwd(),
    `.env.${process.env.NODE_ENV || "development"}`
  ),
});

const { io } = require("../../server");
const { admin } = require("../firebase/firebase-handler");
const {
  default: CdkJobExport,
  CdkSelectedCustomer,
} = require("../cdk/cdk-job-export");
const CdkGetMakes = require("../cdk/cdk-get-makes").default;
const CdkCalculateAllocations =
  require("../cdk/cdk-calculate-allocations").default;
const { isArray } = require("lodash");
const logger = require("../utils/logger");
const {
  default: PbsExportJob,
  PbsSelectedCustomer,
} = require("../accounting/pbs/pbs-job-export");

const {
  PbsCalculateAllocationsAp,
  PbsExportAp,
} = require("../accounting/pbs/pbs-ap-allocations");

io.use(function (socket, next) {
  try {
    if (socket.handshake.auth.token) {
      admin
        .auth()
        .verifyIdToken(socket.handshake.auth.token)
        .then((user) => {
          socket.user = user;
          next();
        })
        .catch((error) => {
          next(new Error("Authentication error", JSON.stringify(error)));
        });
    } else {
      next(new Error("Authentication error - no authorization token."));
    }
  } catch (error) {
    console.log("Uncaught connection error:::", error);
    logger.log("websocket-connection-error", "error", null, null, {
      token: socket.handshake.auth.token,
      ...error,
    });
    next(new Error(`Authentication error ${error}`));
  }
});

io.on("connection", (socket) => {
  socket.log_level = "TRACE";
  createLogEvent(socket, "DEBUG", `Connected and Authenticated.`);

  socket.on("set-log-level", (level) => {
    socket.log_level = level;
    socket.emit("log-event", {
      timestamp: new Date(),
      level: "INFO",
      message: `Updated log level to ${level}`,
    });
  });

  ///CDK
  socket.on("cdk-export-job", (jobid) => {
    CdkJobExport(socket, jobid);
  });
  socket.on("cdk-selected-customer", (selectedCustomerId) => {
    createLogEvent(
      socket,
      "DEBUG",
      `User selected customer ID  ${selectedCustomerId}`
    );
    socket.selectedCustomerId = selectedCustomerId;
    CdkSelectedCustomer(socket, selectedCustomerId);
  });

  socket.on("cdk-get-makes", async (cdk_dealerid, callback) => {
    try {
      const makes = await CdkGetMakes(socket, cdk_dealerid);
      callback(makes);
    } catch (error) {
      createLogEvent(
        socket,
        "ERROR",
        `Error in cdk-get-makes WS call. ${JSON.stringify(error, null, 2)}`
      );
    }
  });

  socket.on("cdk-calculate-allocations", async (jobid, callback) => {
    const allocations = await CdkCalculateAllocations(socket, jobid);
    createLogEvent(socket, "DEBUG", `Allocations calculated.`);
    createLogEvent(
      socket,
      "TRACE",
      `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`
    );

    callback(allocations);
  });
  //END CDK

  //PBS AR
  socket.on("pbs-calculate-allocations", async (jobid, callback) => {
    const allocations = await CdkCalculateAllocations(socket, jobid);
    createLogEvent(socket, "DEBUG", `Allocations calculated.`);
    createLogEvent(
      socket,
      "TRACE",
      `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`
    );

    callback(allocations);
  });
  socket.on("pbs-export-job", (jobid) => {
    PbsExportJob(socket, jobid);
  });
  socket.on("pbs-selected-customer", (selectedCustomerId) => {
    createLogEvent(
      socket,
      "DEBUG",
      `User selected customer ID  ${selectedCustomerId}`
    );
    socket.selectedCustomerId = selectedCustomerId;
    PbsSelectedCustomer(socket, selectedCustomerId);
  });
  //End PBS AR

  //PBS AP
  socket.on("pbs-calculate-allocations-ap", async (billids, callback) => {
    const allocations = await PbsCalculateAllocationsAp(socket, billids);
    createLogEvent(socket, "DEBUG", `AP Allocations calculated.`);
    createLogEvent(
      socket,
      "TRACE",
      `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`
    );
    socket.apAllocations = allocations;
    callback(allocations);
  });

  socket.on("pbs-export-ap", ({ billids, txEnvelope }) => {
    socket.txEnvelope = txEnvelope;
    PbsExportAp(socket, { billids, txEnvelope });
  });

  //END PBS AP

  socket.on("disconnect", () => {
    createLogEvent(socket, "DEBUG", `User disconnected.`);
  });
});

function createLogEvent(socket, level, message) {
  if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy(level)) {
    console.log(
      `[WS LOG EVENT] ${level} - ${new Date()} - ${socket.user.email} - ${
        socket.id
      } - ${message}`
    );
    socket.emit("log-event", {
      timestamp: new Date(),
      level,
      message,
    });

    logger.log("ws-log-event", level, socket.user.email, socket.recordid, {
      wsmessage: message,
    });

    if (socket.logEvents && isArray(socket.logEvents)) {
      socket.logEvents.push({
        timestamp: new Date(),
        level,
        message,
      });
    }
    // if (level === "ERROR") {
    //   throw new Error(message);
    // }
  }
}

function createJsonEvent(socket, level, message, json) {
  if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy(level)) {
    console.log(
      `[WS LOG EVENT] ${level} - ${new Date()} - ${socket.user.email} - ${
        socket.id
      } - ${message}`
    );
    socket.emit("log-event", {
      timestamp: new Date(),
      level,
      message,
    });
  }
  logger.log("ws-log-event-json", level, socket.user.email, socket.recordid, {
    wsmessage: message,
    json,
  });

  if (socket.logEvents && isArray(socket.logEvents)) {
    socket.logEvents.push({
      timestamp: new Date(),
      level,
      message,
    });
  }
  // if (level === "ERROR") {
  //   throw new Error(message);
  // }
}

function createXmlEvent(socket, xml, message, isError = false) {
  if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy("TRACE")) {
    socket.emit("log-event", {
      timestamp: new Date(),
      level: isError ? "ERROR" : "TRACE",
      message: `${message}: ${xml}`,
    });
  }

  logger.log(
    isError ? "ws-log-event-xml-error" : "ws-log-event-xml",
    isError ? "ERROR" : "TRACE",
    socket.user.email,
    socket.recordid,
    {
      wsmessage: message,
      xml,
    }
  );

  if (socket.logEvents && isArray(socket.logEvents)) {
    socket.logEvents.push({
      timestamp: new Date(),
      level: isError ? "ERROR" : "TRACE",
      message,
      xml,
    });
  }
}

function LogLevelHierarchy(level) {
  switch (level) {
    case "XML":
      return 5;
    case "TRACE":
      return 5;
    case "DEBUG":
      return 4;
    case "INFO":
      return 3;
    case "WARNING":
      return 2;
    case "ERROR":
      return 1;
    default:
      return 3;
  }
}

exports.createLogEvent = createLogEvent;
exports.createXmlEvent = createXmlEvent;
exports.createJsonEvent = createJsonEvent;
