const DineroQbFormat = require("../accounting-constants").DineroQbFormat;
const queries = require("../../graphql-client/queries");
const Dinero = require("dinero.js");
const builder = require("xmlbuilder2");
const QbXmlUtils = require("./qbxml-utils");
const moment = require("moment-timezone");
const logger = require("../../utils/logger");
const InstanceManager = require("../../utils/instanceMgr").default;

exports.default = async (req, res) => {
  const { bills: billsToQuery } = req.body;

  const BearerToken = req.BearerToken;
  const client = req.userGraphQLClient;

  try {
    logger.log("qbxml-payable-create", "DEBUG", req.user.email, req.body.billsToQuery);

    const result = await client
      .setHeaders({ Authorization: BearerToken })
      .request(queries.QUERY_BILLS_FOR_PAYABLES_EXPORT, {
        bills: billsToQuery
      });
    const { bills, bodyshops } = result;
    const bodyshop = bodyshops[0];

    const QbXmlToExecute = [];
    bills.map((i) => {
      QbXmlToExecute.push({
        id: i.id,
        okStatusCodes: ["0"],
        qbxml: generateBill(i, bodyshop)
      });
    });

    //For each invoice.
    res.status(200).json(QbXmlToExecute);
  } catch (error) {
    logger.log("qbxml-payable-error", "ERROR", req?.user?.email, null, {
      billsToQuery: req?.body?.billsToQuery,
      error: error?.message,
      stack: error?.stack
    });
    res.status(400).send(JSON.stringify(error));
  }
};

const generateBill = (bill, bodyshop) => {
  let lines;
  if (bodyshop.accountingconfig.accumulatePayableLines === true) {
    lines = Object.values(
      bill.billlines.reduce((acc, il) => {
        const { cost_center, actual_cost, quantity = 1 } = il;

        if (!acc[cost_center]) {
          acc[cost_center] = { ...il, actual_cost: 0, quantity: 1 };
        }

        acc[cost_center].actual_cost += Math.round(actual_cost * quantity * 100);

        return acc;
      }, {})
    ).map((il) => {
      il.actual_cost /= 100;
      return generateBillLine(il, bodyshop.md_responsibility_centers, bill.job.class);
    });
  } else {
    lines = bill.billlines.map((il) => generateBillLine(il, bodyshop.md_responsibility_centers, bill.job.class));
  }

  const billQbxmlObj = {
    QBXML: {
      QBXMLMsgsRq: {
        "@onError": "continueOnError",
        [`${bill.is_credit_memo ? "VendorCreditAddRq" : "BillAddRq"}`]: {
          [`${bill.is_credit_memo ? "VendorCreditAdd" : "BillAdd"}`]: {
            VendorRef: {
              FullName: bill.vendor.name
            },
            TxnDate: moment(bill.date)
              //.tz(bill.job.bodyshop.timezone)
              .format("YYYY-MM-DD"),
            ...(!bill.is_credit_memo &&
              bill.vendor.due_date && {
                DueDate: moment(bill.date)
                  // .tz(bill.job.bodyshop.timezone)
                  .add(bill.vendor.due_date, "days")
                  .format("YYYY-MM-DD")
              }),
            RefNumber: bill.invoice_number,
            Memo: `RO ${bill.job.ro_number || ""}`,
            ExpenseLineAdd: lines
          }
        }
      }
    }
  };

  var billQbxml_partial = builder
    .create(billQbxmlObj, {
      version: "1.30",
      encoding: "UTF-8",
      headless: true
    })
    .end({ pretty: true });

  const billQbxml_Full = QbXmlUtils.addQbxmlHeader(billQbxml_partial);

  return billQbxml_Full;
};

const generateBillLine = (billLine, responsibilityCenters, jobClass) => {
  return {
    AccountRef: {
      FullName: responsibilityCenters.costs.find((c) => c.name === billLine.cost_center).accountname
    },
    Amount: Dinero({
      amount: Math.round(billLine.actual_cost * 100)
    })
      .multiply(billLine.quantity || 1)
      .toFormat(DineroQbFormat),
    ...(jobClass ? { ClassRef: { FullName: jobClass } } : {}),
    ...InstanceManager({
      imex: {
        SalesTaxCodeRef: {
          FullName: findTaxCode(billLine, responsibilityCenters.sales_tax_codes)
        }
      }
    })
  };
};

const findTaxCode = (billLine, taxcode) => {
  const {
    applicable_taxes: { local, state, federal }
  } =
    billLine.applicable_taxes === null
      ? {
          ...billLine,
          applicable_taxes: { local: false, state: false, federal: false }
        }
      : billLine;
  const t = taxcode.filter((t) => !!t.local === !!local && !!t.state === !!state && !!t.federal === !!federal);
  if (t.length === 1) {
    return t[0].code;
  } else if (t.length > 1) {
    return "Multiple Tax Codes Match";
  } else {
    return "No Tax Code Matches";
  }
};
