// // utils/flightUtils.js
// "use strict";

// const AIRLINES = {
//   "AI": "Air India",
//   "6E": "IndiGo",
//   "SG": "SpiceJet",
//   "UK": "Vistara",
//   "G8": "GoAir",
//   "IX": "Air India Express",
//   "QP": "Akasa Air",
//   "9I": "Alliance Air",
//   "I5": "AirAsia India"
// };

// function getAirlineName(code) {
//   return AIRLINES[code] || code;
// }

// function getHourSlot(timeStr) {
//   if (!timeStr) return "before6";
//   let hour = 0;
  
//   // Try parsing with Date object first for robustness
//   const date = new Date(timeStr);
//   if (!isNaN(date.getTime())) {
//     hour = date.getHours();
//   } else if (timeStr.includes("T")) {
//     const match = timeStr.match(/T(\d{2}):/);
//     hour = match ? parseInt(match[1]) : 0;
//   } else {
//     // Fallback for HH:mm formats or weird strings
//     const match = timeStr.match(/(\d{2}):/);
//     hour = match ? parseInt(match[1]) : 0;
//   }
  
//   if (hour < 6) return "before6";
//   if (hour < 12) return "6to12";
//   if (hour < 18) return "12to18";
//   return "after18";
// }

// function getHourSlotLabel(slot) {
//   const labels = {
//     "before6": "Before 6 AM",
//     "6to12": "6 AM - 12 PM",
//     "12to18": "12 PM - 6 PM",
//     "after18": "After 6 PM"
//   };
//   return labels[slot] || slot;
// }

// // ─────────────────────────────────────────────────────────────
// // ANALYZER
// // ─────────────────────────────────────────────────────────────
// exports.getFilterableFlights = (travelportResponse) => {
//   if (!travelportResponse) return [];

//   let rsp = travelportResponse;
//   if (rsp["SOAP:Envelope"]) rsp = rsp["SOAP:Envelope"]["SOAP:Body"] || {};
//   else if (rsp["soapenv:Envelope"]) rsp = rsp["soapenv:Envelope"]["soapenv:Body"] || {};

//   rsp = rsp["air:LowFareSearchRsp"] || rsp["air:AirLowFareSearchRsp"] || rsp["air:AirSearchRsp"] || rsp;

//   const itinerarySegments = {};
//   const itinerary = rsp["air:AirItinerary"] || rsp["air:AirSegmentList"];
//   if (itinerary && itinerary["air:AirSegment"]) {
//     const segs = Array.isArray(itinerary["air:AirSegment"]) ? itinerary["air:AirSegment"] : [itinerary["air:AirSegment"]];
//     segs.forEach(seg => {
//       const key = (seg?.$?.Key || seg.Key || "").trim();
//       if (key) itinerarySegments[key] = seg;
//     });
//   }

//   // Collect solutions from ALL possible tags
//   const pricingSolutions = rsp["air:AirPricingSolution"] ? (Array.isArray(rsp["air:AirPricingSolution"]) ? rsp["air:AirPricingSolution"] : [rsp["air:AirPricingSolution"]]) : [];
//   const pricePoints = (rsp["air:AirPricePointList"] && rsp["air:AirPricePointList"]["air:AirPricePoint"]) ? (Array.isArray(rsp["air:AirPricePointList"]["air:AirPricePoint"]) ? rsp["air:AirPricePointList"]["air:AirPricePoint"] : [rsp["air:AirPricePointList"]["air:AirPricePoint"]]) : [];
  
//   const allSolutions = [...pricingSolutions, ...pricePoints];
//   const filterable = [];

//   allSolutions.forEach(sol => {
//     if (!sol) return;

//     // Use a Set to ensure we only count each unique segment once (fixes pax duplication issue)
//     const segmentKeysSet = new Set();
    
//     // Check top-level refs
//     if (sol["air:AirSegmentRef"]) {
//       const refs = Array.isArray(sol["air:AirSegmentRef"]) ? sol["air:AirSegmentRef"] : [sol["air:AirSegmentRef"]];
//       refs.forEach(r => segmentKeysSet.add((r?.$?.Key || r.Key || "").trim()));
//     }
    
//     if (sol["air:Journey"]) {
//       const journeys = Array.isArray(sol["air:Journey"]) ? sol["air:Journey"] : [sol["air:Journey"]];
//       journeys.forEach(j => {
//           if (j["air:AirSegmentRef"]) {
//              const jRefs = Array.isArray(j["air:AirSegmentRef"]) ? j["air:AirSegmentRef"] : [j["air:AirSegmentRef"]];
//              jRefs.forEach(r => segmentKeysSet.add((r?.$?.Key || r.Key || "").trim()));
//           }
//       });
//     }

//     // Fallback: deep dive into PricingInfo -> BookingInfo
//     if (segmentKeysSet.size === 0 && sol["air:AirPricingInfo"]) {
//         let pricingInfos = Array.isArray(sol["air:AirPricingInfo"]) ? sol["air:AirPricingInfo"] : [sol["air:AirPricingInfo"]];
//         pricingInfos.forEach(api => {
//             if (api["air:BookingInfo"]) {
//                 let bookingInfos = Array.isArray(api["air:BookingInfo"]) ? api["air:BookingInfo"] : [api["air:BookingInfo"]];
//                 bookingInfos.forEach(bi => {
//                     const sRef = (bi?.$?.SegmentRef || bi.SegmentRef || "").trim();
//                     if (sRef) segmentKeysSet.add(sRef);
//                 });
//             }
//         });
//     }

//     const segments = Array.from(segmentKeysSet)
//       .map(k => itinerarySegments[k.trim()])
//       .filter(Boolean)
//       .sort((a,b) => {
//         const tA = new Date(a?.$?.DepartureTime || a.DepartureTime || 0);
//         const tB = new Date(b?.$?.DepartureTime || b.DepartureTime || 0);
//         return tA - tB;
//       });

//     if (segments.length === 0) return;

//     const firstSeg = segments[0];
//     const lastSeg = segments[segments.length - 1];
//     const depStr = firstSeg?.$?.DepartureTime || firstSeg?.DepartureTime;
//     const arrStr = lastSeg?.$?.ArrivalTime || lastSeg?.ArrivalTime;
    
//     if (!depStr || !arrStr) return;
//     const totalPriceStr = sol?.$?.TotalPrice || sol.TotalPrice || "0";
//     const totalFareNum = parseFloat(String(totalPriceStr).replace(/[^\d.]/g, ""));
//     const carrier = (firstSeg?.$?.Carrier || firstSeg?.Carrier || "").toUpperCase();
//     const allCarriers = [...new Set(segments.map(s => (s?.$?.Carrier || s.Carrier || "").toUpperCase()).filter(Boolean))];

//     filterable.push({
//       id: (sol?.$?.Key || sol.Key || "").trim(),
//       _meta: {
//         totalFare: totalFareNum,
//         stops: segments.length - 1,
//         departureTime: new Date(depStr),
//         duration: Math.round((new Date(arrStr) - new Date(depStr)) / 60000),
//         carrier,
//         allCarriers,
//         departureHourSlot: getHourSlot(depStr),
//         arrivalHourSlot: getHourSlot(arrStr)
//       }
//     });
//   });

//   return filterable;
// };

// exports.buildFilterMetadata = (filterable) => {
//   if (!filterable || !filterable.length) return null;

//   let minPrice = Infinity;
//   let maxPrice = -Infinity;
//   const stopsMap = {}, depTimeMap = {}, arrTimeMap = {}, airlineMap = {};

//   filterable.forEach(f => {
//     const m = f._meta;
//     if (m.totalFare < minPrice) minPrice = m.totalFare;
//     if (m.totalFare > maxPrice) maxPrice = m.totalFare;

//     const stopId = m.stops;
//     if (!stopsMap[stopId]) stopsMap[stopId] = { id: stopId, label: stopId === 0 ? "Non-Stop" : `${stopId} Stop(s)`, count: 0 };
//     stopsMap[stopId].count++;

//     const depSlot = m.departureHourSlot;
//     if (!depTimeMap[depSlot]) depTimeMap[depSlot] = { id: depSlot, label: getHourSlotLabel(depSlot), count: 0 };
//     depTimeMap[depSlot].count++;

//     const arrSlot = m.arrivalHourSlot;
//     if (!arrTimeMap[arrSlot]) arrTimeMap[arrSlot] = { id: arrSlot, label: getHourSlotLabel(arrSlot), count: 0 };
//     arrTimeMap[arrSlot].count++;

//     if (!airlineMap[m.carrier]) airlineMap[m.carrier] = { id: m.carrier, name: getAirlineName(m.carrier), count: 0 };
//     airlineMap[m.carrier].count++;
//   });

//   return {
//     priceRange: { min: minPrice, max: maxPrice },
//     stops: Object.values(stopsMap).sort((a,b) => a.id - b.id),
//     departureTimeSlots: Object.values(depTimeMap),
//     arrivalTimeSlots: Object.values(arrTimeMap),
//     airlines: Object.values(airlineMap).sort((a,b) => a.id.localeCompare(b.id))
//   };
// };

// exports.applyFiltersToKeys = (filterable, query) => {
//   let results = [...filterable];

//   // Price (Handles: "5000", "INR5000", "5,000", etc)
//   if (query.minPrice) {
//     const min = parseFloat(String(query.minPrice).replace(/[^\d.]/g, ""));
//     if (!isNaN(min)) results = results.filter(f => f._meta.totalFare >= min);
//   }
//   if (query.maxPrice) {
//     const max = parseFloat(String(query.maxPrice).replace(/[^\d.]/g, ""));
//     if (!isNaN(max)) results = results.filter(f => f._meta.totalFare <= max);
//   }

