const { buildClientAndOpts, rrCombinedSearch } = require("./rr-lookup");
const CreateRRLogEvent = require("./rr-logger-event");
const { withRRRequestXml } = require("./rr-log-xml");
/**
 * Pick and normalize VIN from inputs
 * @param vin
 * @param job
 * @returns {string}
 */
const pickVin = ({ vin, job }) => {
  const v = vin || job?.v_vin || job?.vehicle?.vin || job?.vin || job?.vehicleVin || null;

  if (!v) return "";
  return String(v)
    .replace(/[^A-Za-z0-9]/g, "")
    .toUpperCase()
    .slice(0, 17);
};

/**
 * Pick and normalize customer number from inputs
 * @param selectedCustomerNo
 * @param custNo
 * @param customerNo
 * @returns {string|string}
 */
const pickCustNo = ({ selectedCustomerNo, custNo, customerNo }) => {
  const c = selectedCustomerNo ?? custNo ?? customerNo ?? null;
  return c != null ? String(c).trim() : "";
};

/**
 * Simple length sanitizer for outbound strings
 * Returns undefined if value is null/undefined/empty after trim.
 */
const sanitizeLength = (value, maxLen) => {
  if (value == null) return undefined;
  let s = String(value).trim();
  if (!s) return undefined;
  if (maxLen && s.length > maxLen) {
    s = s.slice(0, maxLen);
  }
  return s;
};

/**
 * Extract owner customer numbers from combined search results
 * @param res
 * @param wantedVin
 * @returns {Set<any>}
 */
const ownersFromCombined = (res, wantedVin) => {
  const blocks = Array.isArray(res?.data) ? res.data : [];
  const owners = new Set();
  for (const blk of blocks) {
    const serv = Array.isArray(blk?.ServVehicle) ? blk.ServVehicle : [];
    for (const sv of serv) {
      const v = sv?.Vehicle || {};
      const vin = (v?.Vin || v?.VIN || v?.vin || "").toString().toUpperCase();
      const cust = sv?.VehicleServInfo?.CustomerNo;
      if (vin && cust && (!wantedVin || vin === wantedVin)) {
        owners.add(String(cust));
      }
    }
  }
  return owners;
};

/**
 * Determine if error indicates "already exists"
 * @param e
 * @returns {boolean}
 */
const isAlreadyExistsError = (e) => {
  if (!e) return false;
  if (e.code === 300) return true;
  const msg = (e.message || "").toUpperCase();
  return msg.includes("ALREADY EXISTS") || msg.includes("VEHICLE ALREADY EXISTS");
};

function deriveMakeCode(makeDesc) {
  if (!makeDesc) return "FR"; // safe default

  const map = {
    Ford: "FO",
    FORD: "FO",
    Lincoln: "LN",
    Mercury: "MY",
    Chevrolet: "CH",
    GMC: "GM",
    Cadillac: "CA",
    Buick: "BU",
    Pontiac: "PO",
    Oldsmobile: "OL",
    Saturn: "SA",
    Dodge: "DO",
    Chrysler: "CH",
    Jeep: "JE",
    Plymouth: "PL",
    Toyota: "TY",
    Lexus: "LX",
    Honda: "HO",
    Acura: "AC",
    Nissan: "NI",
    Infiniti: "IN",
    Hyundai: "HY",
    Kia: "KI",
    Subaru: "SU",
    Mazda: "MZ",
    Volkswagen: "VW",
    Audi: "AU",
    BMW: "BM",
    Mercedes: "MB",
    "Mercedes-Benz": "MB",
    Volvo: "VO",
    Ram: "RA" // post-2010
  };

  return map[makeDesc.trim().toUpperCase()] || "FR"; // TODO Default set
}

/**
 * Ensure/create a Service Vehicle in RR for the given VIN + customer.
 * Args may contain:
 *  - client, routing  (prebuilt)  OR  bodyshop (we'll build a client)
 *  - vin OR job (we'll derive vin from job)
 *  - selectedCustomerNo / custNo / customerNo
 *  - license (optional)
 *  - socket (for logging)
 *
 * Returns: { created:boolean, exists:boolean, vin, customerNo, svId?, status? }
 */
