



// //updated code with passenger keys 
// "use strict";

// const flightService = require("../services/airPricingServiceUpdated");

// const VALID_PTC_CODES = ["ADT", "CNN", "INF", "CHD", "INS", "MIL", "SEA", "STU"];

// exports.airPrice = async (req, res) => {
//   const traceId = req.body.traceId;

//   if (!traceId) {
//     return res.status(400).json({
//       success: false,
//       message: "traceId is required.",
//     });
//   }

//   console.log(`\n[${traceId}] 🟢 === NEW AIR PRICING REQUEST ===`);
//   console.log(`[${traceId}] 📥 Body:`, JSON.stringify(req.body, null, 2));

//   try {
//     const { segments, passengers } = req.body;

//     // ── Validate segments ──
//     if (!Array.isArray(segments) || segments.length === 0) {
//       return res.status(400).json({
//         success: false,
//         traceId,
//         message: "segments array is required and must not be empty.",
//       });
//     }

//   const requiredSegmentFields = [
//   "segmentKey",
//   "group",
//   "carrier",
//   "flightNumber",
//   "origin",
//   "destination",
//   "departureTime",
//   "arrivalTime",
//   "flightTime",
//   "distance",
//   "equipment",
//   "classOfService",

//   // ✅ NEW REQUIRED FIELDS (as per checklist)
//   "eTicketability",
//   "changeOfPlane",
//   "participantLevel",
//   "linkAvailability",
//   "polledAvailabilityOption",
//   "optionalServicesIndicator",
//   "availabilitySource",
//   "availabilityDisplayType",
// ];
//   for (const [i, seg] of segments.entries()) {

//   for (const field of requiredSegmentFields) {
//     if (
//       seg[field] === undefined ||
//       seg[field] === null ||
//       seg[field] === ""
//     ) {
//       return res.status(400).json({
//         success: false,
//         traceId,
//         message: `segments[${i}] missing field "${field}"`,
//       });
//     }
//   }

//   // ── BASIC TYPE CHECKS ──

//   if (typeof seg.segmentKey !== "string") {
//     return res.status(400).json({
//       success: false,
//       traceId,
//       message: `segments[${i}].segmentKey must be string`,
//     });
//   }

//   if (![0, 1].includes(Number(seg.group))) {
//     return res.status(400).json({
//       success: false,
//       traceId,
//       message: `segments[${i}].group must be 0 or 1`,
//     });
//   }

//   if (isNaN(Number(seg.flightTime))) {
//     return res.status(400).json({
//       success: false,
//       traceId,
//       message: `segments[${i}].flightTime must be number`,
//     });
//   }

//   if (isNaN(Number(seg.distance))) {
//     return res.status(400).json({
//       success: false,
//       traceId,
//       message: `segments[${i}].distance must be number`,
//     });
//   }

//   // ── STRING ACCEPTANCE (IMPORTANT CHANGE) ──

//   const stringFields = [
//     "eTicketability",
//     "changeOfPlane",
//     "participantLevel",
//     "linkAvailability",
//     "polledAvailabilityOption",
//     "optionalServicesIndicator",
//     "availabilitySource",
//     "availabilityDisplayType",
//   ];

//   for (const field of stringFields) {
//     if (typeof seg[field] !== "string") {
//       return res.status(400).json({
//         success: false,
//         traceId,
//         message: `segments[${i}].${field} must be string`,
//       });
//     }
//   }

//   // ── OPTIONAL VALUE VALIDATION (VERY IMPORTANT FOR TRAVELPORT) ──

//   // Accept only valid values (avoid garbage input)

//   if (!["Yes", "No"].includes(seg.eTicketability)) {
//     return res.status(400).json({
//       success: false,
//       traceId,
//       message: `segments[${i}].eTicketability must be "Yes" or "No"`,
//     });
//   }

//   if (!["true", "false"].includes(seg.changeOfPlane)) {
//     return res.status(400).json({
//       success: false,
//       traceId,
//       message: `segments[${i}].changeOfPlane must be "true" or "false"`,
//     });
//   }

//   if (!["true", "false"].includes(seg.linkAvailability)) {
//     return res.status(400).json({
//       success: false,
//       traceId,
//       message: `segments[${i}].linkAvailability must be "true" or "false"`,
//     });
//   }