//   // Stops (Handles aliases: stops, stop)
//   const stopsQuery = query.stops !== undefined ? query.stops : query.stop;
//   if (stopsQuery !== undefined && stopsQuery !== "") {
//     const stopsArr = String(stopsQuery).split(',').map(s => {
//       const val = s.trim().toLowerCase();
//       if (val === 'nonstop' || val === '0') return 0;
//       if (val === '1stop' || val === '1') return 1;
//       if (val === '2+' || val === '2stops' || val === '2') return 2;
//       return parseInt(val);
//     }).filter(s => !isNaN(s));

//     if (stopsArr.length > 0) {
//       results = results.filter(f => {
//         return stopsArr.some(s => (s >= 2 ? f._meta.stops >= 2 : f._meta.stops === s));
//       });
//     }
//   }

//   // Airlines (Handles aliases: airlines, airline)
//   const airlineQuery = query.airlines || query.airline;
//   if (airlineQuery) {
//     const input = String(airlineQuery).split(',').map(c => c.trim().toUpperCase());
//     const airlineNameToCode = {};
//     Object.entries(AIRLINES).forEach(([code, name]) => {
//         airlineNameToCode[name.toUpperCase()] = code;
//         const firstWord = name.split(' ')[0].toUpperCase();
//         if (!airlineNameToCode[firstWord]) airlineNameToCode[firstWord] = code;
//     });

//     const targetCodes = input.map(c => airlineNameToCode[c] || c);
//     results = results.filter(f => {
//         const flightAirlines = f._meta.allCarriers;
//         return flightAirlines.some(code => targetCodes.includes(code));
//     });
//   }

//   // Time Slot Normalization (Strictly mappings labels to internal slot keys)
//   const normSlot = (t) => {
//     if (!t) return "";
//     const v = String(t).toLowerCase().replace(/\s/g, ""); // Remove all spaces for robust matching
    
//     // Handles: "Before 6 AM", "before6", "before6am"
//     if (v.includes("before6") || v === "b6") return "before6";
//     // Handles: "6 AM - 12 PM", "6am to 12pm", "6to12", "6am-12pm"
//     if (v.includes("6to12") || (v.includes("6am") && v.includes("12pm"))) return "6to12";
//     // Handles: "12 PM - 6 PM", "12pm to 6pm", "12to18", "12pm-6pm"
//     if (v.includes("12to18") || (v.includes("12pm") && v.includes("6pm") && !v.includes("before"))) return "12to18";
//     // Handles: "After 6 PM", "after6", "after6pm", "6pm-12am"
//     if (v.includes("after6") || (v.includes("6pm") && (v.includes("12am") || v.includes("after")))) return "after18";
    
//     // Exact internal key match fallback
//     if (v === "before6" || v === "6to12" || v === "12to18" || v === "after18") return v;
    
//     return t.trim();
//   };

//   if (query.departureTimes || query.departureTime) {
//     const val = query.departureTimes || query.departureTime;
//     const slots = String(val).split(',').map(normSlot);
//     results = results.filter(f => slots.includes(f._meta.departureHourSlot));
//   }

//   if (query.arrivalTimes || query.arrivalTime) {
//     const val = query.arrivalTimes || query.arrivalTime;
//     const slots = String(val).split(',').map(normSlot);
//     results = results.filter(f => slots.includes(f._meta.arrivalHourSlot));
//   }

//   // Sort
//   const sortBy = query.sortBy || 'price';
//   if (sortBy === 'price') results.sort((a, b) => a._meta.totalFare - b._meta.totalFare);
//   else if (sortBy === 'duration') results.sort((a, b) => a._meta.duration - b._meta.duration);
//   else if (sortBy === 'departure') results.sort((a, b) => a._meta.departureTime - b._meta.departureTime);

//   return results.map(f => f.id);
// };

// // ─────────────────────────────────────────────────────────────
// // RECONSTRUCTOR
// // ─────────────────────────────────────────────────────────────
// exports.reconstructFilteredResponse = (travelportResponse, matchingKeys) => {
//   const response = JSON.parse(JSON.stringify(travelportResponse));
//   const keysSet = new Set(matchingKeys.map(k => String(k).trim()));
  
//   let body = response;
//   if (response["SOAP:Envelope"]) body = response["SOAP:Envelope"]["SOAP:Body"] || {};
//   else if (response["soapenv:Envelope"]) body = response["soapenv:Envelope"]["soapenv:Body"] || {};

//   const rsp = body["air:LowFareSearchRsp"] || body["air:AirLowFareSearchRsp"] || body["air:AirSearchRsp"] || body;

//   const segMap = {}, fareMap = {}, brandMap = {};
  
//   // Collect ALL reference data
//   const itinerary = rsp["air:AirItinerary"] || rsp["air:AirSegmentList"];
//   if (itinerary && itinerary["air:AirSegment"]) {
//     const segs = Array.isArray(itinerary["air:AirSegment"]) ? itinerary["air:AirSegment"] : [itinerary["air:AirSegment"]];
//     segs.forEach(s => segMap[(s?.$?.Key || s.Key || "").trim()] = s);
//   }

//   if (rsp["air:FareInfoList"]) {
//      const fareNode = rsp["air:FareInfoList"]["air:AirFareInfo"] || rsp["air:FareInfoList"]["air:FareInfo"];
//      if (fareNode) {
//          const fares = Array.isArray(fareNode) ? fareNode : [fareNode];
//          fares.forEach(f => fareMap[(f?.$?.Key || f.Key || "").trim()] = f);
//      }
//   }

//   if (rsp["air:BrandList"] && rsp["air:BrandList"]["air:Brand"]) {
//      const brands = Array.isArray(rsp["air:BrandList"]["air:Brand"]) ? rsp["air:BrandList"]["air:Brand"] : [rsp["air:BrandList"]["air:Brand"]];
//      brands.forEach(b => brandMap[(b?.$?.Key || b.Key || "").trim()] = b);
//   }

//   // 1. FILTER Pricing Solutions
//   const finalSolutions = [];
//   if (rsp["air:AirPricingSolution"]) {
//     const sols = Array.isArray(rsp["air:AirPricingSolution"]) ? rsp["air:AirPricingSolution"] : [rsp["air:AirPricingSolution"]];
//     rsp["air:AirPricingSolution"] = sols.filter(s => keysSet.has((s?.$?.Key || s.Key || "").trim()));
//     finalSolutions.push(...rsp["air:AirPricingSolution"]);
//   }

//   // 2. FILTER Price Points
//   if (rsp["air:AirPricePointList"] && rsp["air:AirPricePointList"]["air:AirPricePoint"]) {
//     const points = Array.isArray(rsp["air:AirPricePointList"]["air:AirPricePoint"]) ? rsp["air:AirPricePointList"]["air:AirPricePoint"] : [rsp["air:AirPricePointList"]["air:AirPricePoint"]];
//     rsp["air:AirPricePointList"]["air:AirPricePoint"] = points.filter(p => keysSet.has((p?.$?.Key || p.Key || "").trim()));
//     finalSolutions.push(...rsp["air:AirPricePointList"]["air:AirPricePoint"]);
//   }

//   // 3. ENRICH matched solutions and COLLECT used refs (to prune global lists)
//   const usedSegKeys = new Set(), usedFareKeys = new Set(), usedBrandKeys = new Set();
  
//   finalSolutions.forEach(sol => {
//     const solSegs = [], solFares = [], solBrands = [];
//     const solSegKeys = new Set();

//     // 1. Gather segments from multiple possible locations in the solution
//     const collectFromRefs = (refs) => {
//       if (!refs) return;
//       const arr = Array.isArray(refs) ? refs : [refs];
//       arr.forEach(r => {
//         const key = (r?.$?.Key || r.Key || "").trim();
//         if (key && segMap[key] && !solSegKeys.has(key)) {
//           solSegs.push(segMap[key]);
//           solSegKeys.add(key);
//           usedSegKeys.add(key);
//         }
//       });
//     };

//     collectFromRefs(sol["air:AirSegmentRef"]);
//     if (sol["air:Journey"]) {
//       const journeys = Array.isArray(sol["air:Journey"]) ? sol["air:Journey"] : [sol["air:Journey"]];
//       journeys.forEach(j => collectFromRefs(j["air:AirSegmentRef"]));
//     }

//     let pricingInfos = sol["air:AirPricingInfo"];
//     if (pricingInfos) {
//       if (!Array.isArray(pricingInfos)) pricingInfos = [pricingInfos];
//       pricingInfos.forEach(api => {
//         let bookingInfos = api["air:BookingInfo"];
//         if (bookingInfos) {
//           if (!Array.isArray(bookingInfos)) bookingInfos = [bookingInfos];
//           bookingInfos.forEach(bi => {
//             const sRef = (bi?.$?.SegmentRef || bi.SegmentRef || "").trim();
//             const fRef = (bi?.$?.FareInfoRef || bi.FareInfoRef || "").trim();
            
//             if (sRef && segMap[sRef] && !solSegKeys.has(sRef)) {
//               solSegs.push(segMap[sRef]);
//               solSegKeys.add(sRef);
//               usedSegKeys.add(sRef);
//             }
//             if (fRef && fareMap[fRef]) {
//               solFares.push(fareMap[fRef]);
//               usedFareKeys.add(fRef);
//               const bRef = (fareMap[fRef]["air:Brand"]?.$?.Key || fareMap[fRef]["air:Brand"]?.Key || "").trim();
//               if (bRef && brandMap[bRef]) {
//                 solBrands.push(brandMap[bRef]);
//                 usedBrandKeys.add(bRef);
//               }
//             }
//           });
//         }
//       });
//     }

//     // Sort segments by time for this specific solution to ensure logical order
//     solSegs.sort((a,b) => {
//       const tA = new Date(a?.$?.DepartureTime || a.DepartureTime || 0);
//       const tB = new Date(b?.$?.DepartureTime || b.DepartureTime || 0);
//       return tA - tB;
//     });

//     sol["air:AirSegment"] = solSegs;
//     sol["air:FareInfo"] = solFares;
//     sol["air:Brand"] = solBrands;
//   });

