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

const queries = require("../graphql-client/queries");
const CdkBase = require("../web-sockets/web-socket");

const Dinero = require("dinero.js");
const _ = require("lodash");
const { DiscountNotAlreadyCounted } = require("../job/job-totals");

exports.default = async function (socket, jobid) {
  try {
    CdkBase.createLogEvent(
      socket,
      "DEBUG",
      `Received request to calculate allocations for ${jobid}`
    );
    const job = await QueryJobData(socket, jobid);
    const { bodyshop } = job;

    const taxAllocations = {
      local: {
        center: bodyshop.md_responsibility_centers.taxes.local.name,
        sale: Dinero(job.job_totals.totals.local_tax),
        cost: Dinero(),
        profitCenter: bodyshop.md_responsibility_centers.taxes.local,
        costCenter: bodyshop.md_responsibility_centers.taxes.local,
      },
      state: {
        center: bodyshop.md_responsibility_centers.taxes.state.name,
        sale: Dinero(job.job_totals.totals.state_tax),
        cost: Dinero(),
        profitCenter: bodyshop.md_responsibility_centers.taxes.state,
        costCenter: bodyshop.md_responsibility_centers.taxes.state,
      },
      federal: {
        center: bodyshop.md_responsibility_centers.taxes.federal.name,
        sale: Dinero(job.job_totals.totals.federal_tax),
        cost: Dinero(),
        profitCenter: bodyshop.md_responsibility_centers.taxes.federal,
        costCenter: bodyshop.md_responsibility_centers.taxes.federal,
      },
    };

    //Determine if there are MAPA and MASH lines already on the estimate.
    //If there are, don't do anything extra (mitchell estimate)
    //Otherwise, calculate them and add them to the default MAPA and MASH centers.
    let hasMapaLine = false;
    let hasMashLine = false;

    const profitCenterHash = job.joblines.reduce((acc, val) => {
      //Check the Parts Assignment
      if (val.db_ref === "936008") {
        //If either of these DB REFs change, they also need to change in job-totals/job-costing calculations.
        hasMapaLine = true;
      }
      if (val.db_ref === "936007") {
        hasMashLine = true;
      }

      if (val.profitcenter_part) {
        if (!acc[val.profitcenter_part]) acc[val.profitcenter_part] = Dinero();

        let DineroAmount = Dinero({
          amount: Math.round(val.act_price * 100),
        }).multiply(val.part_qty || 1);

        DineroAmount = DineroAmount.add(
          ((val.prt_dsmk_m && val.prt_dsmk_m !== 0) ||
            (val.prt_dsmk_p && val.prt_dsmk_p !== 0)) &&
            DiscountNotAlreadyCounted(val, job.joblines)
            ? val.prt_dsmk_m
              ? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) })
              : Dinero({
                  amount: Math.round(val.act_price * 100),
                })
                  .multiply(val.part_qty || 0)
                  .percentage(Math.abs(val.prt_dsmk_p || 0))
                  .multiply(val.prt_dsmk_p > 0 ? 1 : -1)
            : Dinero()
        );

        acc[val.profitcenter_part] =
          acc[val.profitcenter_part].add(DineroAmount);
      }
      if (val.profitcenter_labor && val.mod_lbr_ty) {
        //Check the Labor Assignment.

        if (!acc[val.profitcenter_labor])
          acc[val.profitcenter_labor] = Dinero();

        acc[val.profitcenter_labor] = acc[val.profitcenter_labor].add(
          Dinero({
            amount: Math.round(
              job[`rate_${val.mod_lbr_ty.toLowerCase()}`] * 100
            ),
          }).multiply(val.mod_lb_hrs)
        );
      }
      return acc;
    }, {});

    const selectedDmsAllocationConfig =
      bodyshop.md_responsibility_centers.dms_defaults.find(
        (d) => d.name === job.dms_allocation
      );
    CdkBase.createLogEvent(
      socket,
      "DEBUG",
      `Using DMS Allocation ${
        selectedDmsAllocationConfig && selectedDmsAllocationConfig.name
      } for cost export.`
    );

    let costCenterHash = {};
    //Check whether to skip this if PBS and using AP module.
    const disablebillwip = !!bodyshop?.pbs_configuration?.disablebillwip;

    if (!disablebillwip) {
      costCenterHash = job.bills.reduce((bill_acc, bill_val) => {
        bill_val.billlines.map((line_val) => {
          if (
            !bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]]
          )
            bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] =
              Dinero();

          let lineDinero = Dinero({
            amount: Math.round((line_val.actual_cost || 0) * 100),
          })
            .multiply(line_val.quantity)
            .multiply(bill_val.is_credit_memo ? -1 : 1);

          bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] =
            bill_acc[
              selectedDmsAllocationConfig.costs[line_val.cost_center]
            ].add(lineDinero);
          return null;
        });
        return bill_acc;
      }, {});
    }

    job.timetickets.forEach((ticket) => {
      //Get the total amount of the ticket.
      let TicketTotal = Dinero({
        amount: Math.round(
          ticket.rate *
            (ticket.employee && ticket.employee.flat_rate
              ? ticket.productivehrs || 0
              : ticket.actualhrs || 0) *
            100
        ),
      });
      //Add it to the right cost center.
      if (!costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]])
        costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]] =
          Dinero();

      costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]] =
        costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]].add(
          TicketTotal
        );
    });

    if (!hasMapaLine && job.job_totals.rates.mapa.total.amount > 0) {
      // console.log("Adding MAPA Line Manually.");
      const mapaAccountName = selectedDmsAllocationConfig.profits.MAPA;

      const mapaAccount = bodyshop.md_responsibility_centers.profits.find(
        (c) => c.name === mapaAccountName
      );

      if (mapaAccount) {
        if (!profitCenterHash[mapaAccountName])
          profitCenterHash[mapaAccountName] = Dinero();

        profitCenterHash[mapaAccountName] = profitCenterHash[
          mapaAccountName
        ].add(Dinero(job.job_totals.rates.mapa.total));
      } else {
        //console.log("NO MAPA ACCOUNT FOUND!!");
      }
    }

    if (!hasMashLine && job.job_totals.rates.mash.total.amount > 0) {
      // console.log("Adding MASH Line Manually.");

      const mashAccountName = selectedDmsAllocationConfig.profits.MASH;

      const mashAccount = bodyshop.md_responsibility_centers.profits.find(
        (c) => c.name === mashAccountName
      );

      if (mashAccount) {
        if (!profitCenterHash[mashAccountName])
          profitCenterHash[mashAccountName] = Dinero();

        profitCenterHash[mashAccountName] = profitCenterHash[
          mashAccountName
        ].add(Dinero(job.job_totals.rates.mash.total));
      } else {
        //  console.log("NO MASH ACCOUNT FOUND!!");
      }
    }
    console.log(
      Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting),
      typeof Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting)
    );
    if (!!bodyshop?.cdk_configuration?.sendmaterialscosting) {
      //Manually send the percentage of the costing.

      //Paint Mat
      const mapaAccountName = selectedDmsAllocationConfig.costs.MAPA;
      const mapaAccount = bodyshop.md_responsibility_centers.costs.find(
        (c) => c.name === mapaAccountName
      );
      if (mapaAccount) {
        if (!costCenterHash[mapaAccountName])
          costCenterHash[mapaAccountName] = Dinero();
        costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add(
          Dinero(job.job_totals.rates.mapa.total).percentage(
            bodyshop?.cdk_configuration?.sendmaterialscosting
          )
        );
      } else {
        //console.log("NO MAPA ACCOUNT FOUND!!");
      }

      //Shop Mat
      const mashAccountName = selectedDmsAllocationConfig.costs.MASH;
      const mashAccount = bodyshop.md_responsibility_centers.costs.find(
        (c) => c.name === mashAccountName
      );
      if (mashAccount) {
        if (!costCenterHash[mashAccountName])
          costCenterHash[mashAccountName] = Dinero();
        costCenterHash[mashAccountName] = costCenterHash[mashAccountName].add(
          Dinero(job.job_totals.rates.mash.total).percentage(
            bodyshop?.cdk_configuration?.sendmaterialscosting
          )
        );
      } else {
        //  console.log("NO MASH ACCOUNT FOUND!!");
      }
    }

    const { ca_bc_pvrt } = job;
    if (ca_bc_pvrt) {
      // const pvrtAccount = bodyshop.md_responsibility_centers.profits.find(
      //   (c) => c.name === mashAccountName
      // );

      taxAllocations.state.sale = taxAllocations.state.sale.add(
        Dinero({ amount: Math.round((ca_bc_pvrt || 0) * 100) })
      );
    }

    if (job.towing_payable && job.towing_payable !== 0) {
      const towAccountName = selectedDmsAllocationConfig.profits.TOW;

      const towAccount = bodyshop.md_responsibility_centers.profits.find(
        (c) => c.name === towAccountName
      );

      if (towAccount) {
        if (!profitCenterHash[towAccountName])
          profitCenterHash[towAccountName] = Dinero();

        profitCenterHash[towAccountName] = profitCenterHash[towAccountName].add(
          Dinero({
            amount: Math.round((job.towing_payable || 0) * 100),
          })
        );
      } else {
        //  console.log("NO MASH ACCOUNT FOUND!!");
      }
    }
    if (job.storage_payable && job.storage_payable !== 0) {
      const storageAccountName = selectedDmsAllocationConfig.profits.TOW;

      const towAccount = bodyshop.md_responsibility_centers.profits.find(
        (c) => c.name === storageAccountName
      );

      if (towAccount) {
        if (!profitCenterHash[storageAccountName])
          profitCenterHash[storageAccountName] = Dinero();

        profitCenterHash[storageAccountName] = profitCenterHash[
          storageAccountName
        ].add(
          Dinero({
            amount: Math.round((job.storage_payable || 0) * 100),
          })
        );
      } else {
        //  console.log("NO MASH ACCOUNT FOUND!!");
      }
    }

    if (job.adjustment_bottom_line && job.adjustment_bottom_line !== 0) {
      const otherAccountName = selectedDmsAllocationConfig.profits.PAO;

      const otherAccount = bodyshop.md_responsibility_centers.profits.find(
        (c) => c.name === otherAccountName
      );

      if (otherAccount) {
        if (!profitCenterHash[otherAccountName])
          profitCenterHash[otherAccountName] = Dinero();

        profitCenterHash[otherAccountName] = profitCenterHash[
          otherAccountName
        ].add(
          Dinero({
            amount: Math.round((job.adjustment_bottom_line || 0) * 100),
          })
        );
      } else {
        //  console.log("NO MASH ACCOUNT FOUND!!");
      }
    }

    const jobAllocations = _.union(
      Object.keys(profitCenterHash),
      Object.keys(costCenterHash)
    ).map((key) => {
      const profitCenter = bodyshop.md_responsibility_centers.profits.find(
        (c) => c.name === key
      );
      const costCenter = bodyshop.md_responsibility_centers.costs.find(
        (c) => c.name === key
      );

      return {
        center: key,
        sale: profitCenterHash[key] ? profitCenterHash[key] : Dinero(),
        cost: costCenterHash[key] ? costCenterHash[key] : Dinero(),
        profitCenter,
        costCenter,
      };
    });

    return [
      ...jobAllocations,
      ...Object.keys(taxAllocations)
        .filter(
          (key) =>
            taxAllocations[key].sale.getAmount() > 0 ||
            taxAllocations[key].cost.getAmount() > 0
        )
        .map((key) => {
          if (
            key === "federal" &&
            selectedDmsAllocationConfig.gst_override &&
            selectedDmsAllocationConfig.gst_override !== ""
          ) {
            const ret = { ...taxAllocations[key], tax: key };
            ret.costCenter.dms_acctnumber =
              selectedDmsAllocationConfig.gst_override;
            ret.profitCenter.dms_acctnumber =
              selectedDmsAllocationConfig.gst_override;
            return ret;
          } else {
            return { ...taxAllocations[key], tax: key };
          }
        }),
    ];
  } catch (error) {
    CdkBase.createLogEvent(
      socket,
      "ERROR",
      `Error encountered in CdkCalculateAllocations. ${error}`
    );
  }
};

async function QueryJobData(socket, jobid) {
  CdkBase.createLogEvent(socket, "DEBUG", `Querying job data for id ${jobid}`);
  const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
  const result = await client
    .setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
    .request(queries.GET_CDK_ALLOCATIONS, { id: jobid });
  CdkBase.createLogEvent(
    socket,
    "TRACE",
    `Job data query result ${JSON.stringify(result, null, 2)}`
  );
  return result.jobs_by_pk;
}
