const queries = require("../graphql-client/queries");
const logger = require("../utils/logger");

const predefinedPartTypes = ["PAN", "PAC", "PAR", "PAL", "PAA", "PAM", "PAP", "PAS", "PASL", "PAG"];
const predefinedModLbrTypes = [
  "LAA",
  "LAB",
  "LAD",
  "LAE",
  "LAF",
  "LAG",
  "LAM",
  "LAR",
  "LAS",
  "LAU",
  "LA1",
  "LA2",
  "LA3",
  "LA4"
];

exports.partsScan = async function (req, res) {
  const { jobid } = req.body;
  const BearerToken = req.BearerToken;
  const client = req.userGraphQLClient;

  logger.log("job-parts-scan", "DEBUG", req.user?.email, jobid, null);

  try {
    const data = await client
      .setHeaders({ Authorization: BearerToken })
      .request(queries.QUERY_PARTS_SCAN, { id: jobid });

    const rules = data.jobs_by_pk.bodyshop.md_parts_scan || [];
    if (!Array.isArray(rules)) {
      // noinspection ExceptionCaughtLocallyJS
      throw new Error("Invalid md_parts_scan format. Expected an array of rules.");
    }

    const compiledRules = rules.map((rule) => ({
      ...rule,
      regex:
        typeof rule.value === "string" && rule.caseInsensitive
          ? new RegExp(rule.value, "i")
          : typeof rule.value === "string"
            ? new RegExp(rule.value)
            : null
    }));

    const criticalIds = new Set();

    for (const jobline of data.jobs_by_pk.joblines) {
      for (const { field, regex, operation, value, mark_critical, update_field, update_value } of compiledRules) {
        // IO-3077 - Remove skip as this is extended to include line updates.
        // if (criticalIds.has(jobline.id)) break; // Skip further evaluation if already critical

        let jobValue = jobline[field];
        let match = false;

        if (field === "part_number") {
          match = regex
            ? regex.test(jobline.oem_partno || "") || regex.test(jobline.alt_partno || "")
            : jobline.oem_partno === value || jobline.alt_partno === value;
        } else if (field === "part_type") {
          match = predefinedPartTypes.includes(value) && value === jobValue;
        } else if (field === "mod_lbr_ty") {
          match = predefinedModLbrTypes.includes(value) && value === jobValue;
        } else if (regex && typeof jobValue === "string") {
          if (operation === "contains") match = regex.test(jobValue);
          if (operation === "startsWith") match = new RegExp(`^${value}`).test(jobValue);
          if (operation === "endsWith") match = new RegExp(`${value}$`).test(jobValue);
          if (operation === "equals") match = jobValue === value;
        } else if (typeof jobValue === "number") {
          if (operation === ">") match = jobValue > value;
          if (operation === "<") match = jobValue < value;
          if (operation === "=") match = jobValue === value;
        }

        if (match) {
          if (mark_critical) {
            //Could technically lead to duplicates, but they'd only be n positives, ultimately leading a positive.
            criticalIds.add(jobline.id);
          }
          if (update_field && update_value) {
            let actualUpdateField = update_field;
            if (update_field === "part_number") {
              // Determine which part number field to update based on the match
                if (!jobline.oem_partno) {
                actualUpdateField = "oem_partno";
                } else {
                if (regex) {
                  actualUpdateField = regex.test(jobline.oem_partno || "") ? "oem_partno" : "alt_partno";
                } else {
                  actualUpdateField = jobline.oem_partno === value ? "oem_partno" : "alt_partno";
                }
                }
            }
            if (actualUpdateField) {
              await client.setHeaders({ Authorization: BearerToken }).request(queries.UPDATE_JOB_LINE, {
                lineId: jobline.id,
                line: { [actualUpdateField]: update_value, manual_line: true }
              });
            }

            await client.setHeaders({ Authorization: BearerToken }).request(queries.INSERT_AUDIT_TRAIL, {
              auditObj: {
                bodyshopid: data.jobs_by_pk.bodyshop.id,
                jobid,
                operation: `Jobline (#${jobline.line_no} ${jobline.line_desc}/${jobline.id}) ${update_field} updated from ${jobline[update_field]} to ${update_value}. Lined marked as manual line.`,
                type: "partscanupdate",
                useremail: req.user.email
              }
            });
          }

          //break; // No need to evaluate further rules for this jobline
        }
      }
    }

    const result = await client
      .setHeaders({ Authorization: BearerToken })
      .request(queries.UPDATE_PARTS_CRITICAL, { IdsToMarkCritical: Array.from(criticalIds), jobid });

    res.status(200).json(result);
  } catch (error) {
    logger.log("job-parts-scan-error", "ERROR", req.user?.email, jobid, {
      jobid,
      error: error.message,
      stack: error.stack
    });
    res.status(400).json(JSON.stringify({ message: error?.message }));
  }
};