//   if (!["true", "false"].includes(seg.optionalServicesIndicator)) {
//     return res.status(400).json({
//       success: false,
//       traceId,
//       message: `segments[${i}].optionalServicesIndicator must be "true" or "false"`,
//     });
//   }
// }

//     // ── Call Service ──
//     const result = await flightService.priceFlight(req.body, traceId);

//     if (!result.success) {
//       return res.status(500).json({
//         success: false,
//         traceId,
//         message: "Pricing failed",
//         error: result.error,
//       });
//     }

//     return res.status(200).json({
//       success: true,
//       traceId,
//       attempt: result.attempt || 1,
//       passengerKeys: result.passengerKeys,
//       data: result.data,
//     });

//   } catch (err) {
//     console.error(`[${traceId}] ❌`, err.message);

//     return res.status(500).json({
//       success: false,
//       traceId,
//       message: "Internal error",
//       error: err.message,
//     });
//   }
// };






"use strict";

const flightService = require("../services/airPricingServiceUpdated");

const VALID_PTC_CODES = ["ADT", "CNN", "INF", "CHD", "INS", "MIL", "SEA", "STU"];

// Same discriminator used by the pricing + booking services.
const ACH_CARRIERS = ["6E"];

exports.airPrice = async (req, res) => {
  const traceId = req.body.traceId;

  if (!traceId) {
    return res.status(400).json({
      success: false,
      message: "traceId is required.",
    });
  }

  console.log(`\n[${traceId}] 🟢 === NEW AIR PRICING REQUEST ===`);
  console.log(`[${traceId}] 📥 Body:`, JSON.stringify(req.body, null, 2));

  try {
    const { segments, passengers, bookingRequirements } = req.body;

    // ── Validate segments array ──
    if (!Array.isArray(segments) || segments.length === 0) {
      return res.status(400).json({
        success: false,
        traceId,
        message: "segments array is required and must not be empty.",
      });
    }

    // ── Decide content type: 6E ⇒ ACH, everything else ⇒ 1G ──
    const isACH = segments.some((s) => ACH_CARRIERS.includes(s.carrier));
    console.log(`[${traceId}] 🛫 isACH=${isACH}`);

    // ── Required segment fields (common + path-specific) ──
    const commonRequired = [
      "segmentKey",
      "group",
      "carrier",
      "flightNumber",
      "origin",
      "destination",
      "departureTime",
      "arrivalTime",
      "flightTime",
      "equipment",
      "changeOfPlane",
      "optionalServicesIndicator",
    ];

    // 1G (GDS) availability/fare attributes
    const gdsRequired = [
      "distance",
      "classOfService",
      "eTicketability",
      "participantLevel",
      "linkAvailability",
      "polledAvailabilityOption",
      "availabilitySource",
      "availabilityDisplayType",
    ];

    // ACH (6E) host-bound attributes (ClassOfService comes from
    // bookingRequirements.bookingCode, so it is NOT required on the segment)
    const achRequired = ["status", "supplierCode", "APISRequirementsRef"];

    const requiredSegmentFields = isACH
      ? [...commonRequired, ...achRequired]
      : [...commonRequired, ...gdsRequired];

    for (const [i, seg] of segments.entries()) {
      // ── presence ──
      for (const field of requiredSegmentFields) {
        if (seg[field] === undefined || seg[field] === null || seg[field] === "") {
          return res.status(400).json({
            success: false,
            traceId,
            message: `segments[${i}] missing field "${field}"`,
          });
        }
      }

      // ── basic type checks (both paths) ──
      if (typeof seg.segmentKey !== "string") {
        return res.status(400).json({
          success: false,
          traceId,
          message: `segments[${i}].segmentKey must be string`,
        });
      }

      if (![0, 1].includes(Number(seg.group))) {
        return res.status(400).json({
          success: false,
          traceId,
          message: `segments[${i}].group must be 0 or 1`,
        });
      }

      if (isNaN(Number(seg.flightTime))) {
        return res.status(400).json({
          success: false,
          traceId,
          message: `segments[${i}].flightTime must be number`,
        });
      }

      // changeOfPlane / optionalServicesIndicator are required on both paths
      if (!["true", "false"].includes(seg.changeOfPlane)) {
        return res.status(400).json({
          success: false,
          traceId,
          message: `segments[${i}].changeOfPlane must be "true" or "false"`,
        });
      }

      if (!["true", "false"].includes(seg.optionalServicesIndicator)) {
        return res.status(400).json({
          success: false,
          traceId,
          message: `segments[${i}].optionalServicesIndicator must be "true" or "false"`,
        });
      }

      if (isACH) {
        // ── ACH-specific checks ──
        const achStringFields = ["status", "supplierCode", "APISRequirementsRef"];
        for (const field of achStringFields) {
          if (typeof seg[field] !== "string") {
            return res.status(400).json({
              success: false,
              traceId,
              message: `segments[${i}].${field} must be string`,
            });
          }
        }
      } else {
        // ── 1G-specific checks (unchanged behaviour) ──
        if (isNaN(Number(seg.distance))) {
          return res.status(400).json({
            success: false,
            traceId,
            message: `segments[${i}].distance must be number`,
          });
        }

        const stringFields = [
          "eTicketability",
          "changeOfPlane",
          "participantLevel",
          "linkAvailability",
          "polledAvailabilityOption",
          "optionalServicesIndicator",
          "availabilitySource",
          "availabilityDisplayType",
        ];
        for (const field of stringFields) {
          if (typeof seg[field] !== "string") {
            return res.status(400).json({
              success: false,
              traceId,
              message: `segments[${i}].${field} must be string`,
            });
          }
        }

        if (!["Yes", "No"].includes(seg.eTicketability)) {
          return res.status(400).json({
            success: false,
            traceId,
            message: `segments[${i}].eTicketability must be "Yes" or "No"`,
          });
        }

        if (!["true", "false"].includes(seg.linkAvailability)) {
          return res.status(400).json({
            success: false,
            traceId,
            message: `segments[${i}].linkAvailability must be "true" or "false"`,
          });
        }
      }
    }

    // ── Validate passengers (both paths) ──
    if (!Array.isArray(passengers) || passengers.length === 0) {
      return res.status(400).json({
        success: false,
        traceId,
        message: "passengers array is required and must not be empty.",
      });
    }

    for (const [i, p] of passengers.entries()) {
      if (!VALID_PTC_CODES.includes(p.code)) {
        return res.status(400).json({
          success: false,
          traceId,
          message: `passengers[${i}].code must be one of ${VALID_PTC_CODES.join(", ")}`,
        });
      }
      // ACH prices against the specific traveler ref minted at search time
      if (isACH && (!p.bookingTravelerRef || typeof p.bookingTravelerRef !== "string")) {
        return res.status(400).json({
          success: false,
          traceId,
          message: `passengers[${i}].bookingTravelerRef is required for ACH (6E) pricing`,
        });
      }
    }

    // ── ACH-only: validate bookingRequirements and segment ↔ requirement match ──
    if (isACH) {
      if (!Array.isArray(bookingRequirements) || bookingRequirements.length === 0) {
        return res.status(400).json({
          success: false,
          traceId,
          message: "bookingRequirements array is required for ACH (6E) pricing.",
        });
      }

      const brRequired = ["segmentKey", "fareBasis", "bookingCode", "hostToken", "hostTokenRef"];
      for (const [i, br] of bookingRequirements.entries()) {
        for (const field of brRequired) {
          if (br[field] === undefined || br[field] === null || br[field] === "") {
            return res.status(400).json({
              success: false,
              traceId,
              message: `bookingRequirements[${i}] missing field "${field}"`,
            });
          }
        }
      }

      // every segment must have a matching bookingRequirements entry by segmentKey
      const brKeys = new Set(bookingRequirements.map((b) => b.segmentKey));
      for (const [i, seg] of segments.entries()) {
        if (!brKeys.has(seg.segmentKey)) {
          return res.status(400).json({
            success: false,
            traceId,
            message: `segments[${i}] has no matching bookingRequirements entry (segmentKey "${seg.segmentKey}")`,
          });
        }
      }
    }

    // ── Call Service ──
    const result = await flightService.priceFlight(req.body, traceId);

    if (!result.success) {
      return res.status(500).json({
        success: false,
        traceId,
        message: "Pricing failed",
        error: result.error,
      });
    }

    return res.status(200).json({
      success: true,
      traceId,
      attempt: result.attempt || 1,
      passengerKeys: result.passengerKeys,
      data: result.data,
    });

  } catch (err) {
    console.error(`[${traceId}] ❌`, err.message);

    return res.status(500).json({
      success: false,
      traceId,
      message: "Internal error",
      error: err.message,
    });
  }
};