const ensureRRServiceVehicle = async (args = {}) => {
  const {
    client: inClient,
    routing: inRouting,
    bodyshop,
    job,
    vin,
    selectedCustomerNo,
    custNo,
    customerNo,
    license,
    socket
  } = args;

  // Build/derive essentials
  let client = inClient;
  let routing = inRouting;

  if (!client || !routing) {
    if (!bodyshop) {
      throw new Error("ensureRRServiceVehicle: either (client & routing) or bodyshop is required");
    }
    const built = buildClientAndOpts(bodyshop);
    client = built.client;
    routing = built.opts?.routing;
  }

  const vinStr = pickVin({ vin, job });
  const custNoStr = pickCustNo({ selectedCustomerNo, custNo, customerNo });

  if (!vinStr) throw new Error("ensureRRServiceVehicle: vin required");
  if (!custNoStr) throw new Error("ensureRRServiceVehicle: customerNo required");

  // --- Preflight: does VIN already exist (and under whom)? ---
  try {
    let owners = new Set();
    if (bodyshop) {
      const combinedSearchResponse = await rrCombinedSearch(bodyshop, { kind: "vin", vin: vinStr, maxResults: 50 });

      CreateRRLogEvent(
        socket,
        "silly",
        "{SV} Preflight combined search by VIN: raw response",
        withRRRequestXml(combinedSearchResponse, { response: combinedSearchResponse })
      );

      owners = ownersFromCombined(combinedSearchResponse, vinStr);
    }
    // Short-circuit: VIN exists anywhere -> don't try to insert
    if (owners.size > 0) {
      const ownedBySame = owners.has(custNoStr);

      CreateRRLogEvent(socket, ownedBySame ? "info" : "warn", "{SV} VIN already present in RR; skipping insert", {
        vin: vinStr,
        selectedCustomerNo: custNoStr,
        owners: Array.from(owners)
      });

      return {
        created: false,
        exists: true,
        vin: vinStr,
        customerNo: custNoStr,
        ownedBySame
      };
    }
  } catch (e) {
    // Preflight shouldn't be fatal; log and continue to insert (idempotency will still be handled)
    CreateRRLogEvent(
      socket,
      "warn",
      "{SV} VIN preflight lookup failed; continuing to insert",
      withRRRequestXml(e, {
        vin: vinStr,
        error: e?.message
      })
    );
  }

  // Vendor says: MODEL DESCRIPTION HAS MAXIMUM LENGTH OF 20
  const rawModelDesc = job?.v_model_desc;
  const safeModelDesc = sanitizeLength(rawModelDesc, 20);
  if (rawModelDesc && safeModelDesc && rawModelDesc.trim() !== safeModelDesc) {
    CreateRRLogEvent(socket, "warn", "{SV} Truncated model description to 20 chars", {
      original: rawModelDesc,
      truncated: safeModelDesc
    });
  }

  const insertPayload = {
    vin: vinStr.toUpperCase(), // "1FDWX34Y28EB01395"

    // 2-character make code (from v_make_desc → known mapping)
    vehicleMake: deriveMakeCode(job?.v_make_desc), // → "FR" for Ford
    year: job?.v_model_yr || undefined,

    // Model description (RR: max length 20)
    modelDesc: safeModelDesc,

    // Model number / carline / other optional fields
    mdlNo: undefined,
    carline: undefined,
    extClrDesc: sanitizeLength(job?.v_color, 30), // safe, configurable if vendor complains
    accentClr: undefined,
    aircond: undefined,
    pwrstr: undefined,
    transm: undefined,
    turbo: undefined,
    engineConfig: undefined,
    trim: undefined,

    // License plate
    licNo: sanitizeLength(license ? String(license) : undefined, 20),

    customerNo: custNoStr,
    stockId: sanitizeLength(job?.ro_number, 20), // RO as stock#, truncated for safety
    vehicleServInfo: {
      customerNo: custNoStr, // REQUIRED — this is what toServiceVehicleView() validates
      salesmanNo: undefined,
      inServiceDate: undefined,
      productionDate: undefined,
      modelMaintCode: undefined,
      teamCode: undefined,
      vehExtWarranty: undefined,
      advisor: undefined
    }
  };

  const insertOpts = {
    routing,
    envelope: {
      sender: {
        component: "Rome",
        task: "SV",
        referenceId: "Insert",
        creator: "RCI",
        senderName: "RCI"
      }
    }
  };

  CreateRRLogEvent(socket, "info", "{SV} Inserting service vehicle", {
    vin: vinStr,
    selectedCustomerNo: custNoStr,
    payloadShape: Object.keys(insertPayload).filter((k) => insertPayload[k] != null)
  });

  try {
    const res = await client.insertServiceVehicle(insertPayload, insertOpts);

    CreateRRLogEvent(socket, "silly", "{SV} insertServiceVehicle: raw response", withRRRequestXml(res, { res }));

    const data = res?.data ?? {};
    const svId = data?.dmsRecKey || data?.svId || undefined;

    CreateRRLogEvent(socket, "info", "{SV} insertServiceVehicle: success", {
      vin: vinStr,
      customerNo: custNoStr,
      svId
    });

    return {
      created: true,
      exists: false,
      vin: vinStr,
      customerNo: custNoStr,
      svId,
      status: res?.statusBlocks?.transaction
    };
  } catch (e) {
    if (isAlreadyExistsError(e)) {
      // Treat as idempotent success
      CreateRRLogEvent(socket, "warn", "{SV} insertServiceVehicle: already exists; treating as success", {
        vin: vinStr,
        customerNo: custNoStr,
        code: e?.code,
        status: e?.meta?.status || e?.status
      });

      return {
        created: false,
        exists: true,
        vin: vinStr,
        customerNo: custNoStr,
        status: e?.meta?.status || e?.status
      };
    }

    CreateRRLogEvent(
      socket,
      "error",
      "{SV} insertServiceVehicle: failure",
      withRRRequestXml(e, {
        message: e?.message,
        code: e?.code,
        status: e?.meta?.status || e?.status
      })
    );

    throw e;
  }
};

module.exports = {
  ensureRRServiceVehicle
};