//   // 4. PRUNE Global Lists (to prevent unfiltered data bleeding through)
//   if (itinerary && itinerary["air:AirSegment"]) {
//       const allSegs = Array.isArray(itinerary["air:AirSegment"]) ? itinerary["air:AirSegment"] : [itinerary["air:AirSegment"]];
//       itinerary["air:AirSegment"] = allSegs.filter(s => usedSegKeys.has((s?.$?.Key || s.Key || "").trim()));
//   }
  
//   if (rsp["air:FareInfoList"]) {
//       const fareNodeName = rsp["air:FareInfoList"]["air:AirFareInfo"] ? "air:AirFareInfo" : "air:FareInfo";
//       const allFares = Array.isArray(rsp["air:FareInfoList"][fareNodeName]) ? rsp["air:FareInfoList"][fareNodeName] : [rsp["air:FareInfoList"][fareNodeName]];
//       rsp["air:FareInfoList"][fareNodeName] = allFares.filter(f => usedFareKeys.has((f?.$?.Key || f.Key || "").trim()));
//   }

//   return response;
// };



//working low fare saech without filters 


"use strict";

// ─────────────────────────────────────────────────────────────
// CONSTANTS
// ─────────────────────────────────────────────────────────────
const AIRLINES = {
  AI:   "Air India",
  "6E": "IndiGo",
  SG:   "SpiceJet",
  UK:   "Vistara",
  G8:   "GoAir",
  IX:   "Air India Express",
  QP:   "Akasa Air",
  "9I": "Alliance Air",
  I5:   "AirAsia India",
};

// ─────────────────────────────────────────────────────────────
// HELPERS
// ─────────────────────────────────────────────────────────────

/**
 * Normalise xml2js output to array.
 * xml2js with explicitArray:false returns a plain object for single items
 * and an array for multiple items.  This undoes that.
 */
function toArray(val) {
  if (val == null) return [];
  return Array.isArray(val) ? val : [val];
}

function getAirlineName(code) {
  return AIRLINES[code] || code;
}

function getHourSlot(timeStr) {
  if (!timeStr) return "before6";
  // ALWAYS parse hour directly from the timestamp string.
  // Travelport timestamps are: "2026-08-25T14:15:00.000+05:30"
  // The HH after "T" is the local flight time (IST) — that is what we want.
  // NEVER use new Date(t).getHours() — that converts to SERVER timezone (UTC
  // on most cloud servers), so a 05:00 IST flight becomes UTC 23:30 and gets
  // wrongly classified as "after18" instead of "before6".
  const match = timeStr.match(/T(\d{2}):/);
  if (!match) return "before6";
  const hour = parseInt(match[1], 10);
  if (hour < 6)  return "before6";
  if (hour < 12) return "6to12";
  if (hour < 18) return "12to18";
  return "after18";
}

function getHourSlotLabel(slot) {
  return {
    before6:  "Before 6 AM",
    "6to12":  "6 AM - 12 PM",
    "12to18": "12 PM - 6 PM",
    after18:  "After 6 PM",
  }[slot] || slot;
}

