const { RRClient } = require("./lib/index.cjs");
const { getRRConfigFromBodyshop } = require("./rr-config");
const CreateRRLogEvent = require("./rr-logger-event");
const { withRRRequestXml } = require("./rr-log-xml");
const InstanceManager = require("../utils/instanceMgr").default;

/**
 * Country code map for normalization
 * @type {{US: string, USA: string, "UNITED STATES": string, CA: string, CAN: string, CANADA: string}}
 */
const COUNTRY_MAP = {
  US: "US",
  USA: "US",
  "UNITED STATES": "US",
  CA: "CA",
  CAN: "CA",
  CANADA: "CA"
};

/**
 * Normalize country input to 2-char code
 * @param v
 * @returns {*|string}
 */
const toCountry2 = (v) => {
  const s = String(v || "")
    .trim()
    .toUpperCase();
  if (!s) return "US"; // sane default
  if (COUNTRY_MAP[s]) return COUNTRY_MAP[s];
  // fallbacks: prefer 2-char; last resort: take first 2
  return s.length === 2 ? s : s.slice(0, 2);
};

/**
 * Normalize phone number to 10-digit string
 * @param num
 * @returns {string}
 */
const normalizePhone = (num) => {
  const d = String(num || "").replace(/\D/g, "");
  const n = d.length === 11 && d.startsWith("1") ? d.slice(1) : d;
  return n.slice(0, 10);
};

/**
 * Normalize postal code based on country
 * @param pc
 * @param country
 * @returns {string}
 */
const normalizePostal = (pc, country) => {
  const s = String(pc || "").trim();
  if (country === "US") return s.replace(/[^0-9]/g, "").slice(0, 5);
  if (country === "CA") return s.toUpperCase().replace(/\s+/g, "").slice(0, 6);
  return s;
};

/**
 * Sanitize RR customer payload (addresses, phones, names)
 * @param payload
 * @returns {{}}
 */
const sanitizeRRCustomerPayload = (payload = {}) => {
  const out = { ...payload };

  out.addresses = (payload.addresses || []).map((a) => {
    const country = toCountry2(a.country);
    return {
      ...a,
      country,
      state: String(a.state || "")
        .toUpperCase()
        .slice(0, 2),
      postalCode: normalizePostal(a.postalCode, country)
    };
  });

  out.phones = (payload.phones || []).map((p) => ({
    ...p,
    number: normalizePhone(p.number)
  }));

  // trim names defensively (RR has various max lengths by site config)
  if (out.firstName) out.firstName = String(out.firstName).trim().slice(0, 30);
  if (out.lastName) out.lastName = String(out.lastName).trim().slice(0, 30);

  return out;
};

/**
 * Build an RR client + common opts from a bodyshop row
 * @param bodyshop
 * @returns {{client: *, opts: {routing: {dealerNumber: *, storeNumber: *, areaNumber: *}, envelope: {sender: {component: string, task: string, referenceId: string, creator: string, senderName: string}}}}}
 */
const buildClientAndOpts = (bodyshop) => {
  const cfg = getRRConfigFromBodyshop(bodyshop);
  const client = new RRClient({
    baseUrl: cfg.baseUrl,
    username: cfg.username,
    password: cfg.password,
    timeoutMs: cfg.timeoutMs,
    retries: cfg.retries
  });

  const opts = {
    routing: cfg.routing,
    envelope: {
      sender: {
        component: "Rome",
        task: "CU",
        referenceId: "Insert",
        creator: "RCI",
        senderName: "RCI"
      }
    }
  };
  return { client, opts };
};

/**
 * Strip all non-digit characters from a string
 * @param s
 * @returns {string}
 */
const digitsOnly = (s) => {
  return String(s || "").replace(/\D/g, "");
};

/**
 * Build RR customer payload from job.ownr_* fields, with optional overrides.
 * @param job
 * @param overrides
 * @returns {{ibFlag: string, firstName, lastName, customerName, createdBy, customerType, addresses: [{type, line1: *, line2, city, state, postalCode, country}], phones: {number: *}[], emails: [{address: string}]}}
 */