/** Unwrap SOAP envelope to reach LowFareSearchRsp */
function getRsp(data) {
  let body = data;
  if (data["SOAP:Envelope"])         body = data["SOAP:Envelope"]["SOAP:Body"] || {};
  else if (data["soapenv:Envelope"]) body = data["soapenv:Envelope"]["soapenv:Body"] || {};
  return (
    body["air:LowFareSearchRsp"]    ||
    body["air:AirLowFareSearchRsp"] ||
    body["air:AirSearchRsp"]        ||
    body
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// STOPS DETECTION
//
// Travelport returns two different schema layouts depending on the provider:
//
// SCHEMA A — ACH direct (IndiGo):
//   AirPricingSolution
//     └─ air:Journey → air:AirSegmentRef[]   ← count refs here
//     └─ air:Connection                       ← present only when stops > 0
//
// SCHEMA B — 1G GDS (Air India etc.):
//   AirPricingSolution
//     └─ air:AirPricingInfo
//          └─ air:FlightOptionsList → FlightOption → Option → BookingInfo[]
//     └─ air:Connection                       ← present only when stops > 0
//
// KEY RULE: if air:Connection is present on the solution → stops >= 1
// This is the most reliable secondary check across all schema variants.
// ─────────────────────────────────────────────────────────────────────────────

function stopsForSolution(sol) {
  // ── Secondary check first: air:Connection at solution level ──────────────
  // If this element exists, Travelport guarantees there is at least one stop.
  // This overrides any counting ambiguity in the Journey refs.
  const hasConnection = !!sol["air:Connection"];

  // ── Schema A: count refs in air:Journey ──────────────────────────────────
  const journeys = toArray(sol["air:Journey"]);
  if (journeys.length > 0) {
    let totalRefs = 0;
    for (const j of journeys) {
      totalRefs += toArray(j["air:AirSegmentRef"]).length;
    }
    if (totalRefs > 0) {
      const computed = Math.max(0, totalRefs - 1);
      // If Connection is present but Journey says 0, trust Connection → 1
      if (hasConnection && computed === 0) return 1;
      return computed;
    }
  }

  // ── Schema B: FlightOptionsList → Option → BookingInfo[] ─────────────────
  for (const pi of toArray(sol["air:AirPricingInfo"])) {
    for (const fo of toArray(pi?.["air:FlightOptionsList"]?.["air:FlightOption"])) {
      for (const opt of toArray(fo["air:Option"])) {
        const bis = toArray(opt["air:BookingInfo"]);
        if (bis.length > 0) {
          const computed = Math.max(0, bis.length - 1);
          if (hasConnection && computed === 0) return 1;
          return computed;
        }
      }
    }
    // Fallback: BookingInfo directly under AirPricingInfo (some 1G solutions)
    const directBis = toArray(pi["air:BookingInfo"]);
    if (directBis.length > 0) {
      const computed = Math.max(0, directBis.length - 1);
      if (hasConnection && computed === 0) return 1;
      return computed;
    }
  }

  // If Connection present but no BookingInfo found, it must be 1 stop
  if (hasConnection) return 1;
  return 0;
}

// ─────────────────────────────────────────────────────────────────────────────
// SEGMENT KEY COLLECTION
// Collects AirSegment keys (NOT FlightDetails keys) referenced by a solution.
// ─────────────────────────────────────────────────────────────────────────────

function segmentKeysForSolution(sol) {
  const keys = new Set();

  // Schema A: Journey → AirSegmentRef
  for (const j of toArray(sol["air:Journey"])) {
    for (const ref of toArray(j["air:AirSegmentRef"])) {
      const k = (ref?.$?.Key || ref?.Key || "").trim();
      if (k) keys.add(k);
    }
  }

  // Schema B and fallback: BookingInfo → SegmentRef
  for (const pi of toArray(sol["air:AirPricingInfo"])) {
    // Via FlightOptionsList
    for (const fo of toArray(pi?.["air:FlightOptionsList"]?.["air:FlightOption"])) {
      for (const opt of toArray(fo["air:Option"])) {
        for (const bi of toArray(opt["air:BookingInfo"])) {
          const k = (bi?.$?.SegmentRef || bi?.SegmentRef || "").trim();
          if (k) keys.add(k);
        }
      }
    }
    // Direct BookingInfo
    for (const bi of toArray(pi["air:BookingInfo"])) {
      const k = (bi?.$?.SegmentRef || bi?.SegmentRef || "").trim();
      if (k) keys.add(k);
    }
  }

  return keys;
}

// ─────────────────────────────────────────────────────────────────────────────
// MAPS — build lookup tables from the global lists
// ─────────────────────────────────────────────────────────────────────────────

/** key → AirSegment node */
function buildAirSegmentMap(rsp) {
  const map = {};
  const node = rsp["air:AirSegmentList"] || rsp["air:AirItinerary"];
  if (node) {
    for (const seg of toArray(node["air:AirSegment"])) {
      const k = (seg?.$?.Key || seg?.Key || "").trim();
      if (k) map[k] = seg;
    }
  }
  return map;
}

/** key → FlightDetails node */
function buildFlightDetailsMap(rsp) {
  const map = {};
  if (rsp["air:FlightDetailsList"]) {
    for (const fd of toArray(rsp["air:FlightDetailsList"]["air:FlightDetails"])) {
      const k = (fd?.$?.Key || fd?.Key || "").trim();
      if (k) map[k] = fd;
    }
  }
  return map;
}

/**
 * Given a set of AirSegment keys and the AirSegment map,
 * return the set of FlightDetails keys those segments reference.
 *
 * This is the critical fix for Bug 1:
 *   AirSegment.Key  ≠  FlightDetails.Key
 *   The link is:  AirSegment → air:FlightDetailsRef → Key
 */
function flightDetailsKeysFromSegmentKeys(segKeys, airSegmentMap) {
  const fdKeys = new Set();
  for (const sk of segKeys) {
    const seg = airSegmentMap[sk];
    if (!seg) continue;
    const fdRef = seg["air:FlightDetailsRef"];
    if (!fdRef) continue;
    const k = (fdRef?.$?.Key || fdRef?.Key || "").trim();
    if (k) fdKeys.add(k);
  }
  return fdKeys;
}

/**
 * Build a combined segment map from BOTH AirSegmentList and FlightDetailsList.
 * Used for timing/carrier metadata when AirSegmentList is empty (ACH responses).
 */
function buildCombinedSegmentMap(rsp) {
  const map = {};

  // Primary: AirSegmentList
  const segNode = rsp["air:AirSegmentList"] || rsp["air:AirItinerary"];
  if (segNode) {
    for (const seg of toArray(segNode["air:AirSegment"])) {
      const k = (seg?.$?.Key || seg?.Key || "").trim();
      if (k) map[k] = seg;
    }
  }

  // Secondary: FlightDetailsList — used when AirSegmentList is empty (ACH)
  // FlightDetails shares the same key namespace as AirSegmentRef in Journey
  if (Object.keys(map).length === 0 && rsp["air:FlightDetailsList"]) {
    for (const fd of toArray(rsp["air:FlightDetailsList"]["air:FlightDetails"])) {
      const k = (fd?.$?.Key || fd?.Key || "").trim();
      if (k) map[k] = fd;
    }
  }

  return map;
}

// ─────────────────────────────────────────────────────────────────────────────
// PUBLIC: getFilterableFlights
// ─────────────────────────────────────────────────────────────────────────────
exports.getFilterableFlights = (travelportResponse) => {
  if (!travelportResponse) return [];

  const responseData = travelportResponse.data || travelportResponse;
  const rsp          = getRsp(responseData);
  const segmentMap   = buildCombinedSegmentMap(rsp);

  const solutions  = toArray(rsp["air:AirPricingSolution"]);
  const filterable = [];

  for (const sol of solutions) {
    if (!sol) continue;

    const solKey = (sol?.$?.Key || sol?.Key || "").trim();
    if (!solKey) continue;

    // ── stops ────────────────────────────────────────────────
    const stops = stopsForSolution(sol);

    // ── price ────────────────────────────────────────────────
    const totalPriceStr = (sol?.$?.TotalPrice || sol?.TotalPrice || "0");
    const totalFareNum  = parseFloat(String(totalPriceStr).replace(/[^\d.]/g, "")) || 0;

    // ── timing + carrier via segment map ─────────────────────
    const segKeys  = segmentKeysForSolution(sol);
    const segments = Array.from(segKeys)
      .map(k => segmentMap[k])
      .filter(Boolean)
      .sort((a, b) =>
        new Date(a?.$?.DepartureTime || 0) - new Date(b?.$?.DepartureTime || 0)
      );

    const firstSeg = segments[0];
    const lastSeg  = segments[segments.length - 1];
    const depStr   = firstSeg?.$?.DepartureTime || firstSeg?.DepartureTime;
    const arrStr   = lastSeg?.$?.ArrivalTime    || lastSeg?.ArrivalTime;

    // ── carrier ───────────────────────────────────────────────
    let carrier = (firstSeg?.$?.Carrier || firstSeg?.Carrier || "").toUpperCase();
    if (!carrier) {
      const firstPI = toArray(sol["air:AirPricingInfo"])[0];
      const tc = (toArray(firstPI?.["air:TaxInfo"])[0]?.$?.SupplierCode || "").toUpperCase();
      const pc = (firstPI?.$?.ProviderCode || "").toUpperCase();
      carrier  = tc || (pc === "ACH" ? "6E" : pc) || "";
    }

    const allCarriers = segments.length
      ? [...new Set(segments.map(s => (s?.$?.Carrier || s?.Carrier || carrier).toUpperCase()).filter(Boolean))]
      : [carrier].filter(Boolean);

    // ── duration from Journey TravelTime ─────────────────────
    let durationMins = 0;
    const journeyTT  = toArray(sol["air:Journey"])[0]?.$?.TravelTime || "";
    if (journeyTT) {
      const h = parseInt(journeyTT.match(/(\d+)H/)?.[1] || "0");
      const m = parseInt(journeyTT.match(/(\d+)M/)?.[1] || "0");
      durationMins = h * 60 + m;
    } else if (depStr && arrStr) {
      durationMins = Math.round((new Date(arrStr) - new Date(depStr)) / 60000);
    }

    filterable.push({
      id: solKey,
      _meta: {
        totalFare:         totalFareNum,
        stops,
        departureTime:     depStr ? new Date(depStr) : null,
        duration:          durationMins,
        carrier,
        allCarriers,
        departureHourSlot: getHourSlot(depStr),
        arrivalHourSlot:   getHourSlot(arrStr),
      },
    });
  }

  return filterable;
};

// ─────────────────────────────────────────────────────────────────────────────
// PUBLIC: buildFilterMetadata
// ─────────────────────────────────────────────────────────────────────────────
exports.buildFilterMetadata = (filterable) => {
  if (!filterable || !filterable.length) return null;

  let minPrice = Infinity, maxPrice = -Infinity;
  const stopsMap = {}, depTimeMap = {}, arrTimeMap = {}, airlineMap = {};

  for (const f of filterable) {
    const m = f._meta;
    if (m.totalFare < minPrice) minPrice = m.totalFare;
    if (m.totalFare > maxPrice) maxPrice = m.totalFare;

    const sid = m.stops;
    if (!stopsMap[sid]) stopsMap[sid] = { id: sid, label: sid === 0 ? "Non-Stop" : `${sid} Stop(s)`, count: 0 };
    stopsMap[sid].count++;

    const ds = m.departureHourSlot;
    if (!depTimeMap[ds]) depTimeMap[ds] = { id: ds, label: getHourSlotLabel(ds), count: 0 };
    depTimeMap[ds].count++;

    const as = m.arrivalHourSlot;
    if (!arrTimeMap[as]) arrTimeMap[as] = { id: as, label: getHourSlotLabel(as), count: 0 };
    arrTimeMap[as].count++;

    const c = m.carrier;
    if (c) {
      if (!airlineMap[c]) airlineMap[c] = { id: c, name: getAirlineName(c), count: 0 };
      airlineMap[c].count++;
    }
  }

  return {
    priceRange:         { min: minPrice, max: maxPrice },
    stops:              Object.values(stopsMap).sort((a, b) => a.id - b.id),
    departureTimeSlots: Object.values(depTimeMap),
    arrivalTimeSlots:   Object.values(arrTimeMap),
    airlines:           Object.values(airlineMap).sort((a, b) => a.id.localeCompare(b.id)),
  };
};

// ─────────────────────────────────────────────────────────────────────────────
// PUBLIC: applyFiltersToKeys
// ─────────────────────────────────────────────────────────────────────────────
exports.applyFiltersToKeys = (filterable, query) => {
  let results = [...filterable];

  const getParam = (...keys) => {
    const qKeys = Object.keys(query);
    for (const key of keys) {
      if (query[key] !== undefined) return query[key];
      const match = qKeys.find(k => k.toLowerCase() === key.toLowerCase());
      if (match !== undefined) return query[match];
    }
    return undefined;
  };

  // ── stops ─────────────────────────────────────────────────
  const stopsQuery = getParam("stops", "stop");
  if (stopsQuery !== undefined && String(stopsQuery).trim() !== "") {
    const stopsArr = String(stopsQuery)
      .split(",")
      .map(s => {
        const v = s.trim().toLowerCase();
        if (v === "nonstop" || v === "non-stop" || v === "0" || v === "0stop") return 0;
        if (v === "1stop" || v === "1" || v === "one" || v === "onestop") return 1;
        if (v === "2+" || v === "2stops" || v === "2") return 2;
        return parseInt(v);
      })
      .filter(s => !isNaN(s));

    if (stopsArr.length > 0) {
      results = results.filter(f =>
        stopsArr.some(s => (s >= 2 ? f._meta.stops >= 2 : f._meta.stops === s))
      );
    }
  }

  // ── price ─────────────────────────────────────────────────
  if (query.minPrice) {
    const min = parseFloat(String(query.minPrice).replace(/[^\d.]/g, ""));
    if (!isNaN(min)) results = results.filter(f => f._meta.totalFare >= min);
  }
  if (query.maxPrice) {
    const max = parseFloat(String(query.maxPrice).replace(/[^\d.]/g, ""));
    if (!isNaN(max)) results = results.filter(f => f._meta.totalFare <= max);
  }

  // ── airline ───────────────────────────────────────────────
  const airlineQuery = getParam("airlines", "airline");
  if (airlineQuery) {
    const input = String(airlineQuery).split(",").map(c => c.trim().toUpperCase());
    const nameToCode = {};
    Object.entries(AIRLINES).forEach(([code, name]) => {
      nameToCode[name.toUpperCase()] = code;
      const first = name.split(" ")[0].toUpperCase();
      if (!nameToCode[first]) nameToCode[first] = code;
    });
    const targetCodes = input.map(c => nameToCode[c] || c);
    results = results.filter(f =>
      f._meta.allCarriers.some(code => targetCodes.includes(code))
    );
  }

  // ── time slots ────────────────────────────────────────────
  // normSlot: maps any user-supplied format to an internal slot key.
  // Handles all of: "Before 6 AM", "before6", "6 AM - 12 PM", "6to12",
  // "12 PM - 6 PM", "12to18", "After 6 PM", "after18", "after6pm" etc.
  const normSlot = (t) => {
    if (!t) return "";
    const v = String(t).toLowerCase().replace(/\s+/g, "");

    // before6: "Before 6 AM", "before6", "b6", "0to6"
    if (v === "before6" || v === "b6" || v === "0to6" ||
        v.includes("before6") || v.includes("before6am"))     return "before6";

    // 6to12: "6 AM - 12 PM", "6to12", "6am-12pm", "6am12pm"
    if (v === "6to12" || v.includes("6to12") ||
        v === "6am-12pm" || v === "6am12pm" ||
        (v.includes("6am") && v.includes("12pm")))             return "6to12";

    // 12to18: "12 PM - 6 PM", "12to18", "12pm-6pm"
    if (v === "12to18" || v.includes("12to18") ||
        v === "12pm-6pm" || v === "12pm6pm" ||
        (v.includes("12pm") && v.includes("6pm")))             return "12to18";

    // after18: "After 6 PM", "after18", "after6pm", "6pm+", "evening"
    if (v === "after18" || v.includes("after18") ||
        v.includes("after6pm") || v.includes("after6") ||
        v === "6pm+" || v === "18to24" || v === "evening")     return "after18";

    return ""; // unrecognised input — filter will skip silently
  };

  const depTimeQuery = getParam("departureTimes", "departureTime");
  if (depTimeQuery) {
    const slots = String(depTimeQuery).split(",").map(normSlot);
    results = results.filter(f => slots.includes(f._meta.departureHourSlot));
  }

  const arrTimeQuery = getParam("arrivalTimes", "arrivalTime");
  if (arrTimeQuery) {
    const slots = String(arrTimeQuery).split(",").map(normSlot);
    results = results.filter(f => slots.includes(f._meta.arrivalHourSlot));
  }

  // ── sort ──────────────────────────────────────────────────
  const sortBy = query.sortBy || "price";
  if      (sortBy === "price")     results.sort((a, b) => a._meta.totalFare - b._meta.totalFare);
  else if (sortBy === "duration")  results.sort((a, b) => a._meta.duration  - b._meta.duration);
  else if (sortBy === "departure") results.sort((a, b) => a._meta.departureTime - b._meta.departureTime);

  return results.map(f => f.id);
};

// ─────────────────────────────────────────────────────────────────────────────
// PUBLIC: reconstructFilteredResponse
//
// BUG 1 FIX — FlightDetailsList pruning:
//   OLD (broken): pruned FlightDetails using AirSegment keys → always empty
//     because AirSegment.Key ≠ FlightDetails.Key
//
//   NEW (correct):
//     1. Collect AirSegment keys from matched solutions
//     2. Look up each AirSegment in AirSegmentList
//     3. Read AirSegment → air:FlightDetailsRef → Key
//     4. Use THOSE keys to prune FlightDetailsList
// ─────────────────────────────────────────────────────────────────────────────
exports.reconstructFilteredResponse = (travelportResponse, matchingKeys) => {
  const response     = JSON.parse(JSON.stringify(travelportResponse));
  const responseData = response.data || response;
  const rsp          = getRsp(responseData);
  const keysSet      = new Set(matchingKeys.map(k => String(k).trim()));

  // Build lookup maps on the CLONED data
  const airSegmentMap    = buildAirSegmentMap(rsp);
  const flightDetailsMap = buildFlightDetailsMap(rsp);

  // ── 1. Filter AirPricingSolution to matched only ──────────
  const allSols = toArray(rsp["air:AirPricingSolution"]);
  const matched = allSols.filter(s =>
    keysSet.has((s?.$?.Key || s?.Key || "").trim())
  );
  rsp["air:AirPricingSolution"] = matched;

  // ── 2. Collect AirSegment keys used by matched solutions ──
  const usedAirSegmentKeys = new Set();
  for (const sol of matched) {
    for (const k of segmentKeysForSolution(sol)) {
      usedAirSegmentKeys.add(k);
    }
  }

  // ── 3. Derive FlightDetails keys from those AirSegments ───
  //   This is the KEY FIX for Bug 1.
  //   AirSegment → air:FlightDetailsRef → Key
  const usedFlightDetailKeys = new Set();
  for (const segKey of usedAirSegmentKeys) {
    const seg = airSegmentMap[segKey];
    if (!seg) continue;
    const fdRef = seg["air:FlightDetailsRef"];
    if (!fdRef) continue;
    const k = (fdRef?.$?.Key || fdRef?.Key || "").trim();
    if (k) usedFlightDetailKeys.add(k);
  }

  // If no AirSegment→FlightDetailsRef links found (ACH schema where
  // Journey refs point directly into FlightDetailsList), treat the
  // AirSegment keys AS FlightDetails keys directly.
  if (usedFlightDetailKeys.size === 0 && usedAirSegmentKeys.size > 0) {
    for (const k of usedAirSegmentKeys) {
      if (flightDetailsMap[k]) usedFlightDetailKeys.add(k);
    }
  }

  // ── 4. Prune AirSegmentList ───────────────────────────────
  const segListNode = rsp["air:AirSegmentList"] || rsp["air:AirItinerary"];
  if (segListNode) {
    const all = toArray(segListNode["air:AirSegment"]);
    segListNode["air:AirSegment"] = all.filter(s =>
      usedAirSegmentKeys.has((s?.$?.Key || s?.Key || "").trim())
    );
  }

  // ── 5. Prune FlightDetailsList using CORRECT keys ─────────
  if (rsp["air:FlightDetailsList"]) {
    const all = toArray(rsp["air:FlightDetailsList"]["air:FlightDetails"]);
    rsp["air:FlightDetailsList"]["air:FlightDetails"] = all.filter(fd =>
      usedFlightDetailKeys.has((fd?.$?.Key || fd?.Key || "").trim())
    );
  }

  // ── 6. Prune FareInfoList ─────────────────────────────────
  if (rsp["air:FareInfoList"]) {
    const usedFareKeys = new Set();
    for (const sol of matched) {
      for (const pi of toArray(sol["air:AirPricingInfo"])) {
        for (const ref of toArray(pi["air:FareInfoRef"])) {
          const k = (ref?.$?.Key || ref?.Key || "").trim();
          if (k) usedFareKeys.add(k);
        }
        for (const bi of toArray(pi["air:BookingInfo"])) {
          const k = (bi?.$?.FareInfoRef || "").trim();
          if (k) usedFareKeys.add(k);
        }
      }
    }
    const fareNodeName = rsp["air:FareInfoList"]["air:FareInfo"] ? "air:FareInfo" : "air:AirFareInfo";
    const allFares = toArray(rsp["air:FareInfoList"][fareNodeName]);
    if (allFares.length) {
      rsp["air:FareInfoList"][fareNodeName] = allFares.filter(f =>
        usedFareKeys.has((f?.$?.Key || f?.Key || "").trim())
      );
    }
  }

  return response;
};

// Export helpers
exports.toArray = toArray;
exports.getRsp  = getRsp;




"use strict";

// ─────────────────────────────────────────────────────────────────────────────
// CONSTANTS
// ─────────────────────────────────────────────────────────────────────────────
// const AIRLINES = {
//   AI:   "Air India",
//   "6E": "IndiGo",
//   SG:   "SpiceJet",
//   UK:   "Vistara",
//   G8:   "GoAir",
//   IX:   "Air India Express",
//   QP:   "Akasa Air",
//   "9I": "Alliance Air",
//   I5:   "AirAsia India",
// };

// const AIRCRAFT_NAMES = {
//   "32A": "Airbus A320", "319": "Airbus A319", "320": "Airbus A320",
//   "321": "Airbus A321", "32N": "Airbus A320neo", "32Q": "Airbus A321neo",
//   "32S": "Airbus A320", "333": "Airbus A330-300", "338": "Airbus A330-800",
//   "339": "Airbus A330-900", "351": "Airbus A350-1000", "359": "Airbus A350-900",
//   "380": "Airbus A380-800", "388": "Airbus A380",
//   "737": "Boeing 737", "738": "Boeing 737-800", "739": "Boeing 737-900",
//   "73G": "Boeing 737-700", "73H": "Boeing 737-800", "73W": "Boeing 737-700",
//   "7M8": "Boeing 737 MAX 8", "7M9": "Boeing 737 MAX 9", "747": "Boeing 747",
//   "777": "Boeing 777", "772": "Boeing 777-200", "773": "Boeing 777-300",
//   "77W": "Boeing 777-300ER", "787": "Boeing 787 Dreamliner",
//   "788": "Boeing 787-8", "789": "Boeing 787-9", "78J": "Boeing 787-10",
//   "AT7": "ATR 72", "ATR": "ATR 42/72", "AT4": "ATR 42", "AT5": "ATR 42-500",
//   "DH4": "Bombardier Dash 8", "E75": "Embraer E-175", "E90": "Embraer E-190",
//   "E95": "Embraer E-195", "CRJ": "Bombardier CRJ",
// };

// const CABIN_CLASSES = {
//   ECONOMY:         "Economy",
//   PREMIUM_ECONOMY: "Premium Economy",
//   BUSINESS:        "Business",
//   FIRST:           "First Class",
// };

// // ─────────────────────────────────────────────────────────────────────────────
// // HELPERS
// // ─────────────────────────────────────────────────────────────────────────────
// function toArray(val) { if (val == null) return []; return Array.isArray(val) ? val : [val]; }
// function getAirlineName(code) { return AIRLINES[code] || code; }
// function getAircraftName(code) {
//   if (!code) return "";
//   const key = String(code).toUpperCase().trim();
//   return AIRCRAFT_NAMES[key] || `Aircraft ${key}`;
// }
// function normalizeCabinClass(raw) {
//   if (!raw) return "";
//   const v = String(raw).toLowerCase().replace(/[\s_-]/g, "");
//   if (v.includes("premiumeconomy"))  return "PREMIUM_ECONOMY";
//   if (v.includes("premiumbusiness")) return "BUSINESS";
//   if (v.includes("premiumfirst"))    return "FIRST";
//   if (v.includes("business"))        return "BUSINESS";
//   if (v === "first" || v.includes("first")) return "FIRST";
//   if (v.includes("economy"))         return "ECONOMY";
//   return raw.toUpperCase();
// }
// function getCabinClassLabel(code) { return CABIN_CLASSES[code] || code; }
// function getHourSlot(timeStr) {
//   if (!timeStr) return null;
//   const match = timeStr.match(/T(\d{2}):/);
//   if (!match) return null;
//   const hour = parseInt(match[1], 10);
//   if (hour < 6) return "before6";
//   if (hour < 12) return "6to12";
//   if (hour < 18) return "12to18";
//   return "after18";
// }
// function getHourSlotLabel(slot) {
//   return { before6: "Before 6 AM", "6to12": "6 AM - 12 PM",
//           "12to18": "12 PM - 6 PM", after18: "After 6 PM" }[slot] || slot;
// }
// function parseDurationToMinutes(travelTime) {
//   if (!travelTime) return 0;
//   const s = String(travelTime);
//   const d = parseInt(s.match(/(\d+)D/)?.[1] || "0");
//   const h = parseInt(s.match(/(\d+)H/)?.[1] || "0");
//   const m = parseInt(s.match(/(\d+)M/)?.[1] || "0");
//   return d * 1440 + h * 60 + m;
// }
// function getRsp(data) {
//   let body = data;
//   if (data["SOAP:Envelope"])          body = data["SOAP:Envelope"]["SOAP:Body"] || {};
//   else if (data["soapenv:Envelope"])  body = data["soapenv:Envelope"]["soapenv:Body"] || {};
//   else if (data["soap:Envelope"])     body = data["soap:Envelope"]["soap:Body"]       || {};
//   return body["air:LowFareSearchRsp"] || body["air:AirLowFareSearchRsp"] ||
//          body["air:AirSearchRsp"]     || body;
// }

// // ─────────────────────────────────────────────────────────────────────────────
// // MAP BUILDERS
// // ─────────────────────────────────────────────────────────────────────────────
// function buildAirSegmentMap(rsp) {
//   const map = {};
//   const node = rsp["air:AirSegmentList"] || rsp["air:AirItinerary"];
//   if (!node) return map;
//   for (const seg of toArray(node["air:AirSegment"])) {
//     const k = (seg?.$?.Key || seg?.Key || "").trim();
//     if (k) map[k] = seg;
//   }
//   return map;
// }
// function buildFlightDetailsMap(rsp) {
//   const map = {};
//   const node = rsp["air:FlightDetailsList"];
//   if (!node) return map;
//   for (const fd of toArray(node["air:FlightDetails"])) {
//     const k = (fd?.$?.Key || fd?.Key || "").trim();
//     if (k) map[k] = fd;
//   }
//   return map;
// }

// function segmentKeysForSolution(sol) {
//   const keys = new Set();
//   for (const j of toArray(sol["air:Journey"])) {
//     for (const ref of toArray(j["air:AirSegmentRef"])) {
//       const k = (ref?.$?.Key || ref?.Key || "").trim();
//       if (k) keys.add(k);
//     }
//   }
//   for (const pi of toArray(sol["air:AirPricingInfo"])) {
//     for (const fo of toArray(pi?.["air:FlightOptionsList"]?.["air:FlightOption"])) {
//       for (const opt of toArray(fo["air:Option"])) {
//         for (const bi of toArray(opt["air:BookingInfo"])) {
//           const k = (bi?.$?.SegmentRef || bi?.SegmentRef || "").trim();
//           if (k) keys.add(k);
//         }
//       }
//     }
//     for (const bi of toArray(pi["air:BookingInfo"])) {
//       const k = (bi?.$?.SegmentRef || bi?.SegmentRef || "").trim();
//       if (k) keys.add(k);
//     }
//   }
//   return keys;
// }

// // ─────────────────────────────────────────────────────────────────────────────
// // EXTRACT LEG DATA FROM AirPricingInfo.FlightOptionsList  (BookingInfo fallback)
// //
// // Used when AirSegmentList is empty but solution has BookingInfo data.
// // This is the ACH (IndiGo) schema path.
// //
// // Returns array of legs, each with { stops, carriers, aircraft, cabinClasses, ...}
// // ─────────────────────────────────────────────────────────────────────────────
// function extractLegsFromBookingInfo(sol) {
//   const legsByOriginDest = new Map(); // key: "ORIG-DEST" → leg data

//   for (const pi of toArray(sol["air:AirPricingInfo"])) {
//     for (const fo of toArray(pi?.["air:FlightOptionsList"]?.["air:FlightOption"])) {
//       const origin = fo?.$?.Origin;
//       const dest   = fo?.$?.Destination;
//       const legKey = `${origin}-${dest}`;

//       // Pick the FIRST option (cheapest/best) for this leg
//       const opts = toArray(fo["air:Option"]);
//       if (opts.length === 0) continue;
//       const opt = opts[0];

//       const bookingInfos = toArray(opt["air:BookingInfo"]);
//       const legCarriers  = new Set();
//       const legAircraft  = new Set();
//       const legCabins    = new Set();

//       for (const bi of bookingInfos) {
//         const a = bi?.$ || bi || {};
//         if (a.CabinClass) legCabins.add(normalizeCabinClass(a.CabinClass));
//         // Some responses put aircraft in BookingInfo.Equipment
//         if (a.Equipment) legAircraft.add(String(a.Equipment).toUpperCase());
//       }

//       // Travel time from the Option
//       const travelTime = opt?.$?.TravelTime;
//       const duration   = travelTime ? parseDurationToMinutes(travelTime) : 0;

//       legsByOriginDest.set(legKey, {
//         stops:        Math.max(0, bookingInfos.length - 1),
//         carriers:     Array.from(legCarriers),
//         carrier:      "",
//         aircraft:     Array.from(legAircraft),
//         cabinClasses: Array.from(legCabins),
//         depTime:      null, arrTime: null, depSlot: null, arrSlot: null,
//         duration,
//         origin, destination: dest,
//       });
//     }
//   }
//   return Array.from(legsByOriginDest.values());
// }

// // ─────────────────────────────────────────────────────────────────────────────
// // EXTRACT METADATA — tries AirSegmentList first, falls back to BookingInfo
// // ─────────────────────────────────────────────────────────────────────────────
// function extractMetaFromSolution(sol, airSegmentMap, flightDetailsMap) {
//   const journeys = toArray(sol["air:Journey"]);
//   let legs = [];

//   // ── PATH 1: Use Journey → AirSegmentRef → AirSegmentList ───────────────
//   if (journeys.length > 0 && Object.keys(airSegmentMap).length > 0) {
//     // Build cabin class map from solution's BookingInfo
//     const cabinBySegRef = {};
//     for (const pi of toArray(sol["air:AirPricingInfo"])) {
//       for (const bi of toArray(pi["air:BookingInfo"])) {
//         const segRef = (bi?.$?.SegmentRef || "").trim();
//         const cc     = bi?.$?.CabinClass;
//         if (segRef && cc) cabinBySegRef[segRef] = normalizeCabinClass(cc);
//       }
//       for (const fo of toArray(pi?.["air:FlightOptionsList"]?.["air:FlightOption"])) {
//         for (const opt of toArray(fo["air:Option"])) {
//           for (const bi of toArray(opt["air:BookingInfo"])) {
//             const segRef = (bi?.$?.SegmentRef || "").trim();
//             const cc     = bi?.$?.CabinClass;
//             if (segRef && cc) cabinBySegRef[segRef] = normalizeCabinClass(cc);
//           }
//         }
//       }
//     }

//     for (const journey of journeys) {
//       const segRefs = toArray(journey["air:AirSegmentRef"]);
//       const segItems = segRefs
//         .map(ref => ({
//           key: (ref?.$?.Key || ref?.Key || "").trim(),
//           seg: airSegmentMap[(ref?.$?.Key || ref?.Key || "").trim()],
//         }))
//         .filter(x => x.seg)
//         .sort((a, b) => new Date(a.seg?.$?.DepartureTime || 0) - new Date(b.seg?.$?.DepartureTime || 0));

//       if (segItems.length === 0) {
//         legs.push({
//           stops: 0, carriers: [], carrier: "", aircraft: [], cabinClasses: [],
//           depTime: null, arrTime: null, depSlot: null, arrSlot: null, duration: 0,
//         });
//         continue;
//       }

//       const legCarriers = new Set(), legAircraft = new Set(), legCabins = new Set();
//       segItems.forEach(({ key, seg }) => {
//         const c = (seg?.$?.Carrier || "").toUpperCase();
//         if (c) legCarriers.add(c);

//         // Aircraft from segment.Equipment or FlightDetails.Equipment
//         let eq = (seg?.$?.Equipment || "").toUpperCase();
//         if (!eq) {
//           const fdRefs = toArray(seg["air:FlightDetailsRef"]);
//           for (const r of fdRefs) {
//             const fdKey = (r?.$?.Key || r?.Key || "").trim();
//             const fd = flightDetailsMap[fdKey];
//             if (fd?.$?.Equipment) { eq = String(fd.$.Equipment).toUpperCase(); break; }
//           }
//         }
//         if (eq) legAircraft.add(eq);

//         const cabin = cabinBySegRef[key];
//         if (cabin) legCabins.add(cabin);
//       });

//       const dep = segItems[0].seg?.$?.DepartureTime;
//       const arr = segItems[segItems.length - 1].seg?.$?.ArrivalTime;
//       let duration = 0;
//       const jt = journey?.$?.TravelTime;
//       if (jt) duration = parseDurationToMinutes(jt);
//       else if (dep && arr) duration = Math.round((new Date(arr) - new Date(dep)) / 60000);

//       legs.push({
//         stops:        Math.max(0, segItems.length - 1),
//         carriers:     Array.from(legCarriers),
//         carrier:      (segItems[0].seg?.$?.Carrier || "").toUpperCase(),
//         aircraft:     Array.from(legAircraft),
//         cabinClasses: Array.from(legCabins),
//         depTime: dep, arrTime: arr,
//         depSlot: getHourSlot(dep), arrSlot: getHourSlot(arr),
//         duration,
//       });
//     }
//   }

//   // ── PATH 2: FALLBACK — No segments → use FlightOptionsList/BookingInfo ──
//   // Critical for ACH (IndiGo) responses where AirSegmentList is empty.
//   if (legs.length === 0 || legs.every(l => l.stops === 0 && l.carriers.length === 0 && l.aircraft.length === 0)) {
//     const fallbackLegs = extractLegsFromBookingInfo(sol);
//     if (fallbackLegs.length > 0) legs = fallbackLegs;
//   }

//   // Carrier fallback
//   if (legs.length > 0 && legs.every(l => !l.carrier)) {
//     const firstPI = toArray(sol["air:AirPricingInfo"])[0];
//     const plating  = (firstPI?.$?.PlatingCarrier || "").toUpperCase();
//     const provider = (firstPI?.$?.ProviderCode   || "").toUpperCase();
//     const supplier = (toArray(firstPI?.["air:TaxInfo"])[0]?.$?.SupplierCode || "").toUpperCase();
//     const fallback = plating || supplier || (provider === "ACH" ? "6E" : provider) || "";
//     if (fallback) legs.forEach(l => {
//       l.carrier = fallback;
//       if (l.carriers.length === 0) l.carriers = [fallback];
//     });
//   }

//   // Aggregates
//   const outbound  = legs[0] || {};
//   const returnLeg = legs.length > 1 ? legs[legs.length - 1] : null;
//   const allCarriers = new Set(), allAircraft = new Set(), allCabins = new Set();
//   legs.forEach(l => {
//     l.carriers.forEach(c => allCarriers.add(c));
//     l.aircraft.forEach(a => allAircraft.add(a));
//     l.cabinClasses.forEach(c => allCabins.add(c));
//   });

//   return {
//     legs,
//     isRoundTrip:       legs.length > 1,
//     totalStops:        legs.reduce((s, l) => s + l.stops, 0),
//     maxStopsOnAnyLeg:  Math.max(0, ...legs.map(l => l.stops)),
//     totalDuration:     legs.reduce((s, l) => s + l.duration, 0),
//     allCarriers:       Array.from(allCarriers),
//     allAircraft:       Array.from(allAircraft),
//     allCabinClasses:   Array.from(allCabins),
//     carrier:           outbound.carrier || "",
//     depTime:           outbound.depTime || null,
//     arrTime:           returnLeg ? returnLeg.arrTime : outbound.arrTime,
//     departureHourSlot: outbound.depSlot || "before6",
//     arrivalHourSlot:   (returnLeg ? returnLeg.arrSlot : outbound.arrSlot) || "before6",
//     outboundDepSlot:   outbound.depSlot || null,
//     outboundArrSlot:   outbound.arrSlot || null,
//     returnDepSlot:     returnLeg?.depSlot || null,
//     returnArrSlot:     returnLeg?.arrSlot || null,
//   };
// }

// // ─────────────────────────────────────────────────────────────────────────────
// // PUBLIC: getFilterableFlights
// // ─────────────────────────────────────────────────────────────────────────────
// exports.getFilterableFlights = (travelportResponse, paxCount = 1) => {
//   if (!travelportResponse) return [];
//   const responseData     = travelportResponse.data || travelportResponse;
//   const rsp              = getRsp(responseData);
//   const airSegmentMap    = buildAirSegmentMap(rsp);
//   const flightDetailsMap = buildFlightDetailsMap(rsp);
//   const filterable       = [];

//   const processSolution = (sol) => {
//     const key = (sol?.$?.Key || "").trim();
//     if (!key) return;
//     const totalPriceStr = sol?.$?.TotalPrice || "0";
//     const totalFareRaw  = parseFloat(String(totalPriceStr).replace(/[^\d.]/g, "")) || 0;
//     const totalFare     = totalFareRaw / (paxCount || 1);
//     const meta = extractMetaFromSolution(sol, airSegmentMap, flightDetailsMap);

//     filterable.push({
//       id: key,
//       _meta: {
//         totalFare,
//         legs: meta.legs, isRoundTrip: meta.isRoundTrip,
//         totalStops: meta.totalStops, maxStopsOnAnyLeg: meta.maxStopsOnAnyLeg,
//         duration: meta.totalDuration, stops: meta.totalStops,
//         carrier: meta.carrier, allCarriers: meta.allCarriers,
//         allAircraft: meta.allAircraft, allCabinClasses: meta.allCabinClasses,
//         departureTime: meta.depTime ? new Date(meta.depTime) : null,
//         arrivalTime:   meta.arrTime ? new Date(meta.arrTime) : null,
//         departureHourSlot: meta.departureHourSlot,
//         arrivalHourSlot:   meta.arrivalHourSlot,
//         outboundDepSlot: meta.outboundDepSlot, outboundArrSlot: meta.outboundArrSlot,
//         returnDepSlot:   meta.returnDepSlot,   returnArrSlot:   meta.returnArrSlot,
//       },
//     });
//   };

//   for (const sol of toArray(rsp["air:AirPricingSolution"])) processSolution(sol);
//   if (filterable.length === 0) {
//     for (const pp of toArray(rsp?.["air:AirPricePointList"]?.["air:AirPricePoint"])) {
//       processSolution(pp);
//     }
//   }
//   return filterable;
// };

// // ─────────────────────────────────────────────────────────────────────────────
// // PUBLIC: buildFilterMetadata
// // ─────────────────────────────────────────────────────────────────────────────
// exports.buildFilterMetadata = (filterable) => {
//   if (!filterable || !filterable.length) return null;
//   let minPrice = Infinity, maxPrice = -Infinity;
//   const stopsMap = {}, depTimeMap = {}, arrTimeMap = {};
//   const airlineMap = {}, aircraftMap = {}, cabinMap = {};

//   for (const f of filterable) {
//     const m = f._meta;
//     if (m.totalFare < minPrice) minPrice = m.totalFare;
//     if (m.totalFare > maxPrice) maxPrice = m.totalFare;

//     const sid = m.maxStopsOnAnyLeg;
//     if (!stopsMap[sid]) stopsMap[sid] = {
//       id: sid, label: sid === 0 ? "Non-Stop" : `${sid} Stop(s)`, count: 0,
//     };
//     stopsMap[sid].count++;

//     const ds = m.outboundDepSlot || m.departureHourSlot;
//     if (ds) {
//       if (!depTimeMap[ds]) depTimeMap[ds] = { id: ds, label: getHourSlotLabel(ds), count: 0 };
//       depTimeMap[ds].count++;
//     }
//     const as = m.outboundArrSlot || m.arrivalHourSlot;
//     if (as) {
//       if (!arrTimeMap[as]) arrTimeMap[as] = { id: as, label: getHourSlotLabel(as), count: 0 };
//       arrTimeMap[as].count++;
//     }
//     for (const c of (m.allCarriers?.length ? m.allCarriers : [m.carrier].filter(Boolean))) {
//       if (!airlineMap[c]) airlineMap[c] = { id: c, name: getAirlineName(c), count: 0 };
//       airlineMap[c].count++;
//     }
//     for (const ac of (m.allAircraft || [])) {
//       if (!aircraftMap[ac]) aircraftMap[ac] = { id: ac, name: getAircraftName(ac), count: 0 };
//       aircraftMap[ac].count++;
//     }
//     for (const cc of (m.allCabinClasses || [])) {
//       if (!cabinMap[cc]) cabinMap[cc] = { id: cc, label: getCabinClassLabel(cc), count: 0 };
//       cabinMap[cc].count++;
//     }
//   }

//   const timeSlotOrder = ["before6", "6to12", "12to18", "after18"];
//   const cabinOrder    = ["ECONOMY", "PREMIUM_ECONOMY", "BUSINESS", "FIRST"];

//   return {
//     priceRange: { min: minPrice, max: maxPrice },
//     stops:      Object.values(stopsMap).sort((a, b) => a.id - b.id),
//     departureTimeSlots: Object.values(depTimeMap)
//       .sort((a, b) => timeSlotOrder.indexOf(a.id) - timeSlotOrder.indexOf(b.id)),
//     arrivalTimeSlots: Object.values(arrTimeMap)
//       .sort((a, b) => timeSlotOrder.indexOf(a.id) - timeSlotOrder.indexOf(b.id)),
//     airlines: Object.values(airlineMap).sort((a, b) => a.id.localeCompare(b.id)),
//     aircraft: Object.values(aircraftMap).sort((a, b) => a.name.localeCompare(b.name)),
//     cabinClasses: Object.values(cabinMap).sort((a, b) => cabinOrder.indexOf(a.id) - cabinOrder.indexOf(b.id)),
//   };
// };

// // ─────────────────────────────────────────────────────────────────────────────
// // PUBLIC: applyFiltersToKeys
// // ─────────────────────────────────────────────────────────────────────────────
// exports.applyFiltersToKeys = (filterable, query) => {
//   let results = [...filterable];
//   const getParam = (...keys) => {
//     const qKeys = Object.keys(query);
//     for (const key of keys) {
//       if (query[key] !== undefined) return query[key];
//       const match = qKeys.find(k => k.toLowerCase() === key.toLowerCase());
//       if (match !== undefined) return query[match];
//     }
//     return undefined;
//   };

//   // STOPS — uses maxStopsOnAnyLeg for correct RT filtering
//   const stopsQuery = getParam("stops", "stop");
//   if (stopsQuery !== undefined && String(stopsQuery).trim() !== "") {
//     const stopsArr = String(stopsQuery).split(",").map(s => {
//       const v = s.trim().toLowerCase();
//       if (v === "nonstop" || v === "non-stop" || v === "0" || v === "0stop") return 0;
//       if (v === "1stop" || v === "1" || v === "one" || v === "onestop") return 1;
//       if (v === "2+" || v === "2stops" || v === "2plus") return "2+";
//       const n = parseInt(v);
//       return isNaN(n) ? null : n;
//     }).filter(s => s !== null);
//     if (stopsArr.length > 0) {
//       results = results.filter(f => {
//         const maxStops = f._meta.maxStopsOnAnyLeg;
//         return stopsArr.some(s => (s === "2+") ? maxStops >= 2 : maxStops === s);
//       });
//     }
//   }

//   // PRICE
//   if (query.minPrice !== undefined && String(query.minPrice).trim() !== "") {
//     const min = parseFloat(String(query.minPrice).replace(/[^\d.]/g, ""));
//     if (!isNaN(min)) results = results.filter(f => f._meta.totalFare >= min);
//   }
//   if (query.maxPrice !== undefined && String(query.maxPrice).trim() !== "") {
//     const max = parseFloat(String(query.maxPrice).replace(/[^\d.]/g, ""));
//     if (!isNaN(max)) results = results.filter(f => f._meta.totalFare <= max);
//   }

//   // AIRLINE — every leg must be a selected airline
//   const airlineQuery = getParam("airlines", "airline");
//   if (airlineQuery) {
//     const input = String(airlineQuery).split(",").map(c => c.trim().toUpperCase());
//     const nameToCode = {};
//     Object.entries(AIRLINES).forEach(([code, name]) => {
//       nameToCode[name.toUpperCase()] = code;
//       const first = name.split(" ")[0].toUpperCase();
//       if (!nameToCode[first]) nameToCode[first] = code;
//     });
//     const targetCodes = input.map(c => nameToCode[c] || c);
//     results = results.filter(f => {
//       const legs = f._meta.legs;
//       if (!legs || legs.length === 0) {
//         const carriers = f._meta.allCarriers?.length ? f._meta.allCarriers : [f._meta.carrier];
//         return carriers.some(c => targetCodes.includes(c));
//       }
//       return legs.every(leg => {
//         const legCarriers = leg.carriers?.length ? leg.carriers : [leg.carrier];
//         return legCarriers.some(c => targetCodes.includes(c));
//       });
//     });
//   }

//   // AIRCRAFT — ANY leg using selected aircraft
//   const aircraftQuery = getParam("aircraft", "aircrafts");
//   if (aircraftQuery) {
//     const input = String(aircraftQuery).split(",").map(c => c.trim().toUpperCase());
//     const targetCodes = new Set();
//     input.forEach(val => {
//       if (!val) return;
//       if (AIRCRAFT_NAMES[val]) { targetCodes.add(val); return; }
//       let matchedExact = false;
//       Object.entries(AIRCRAFT_NAMES).forEach(([code, name]) => {
//         if (name.toUpperCase() === val) { targetCodes.add(code); matchedExact = true; }
//       });
//       if (matchedExact) return;
//       Object.entries(AIRCRAFT_NAMES).forEach(([code, name]) => {
//         if (name.toUpperCase().includes(val) || code.includes(val)) targetCodes.add(code);
//       });
//       if (targetCodes.size === 0) targetCodes.add(val);
//     });
//     results = results.filter(f => {
//       const ac = f._meta.allAircraft || [];
//       return ac.some(code => targetCodes.has(code));
//     });
//   }

//   // CABIN CLASS — every leg must offer the class
//   const cabinQuery = getParam("cabinClass", "class", "travelClass", "cabin");
//   if (cabinQuery) {
//     const input = String(cabinQuery).split(",").map(c => normalizeCabinClass(c)).filter(Boolean);
//     if (input.length > 0) {
//       results = results.filter(f => {
//         const legs = f._meta.legs;
//         if (!legs || legs.length === 0) {
//           return (f._meta.allCabinClasses || []).some(c => input.includes(c));
//         }
//         return legs.every(leg => {
//           const cabins = leg.cabinClasses || [];
//           return cabins.some(c => input.includes(c));
//         });
//       });
//     }
//   }

//   // TIME SLOTS
//   const normSlot = (t) => {
//     if (!t) return "";
//     const v = String(t).toLowerCase().replace(/\s+/g, "");
//     if (v === "before6" || v.includes("before6")) return "before6";
//     if (v === "6to12" || (v.includes("6am") && v.includes("12pm"))) return "6to12";
//     if (v === "12to18" || (v.includes("12pm") && v.includes("6pm"))) return "12to18";
//     if (v === "after18" || v.includes("after6pm") || v.includes("after18")) return "after18";
//     return "";
//   };
//   const depTimeQuery = getParam("departureTimes", "departureTime");
//   if (depTimeQuery) {
//     const slots = String(depTimeQuery).split(",").map(normSlot).filter(Boolean);
//     if (slots.length) results = results.filter(f => slots.includes(f._meta.outboundDepSlot || f._meta.departureHourSlot));
//   }
//   const arrTimeQuery = getParam("arrivalTimes", "arrivalTime");
//   if (arrTimeQuery) {
//     const slots = String(arrTimeQuery).split(",").map(normSlot).filter(Boolean);
//     if (slots.length) results = results.filter(f => slots.includes(f._meta.outboundArrSlot || f._meta.arrivalHourSlot));
//   }
//   const retDepTimeQuery = getParam("returnDepartureTimes", "returnDepartureTime");
//   if (retDepTimeQuery) {
//     const slots = String(retDepTimeQuery).split(",").map(normSlot).filter(Boolean);
//     if (slots.length) results = results.filter(f => f._meta.returnDepSlot && slots.includes(f._meta.returnDepSlot));
//   }
//   const retArrTimeQuery = getParam("returnArrivalTimes", "returnArrivalTime");
//   if (retArrTimeQuery) {
//     const slots = String(retArrTimeQuery).split(",").map(normSlot).filter(Boolean);
//     if (slots.length) results = results.filter(f => f._meta.returnArrSlot && slots.includes(f._meta.returnArrSlot));
//   }

//   // SORT
//   const sortBy = query.sortBy || "price";
//   if      (sortBy === "price")     results.sort((a, b) => a._meta.totalFare - b._meta.totalFare);
//   else if (sortBy === "duration")  results.sort((a, b) => a._meta.duration  - b._meta.duration);
//   else if (sortBy === "departure") {
//     results.sort((a, b) => {
//       const at = a._meta.departureTime ? a._meta.departureTime.getTime() : Infinity;
//       const bt = b._meta.departureTime ? b._meta.departureTime.getTime() : Infinity;
//       return at - bt;
//     });
//   }

//   return results.map(f => f.id);
// };

// // ─────────────────────────────────────────────────────────────────────────────
// // PUBLIC: reconstructFilteredResponse
// // ─────────────────────────────────────────────────────────────────────────────
// exports.reconstructFilteredResponse = (travelportResponse, matchingKeys) => {
//   const response     = JSON.parse(JSON.stringify(travelportResponse));
//   const responseData = response.data || response;
//   const rsp          = getRsp(responseData);
//   const originalRsp  = getRsp(travelportResponse.data || travelportResponse);
//   const responseMessages = originalRsp["common_v54_0:ResponseMessage"];
//   const keysSet = new Set(matchingKeys.map(k => String(k).trim()));
//   const airSegmentMap    = buildAirSegmentMap(rsp);
//   const flightDetailsMap = buildFlightDetailsMap(rsp);

//   let matchedSolutions = [];
//   if (Array.isArray(rsp["air:AirPricingSolution"])) {
//     matchedSolutions = rsp["air:AirPricingSolution"].filter(s => keysSet.has((s?.$?.Key || "").trim()));
//     rsp["air:AirPricingSolution"] = matchedSolutions;
//   }
//   if (rsp["air:AirPricePointList"]?.["air:AirPricePoint"]) {
//     const all = toArray(rsp["air:AirPricePointList"]["air:AirPricePoint"]);
//     const matched = all.filter(pp => keysSet.has((pp?.$?.Key || "").trim()));
//     rsp["air:AirPricePointList"]["air:AirPricePoint"] = matched;
//     matchedSolutions = matchedSolutions.concat(matched);
//   }

//   const usedSegmentKeys = new Set();
//   for (const sol of matchedSolutions) {
//     for (const k of segmentKeysForSolution(sol)) usedSegmentKeys.add(k);
//   }

//   const usedFlightDetailsKeys = new Set();
//   for (const segKey of usedSegmentKeys) {
//     const seg = airSegmentMap[segKey];
//     if (!seg) continue;
//     for (const fdRef of toArray(seg["air:FlightDetailsRef"])) {
//       const k = (fdRef?.$?.Key || fdRef?.Key || "").trim();
//       if (k) usedFlightDetailsKeys.add(k);
//     }
//   }
//   if (usedFlightDetailsKeys.size === 0 && usedSegmentKeys.size > 0) {
//     for (const k of usedSegmentKeys) if (flightDetailsMap[k]) usedFlightDetailsKeys.add(k);
//   }

//   const usedFareKeys = new Set();
//   for (const sol of matchedSolutions) {
//     for (const pi of toArray(sol["air:AirPricingInfo"])) {
//       for (const ref of toArray(pi["air:FareInfoRef"])) {
//         const k = (ref?.$?.Key || ref?.Key || "").trim();
//         if (k) usedFareKeys.add(k);
//       }
//       for (const bi of toArray(pi["air:BookingInfo"])) {
//         const k = (bi?.$?.FareInfoRef || "").trim();
//         if (k) usedFareKeys.add(k);
//       }
//       for (const fo of toArray(pi?.["air:FlightOptionsList"]?.["air:FlightOption"])) {
//         for (const opt of toArray(fo["air:Option"])) {
//           for (const bi of toArray(opt["air:BookingInfo"])) {
//             const k = (bi?.$?.FareInfoRef || "").trim();
//             if (k) usedFareKeys.add(k);
//           }
//         }
//       }
//     }
//   }

//   const segListNode = rsp["air:AirSegmentList"] || rsp["air:AirItinerary"];
//   if (segListNode) {
//     const all = toArray(segListNode["air:AirSegment"]);
//     segListNode["air:AirSegment"] = all.filter(s => usedSegmentKeys.has((s?.$?.Key || "").trim()));
//   }
//   if (rsp["air:FlightDetailsList"]) {
//     const all = toArray(rsp["air:FlightDetailsList"]["air:FlightDetails"]);
//     rsp["air:FlightDetailsList"]["air:FlightDetails"] = all.filter(fd => usedFlightDetailsKeys.has((fd?.$?.Key || "").trim()));
//   }
//   if (rsp["air:FareInfoList"]) {
//     const fareNodeName = rsp["air:FareInfoList"]["air:FareInfo"] ? "air:FareInfo" : "air:AirFareInfo";
//     const all = toArray(rsp["air:FareInfoList"][fareNodeName]);
//     if (all.length) {
//       rsp["air:FareInfoList"][fareNodeName] = all.filter(f => usedFareKeys.has((f?.$?.Key || "").trim()));
//     }
//   }

//   if (responseMessages) rsp["common_v54_0:ResponseMessage"] = responseMessages;
//   return response;
// };

// exports.toArray = toArray;
// exports.getRsp  = getRsp;
// exports.AIRLINES = AIRLINES;
// exports.AIRCRAFT_NAMES = AIRCRAFT_NAMES;
// exports.CABIN_CLASSES = CABIN_CLASSES;