const buildCustomerPayloadFromJob = (job, overrides = {}) => {
  // Pull ONLY from job.ownr_* fields (no job.customer.*)
  const firstName = overrides.firstName ?? job?.ownr_fn ?? undefined;
  const lastName = overrides.lastName ?? job?.ownr_ln ?? undefined;
  const companyName = overrides.companyName ?? overrides.company ?? job?.ownr_co_nm ?? undefined;

  // Decide Individual vs Business (caller can force via overrides.ibFlag)
  const ibFlag = (overrides.ibFlag || (firstName ? "I" : companyName ? "B" : "I")).toUpperCase();

  // Email(s)
  const email = overrides.email ?? job?.ownr_ea ?? undefined;
  const emails = email ? [{ address: String(email) }] : undefined;

  // Phones
  const phone1 = digitsOnly(job?.ownr_ph1);
  const phone2 = digitsOnly(job?.ownr_ph2);

  let primaryPhone = phone1 || phone2;
  if (primaryPhone) {
    primaryPhone = primaryPhone.slice(-10); // enforce 10 digits
  }

  const phones = primaryPhone ? [{ number: primaryPhone, type: "H" }] : [];

  // Address (include only if line1 exists; template requires Addr1 if address is present)
  const line1 = overrides.addressLine1 ?? job?.ownr_addr1 ?? undefined;

  const addresses = line1
    ? [
        {
          type: overrides.addressType || "P",
          line1,
          line2: overrides.addressLine2 ?? job?.ownr_addr2 ?? undefined,
          city: overrides.city ?? job?.ownr_city ?? undefined,
          state: overrides.state ?? job?.ownr_st ?? undefined,
          postalCode: overrides.postalCode ?? job?.ownr_zip ?? undefined,
          country: (overrides.country ?? job?.ownr_ctry ?? "CA") || undefined
        }
      ]
    : undefined;

  // Enforce lib requirement: lastName OR customerName
  if (!lastName && !companyName) {
    throw new Error(
      "Cannot build RR customer payload: lastName or companyName is required (no ownr_ln / ownr_co_nm on job)."
    );
  }

  const payload = {
    ibFlag, // 'I' or 'B'
    firstName: firstName || undefined,
    lastName: lastName || undefined,
    customerName: companyName || undefined,
    createdBy: overrides.createdBy || InstanceManager({ imex: "ImEX Online", rome: "Rome Online" }),
    customerType: overrides.customerType || "R", // Retail default
    addresses,
    phones,
    emails
  };

  Object.keys(payload).forEach((k) => payload[k] === undefined && delete payload[k]);
  return payload;
};

/**
 * Create a customer in RR and return { customerNo, raw }.
 * Maps data.dmsRecKey -> customerNo for compatibility with existing callers.
 */
const createRRCustomer = async ({ bodyshop, job, overrides = {}, socket }) => {
  const { client, opts } = buildClientAndOpts(bodyshop);
  const payload = buildCustomerPayloadFromJob(job, overrides);
  const safePayload = sanitizeRRCustomerPayload(payload);

  // Story step: clearly show we are about to hit Reynolds insertCustomer
  CreateRRLogEvent(socket, "DEBUG", "{CU} insertCustomer: begin", {
    ibFlag: safePayload.ibFlag,
    hasAddress: Array.isArray(safePayload.addresses) && safePayload.addresses.length > 0,
    hasPhones: Array.isArray(safePayload.phones) && safePayload.phones.length > 0,
    hasEmails: Array.isArray(safePayload.emails) && safePayload.emails.length > 0
  });

  let response;
  try {
    response = await client.insertCustomer(safePayload, opts);
    // Very noisy; only show when log level is cranked to SILLY
    CreateRRLogEvent(
      socket,
      "SILLY",
      "{CU} insertCustomer: raw response",
      withRRRequestXml(response, { response })
    );
  } catch (e) {
    CreateRRLogEvent(
      socket,
      "ERROR",
      "RR insertCustomer transport error",
      withRRRequestXml(e, {
        message: e?.message,
        code: e?.code,
        status: e?.meta?.status || e?.status,
        payload: safePayload
      })
    );
    throw e;
  }

  const data = response?.data ?? response;
  const trx = response?.statusBlocks?.transaction;

  let customerNo = data?.dmsRecKey;
  if (!customerNo) {
    CreateRRLogEvent(
      socket,
      "ERROR",
      "RR insertCustomer returned no dmsRecKey/custNo",
      withRRRequestXml(response, {
        status: trx?.status,
        statusCode: trx?.statusCode,
        message: trx?.message,
        data
      })
    );

    throw new Error(
      `RR insertCustomer returned no dmsRecKey (status=${trx?.status ?? "?"} code=${trx?.statusCode ?? "?"}${
        trx?.message ? ` msg=${trx.message}` : ""
      })`
    );
  }

  const out = { customerNo: String(customerNo), raw: data };

  CreateRRLogEvent(socket, "INFO", "{CU} insertCustomer: success", {
    customerNo: out.customerNo,
    status: trx || null
  });

  return out;
};

module.exports = {
  createRRCustomer
};
