package com.fractalite.amad_ndc.convertersRS;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;


import com.amadeus.fmptbr_24_1_1a.*;


import com.fractalite.mercure.data.model.bo.Price;
import com.fractalite.mercure.data.model.bo.PriceScope;
import com.fractalite.mercure.data.model.person.LegalPerson;
import com.fractalite.mercure.data.model.person.NaturalPerson;
import com.fractalite.mercure.data.model.person.Person;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;


import com.fractalite.mercure.data.model.bo.Tax;
import com.fractalite.mercure.data.model.product.*;

@Component
public class Fare_MasterPricerTravelBoardSearchRS {

	private static final Logger LOG = LoggerFactory.getLogger(Fare_MasterPricerTravelBoardSearchRS.class);
	private static final DateTimeFormatter TIME_FMT = DateTimeFormatter.ofPattern("HHmm");
	private static final DateTimeFormatter DATE_FMT_AMADEUS = DateTimeFormatter.ofPattern("ddMMyy");

	// Inner class to hold context for a specific Offer within a Recommendation
	private static class OfferContext {
		final FareMasterPricerTravelBoardSearchReply.Recommendation recommendation;
		final List<ReferenceInfoType> matchingSegmentRefs;

		public OfferContext(FareMasterPricerTravelBoardSearchReply.Recommendation recommendation, List<ReferenceInfoType> matchingSegmentRefs) {
			this.recommendation = recommendation;
			this.matchingSegmentRefs = matchingSegmentRefs;
		}
	}

	// Wrapper to track leg index
	private static class FlightGroupInfo {
		final FareMasterPricerTravelBoardSearchReply.FlightIndex.GroupOfFlights gof;
		final int legIndex;

		public FlightGroupInfo(FareMasterPricerTravelBoardSearchReply.FlightIndex.GroupOfFlights gof, int legIndex) {
			this.gof = gof;
			this.legIndex = legIndex;
		}
	}

	public List<Product> buildProducts(FareMasterPricerTravelBoardSearchReply reply, String currency) {
		List<Product> products = new ArrayList<>();

		if (reply == null || reply.getOffersGroup() == null || reply.getOffersGroup().isEmpty()) {
			LOG.warn("No offersGroup found in reply");
			return products;
		}

		// 1. Currency — use provided param; fallback to conversion rate in reply
		if (currency == null || currency.isBlank()) {
			currency = "EUR";
			if (reply.getConversionRate() != null
					&& reply.getConversionRate().getConversionRateDetail() != null
					&& !reply.getConversionRate().getConversionRateDetail().isEmpty()) {
				currency = reply.getConversionRate().getConversionRateDetail().get(0).getCurrency();
			}
		}

		// 2. Index Flights
		// 2. Index Flights with Leg Tracking
		Map<String, FlightGroupInfo> flightMap = indexFlights(reply);

		// 3. Index Recommendations by Offer Reference
		Map<String, OfferContext> offerContextMap = indexRecommendationsByOfferRef(reply);

		// 4. Index Baggage
		Map<String, String> baggageMap = indexBaggage(reply);

		// 5. Process NDC Offers - deduplicate by recommendation
		Set<String> processedRecommendations = new HashSet<>();
		for (FareMasterPricerTravelBoardSearchReply.OffersGroup group : reply.getOffersGroup()) {
			if (group.getOffers() != null) {
				for (FareMasterPricerTravelBoardSearchReply.OffersGroup.Offers offer : group.getOffers()) {

					String offerRefId = null;
					if (offer.getOfferDetails() != null) {
						offerRefId = offer.getOfferDetails().getReference();
					}

					if (offerRefId != null && offerContextMap.containsKey(offerRefId)) {
						OfferContext ctx = offerContextMap.get(offerRefId);

						// Deduplicate: skip if we already processed this recommendation
						String recId = (ctx.recommendation.getItemNumber() != null
								&& ctx.recommendation.getItemNumber().getItemNumberId() != null)
								? ctx.recommendation.getItemNumber().getItemNumberId().getNumber()
								: null;
						if (recId != null && processedRecommendations.contains(recId)) {
							continue;
						}
						if (recId != null) {
							processedRecommendations.add(recId);
						}

						Product product = buildSingleProduct(offer, ctx, flightMap, baggageMap, currency, reply);
						if (product != null) {
							products.add(product);
						}
					} else {
						LOG.debug("Could not link Offer Ref {} to any Recommendation", offerRefId);
					}
				}
			}
		}

		return products;
	}

	private Map<String, OfferContext> indexRecommendationsByOfferRef(FareMasterPricerTravelBoardSearchReply reply) {
		Map<String, OfferContext> map = new HashMap<>();

		if (reply.getRecommendation() == null) return map;

		for (FareMasterPricerTravelBoardSearchReply.Recommendation rec : reply.getRecommendation()) {
			if (rec.getSegmentFlightRef() == null) continue;

			Map<String, List<ReferenceInfoType>> tempMap = new HashMap<>();

			for (ReferenceInfoType segRef : rec.getSegmentFlightRef()) {
				String oiRef = null;

				if (segRef.getReferencingDetail() != null) {
					for (var detail : segRef.getReferencingDetail()) {
						if ("OI".equals(detail.getRefQualifier())) {
							oiRef = detail.getRefNumber().toString();
							break;
						}
					}
				}

				if (oiRef != null) {
					tempMap.computeIfAbsent(oiRef, k -> new ArrayList<>()).add(segRef);
				}
			}

			for (Map.Entry<String, List<ReferenceInfoType>> entry : tempMap.entrySet()) {
				map.put(entry.getKey(), new OfferContext(rec, entry.getValue()));
			}
		}
		return map;
	}

	private Product buildSingleProduct(FareMasterPricerTravelBoardSearchReply.OffersGroup.Offers offer,
									   OfferContext ctx,
									   Map<String, FlightGroupInfo> flightMap,
									   Map<String, String> baggageMap,
									   String currency,
									   FareMasterPricerTravelBoardSearchReply reply) {

		//offer.get
		Product product = new Product();
		FareMasterPricerTravelBoardSearchReply.Recommendation rec = ctx.recommendation;

		// --- Metadata ---
		String uniqueRef = (offer.getOfferDetails() != null) ? offer.getOfferDetails().getUniqueOfferReference() : "";
		String itemNumber = (rec.getItemNumber() != null && rec.getItemNumber().getItemNumberId() != null)
				? rec.getItemNumber().getItemNumberId().getNumber() : "0";

		String shoppingRef = (offer.getOfferDetails() != null) ? offer.getOfferItems().get(0).getOfferItemDetails().getOfferItemId() : "";
		product.setName(uniqueRef);

		String finalReference = buildReferenceFromOffer( reply.getOffersGroup().get(0).getResponse().getApplicationDetails().getInternalId(),offer);

		//String finalReference = uniqueRef + ";" + shoppingRef + ";" + reply.getOffersGroup().get(0).getResponse().getApplicationDetails().getInternalId();
		product.setReference(finalReference);

		// --- Product Type ---
		ProductType productType = new ProductType();
		productType.setProductTypeId(40L);
		productType.setCode("amadeus");
		productType.setName("Amadeus");
		product.setProducType(productType);

		// --- PRIX GLOBAUX DU PRODUIT (somme de tous les passagers) ---
		BigDecimal totalAmount = BigDecimal.ZERO;
		BigDecimal taxAmount = BigDecimal.ZERO;

		if (rec.getRecPriceInfo() != null && rec.getRecPriceInfo().getMonetaryDetail() != null) {
			List<MonetaryInformationDetailsType277475C> details = rec.getRecPriceInfo().getMonetaryDetail();
			if (!details.isEmpty()) totalAmount = details.get(0).getAmount();
			if (details.size() > 1) taxAmount = details.get(1).getAmount();
		}

		BigDecimal baseAmount = totalAmount.subtract(taxAmount);

		// Prix au niveau du produit (total)
		List<Price> prices = new ArrayList<>();
		prices.add(createPrice(baseAmount, "PURCHASE_TOTAL_BASE", currency));
		prices.add(createPrice(totalAmount, "TOTAL", currency));
		prices.add(createPrice(taxAmount, "TAX_TOTAL", currency));
		product.setPrices(prices);

		// Taxes au niveau du produit
		List<Tax> taxes = new ArrayList<>();
		Tax taxObj = new Tax();
		taxObj.setCode("TOTAL");
		taxObj.setDescription("Total taxes");
		taxObj.setAmount((float) taxAmount.doubleValue());
		taxObj.setCurrency(Currency.getInstance(currency));
		taxes.add(taxObj);


		// --- BUILD ITINERARIES: use ALL segment refs from the recommendation (both legs) ---
		List<ReferenceInfoType> allSegmentRefs = rec.getSegmentFlightRef();
		List<ReferenceInfoType> segmentRefs = ctx.matchingSegmentRefs;
		List<Itinerary> mainItineraries = buildItineraries(allSegmentRefs, flightMap, rec, baggageMap);

		// --- SUPPLIER ---
		product.setSupplier(buildSupplier(segmentRefs, flightMap, currency));

		// ========================================
		// MODIFICATION PRINCIPALE : UN RATEGROUP PAR TYPE DE PASSAGER
		// ========================================
		List<RateGroup> rateGroups = new ArrayList<>();

		// Parcourir tous les paxFareProduct dans la recommendation
		if (rec.getPaxFareProduct() != null) {
			for (FareMasterPricerTravelBoardSearchReply.Recommendation.PaxFareProduct paxFare : rec.getPaxFareProduct()) {
				RateGroup rateGroup = buildRateGroupForPax(rec.getRecPriceInfo(),paxFare, segmentRefs, flightMap, baggageMap, currency, mainItineraries);
				rateGroup.setReference(finalReference);
				if (rateGroup != null) {
					rateGroups.add(rateGroup);
				}
			}
		}

		product.setRateGroups(rateGroups);

		return product;
	}

	private String buildReferenceFromOffer(String internalId,FareMasterPricerTravelBoardSearchReply.OffersGroup.Offers offer) {

		StringBuilder reference = new StringBuilder();

		reference.append("internalId:"+internalId).append("*");

		reference.append("uniqueOfferReference:").append(offer.getOfferDetails().getUniqueOfferReference()).append("*");
		reference.append("offerItems:");
		for(FareMasterPricerTravelBoardSearchReply.OffersGroup.Offers.OfferItems offerItem:offer.getOfferItems()){
			reference.append(offerItem.getPaxReference().get(0).getPtc()).append("#")
			.append(offerItem.getOfferItemDetails().getOfferItemId()).append("*");
		}

		return reference.toString();
	}

	private RateGroup buildRateGroupForPax(MonetaryInformationType204470S recPriceInfo,
										   FareMasterPricerTravelBoardSearchReply.Recommendation.PaxFareProduct paxFare,
										   List<ReferenceInfoType> segmentRefs,
										   Map<String, FlightGroupInfo> flightMap,
										   Map<String, String> baggageMap,
										   String currency,
										   List<Itinerary> mainItineraries) {

		RateGroup rateGroup = new RateGroup();

		// --- 1. Déterminer le type de passager et le nombre de travellers ---
		String paxType = "ADT";
		List<Integer> travellerRefs = new ArrayList<>();

		if (paxFare.getPaxReference() != null && !paxFare.getPaxReference().isEmpty()) {
			var paxRef = paxFare.getPaxReference().get(0);

			if (paxRef.getPtc() != null && !paxRef.getPtc().isEmpty()) {
				paxType = paxRef.getPtc().get(0);
			}

			// CORRECTION : compter tous les travellers pour cette PTC
			if (paxRef.getTraveller() != null) {
				for (var t : paxRef.getTraveller()) {
					travellerRefs.add(t.getRef().intValue());
				}
			}
		}

		int quantity = travellerRefs.isEmpty() ? 1 : travellerRefs.size(); // ex: 2 pour ADT, 1 pour CHD

		// --- 2. Prix : DÉCOMMENTER et utiliser paxFareDetail ---
		BigDecimal paxTotal = BigDecimal.ZERO;
		BigDecimal paxTax = BigDecimal.ZERO;

		// FIX : paxFareDetail contient le prix UNITAIRE (par passager)
		if (paxFare.getPaxFareDetail() != null) {
			if (paxFare.getPaxFareDetail().getTotalFareAmount() != null) {
				paxTotal = paxFare.getPaxFareDetail().getTotalFareAmount();
			}
			if (paxFare.getPaxFareDetail().getTotalTaxAmount() != null) {
				paxTax = paxFare.getPaxFareDetail().getTotalTaxAmount();
			}
		}

		BigDecimal paxBase = paxTotal.subtract(paxTax);

		// --- 3. RateDetail ---
		RateDetail rate = new RateDetail();
		rate.setQuantity(quantity);
		rate.setMarkup(15.0);
		rate.setMin(1);
		rate.setMax(2);
		rate.setExpriresAt(13);

		String paxTypeLabel = getPaxTypeLabel(paxType);
		rate.setPrice(paxTotal.floatValue());
		rate.setBasePrice(paxBase.floatValue());
		rate.setPurchasePrice(paxTotal.floatValue());

		if (mainItineraries != null && !mainItineraries.isEmpty() && mainItineraries.get(0).getSpaceTimePoints() != null && !mainItineraries.get(0).getSpaceTimePoints().isEmpty()) {
			SpaceTimePoint firstStp = mainItineraries.get(0).getSpaceTimePoints().get(0);
			String depCode = firstStp.getDfrom().split(":")[0];
			String arrCode = firstStp.getDto().split(":")[0];
			String depDate = firstStp.getFromDate().toString();
			String depTime = firstStp.getFromTime();
			String arrTime = firstStp.getToTime();
			String flightNum = extractFlightNumber(firstStp.getTags());

			rate.setRef(" <ul> <b> " + flightNum + " </b> <br> De  <b> " + depCode + " </b> à <b> " + arrCode + "<br> <b> " + depDate + " " + depTime + " -> " + depDate + " " + arrTime + " </b></ul><br><ul> Voyageurs : " + quantity + " " + paxTypeLabel + (quantity > 1 ? "s" : "") + ",  </ul> ");
		} else {
			rate.setRef("<ul> Voyageurs : " + quantity + " " + paxTypeLabel + (quantity > 1 ? "s" : "") + " </ul>");
		}

		// Taxes
		List<Tax> rateTaxes = new ArrayList<>();
		Tax tax = new Tax();
		tax.setCode("TOTAL");
		tax.setDescription("Total taxes");
		tax.setAmount(paxTax.floatValue());
		tax.setCurrency(Currency.getInstance(currency));
		rateTaxes.add(tax);
		rate.setTaxes(rateTaxes);
		rate.setMarkup(15.0);

		// Itinéraire cloné avec le bon paxType
		List<Itinerary> rateItineraries = new ArrayList<>();
		if (mainItineraries != null) {
			for (Itinerary itin : mainItineraries) {
				Itinerary clonedItinerary = cloneItinerary(itin, paxFare, baggageMap, paxType);
				rateItineraries.add(clonedItinerary);
			}
		}
		rate.setItinerary(rateItineraries);

		List<RateDetail> rates = new ArrayList<>();
		rates.add(rate);
		rateGroup.setRates(rates);

		// --- 4. Beneficiaries : un NaturalPerson par traveller réel ---
		// FIX : générer dynamiquement les refs selon les vrais indices de travellers
		List<Person> beneficiaries = new ArrayList<>();
		List<String> tags = new ArrayList<>();

		for (Integer travRef : travellerRefs) {
			String paxTag = "PAX" + travRef + ":" + paxType; // ex: PAX1:ADT, PAX2:ADT, PAX3:CHD
			NaturalPerson person = new NaturalPerson();
			person.setRef(paxTag);
			beneficiaries.add(person);
			tags.add(paxTag);
		}

		// Cas fallback si pas de traveller refs
		if (beneficiaries.isEmpty()) {
			String paxTag = "PAX1:" + paxType;
			NaturalPerson person = new NaturalPerson();
			person.setRef(paxTag);
			beneficiaries.add(person);
			tags.add(paxTag);
		}

		rateGroup.setBeneficiaries(beneficiaries);
		rateGroup.setTags(tags);

		// ProductType
		ProductType pt = new ProductType();
		pt.setProductTypeId(40L);
		pt.setCode("amadeus");
		pt.setName("Amadeus");
		rateGroup.setProducType(pt);

		rateGroup.setSupplier(buildSupplier(segmentRefs, flightMap, currency));

		return rateGroup;
	}

	/**
	 * Clone l'itinéraire principal et ajuste les tags avec les infos spécifiques au passager
	 */
	private Itinerary cloneItinerary(Itinerary source,
									 FareMasterPricerTravelBoardSearchReply.Recommendation.PaxFareProduct paxFare,
									 Map<String, String> baggageMap,
									 String paxType) {
		Itinerary cloned = new Itinerary();
		cloned.setName(source.getName());
		cloned.setSort(source.getSort());

		// Cloner les SpaceTimePoints avec les tags mis à jour
		List<SpaceTimePoint> clonedStps = new ArrayList<>();
		if (source.getSpaceTimePoints() != null) {
			for (SpaceTimePoint stp : source.getSpaceTimePoints()) {
				SpaceTimePoint newStp = new SpaceTimePoint();
				newStp.setDfrom(stp.getDfrom());
				newStp.setDto(stp.getDto());
				newStp.setPriority(stp.getPriority());
				newStp.setFromDate(stp.getFromDate());
				newStp.setToDate(stp.getToDate());
				newStp.setFromTime(stp.getFromTime());
				newStp.setToTime(stp.getToTime());
				newStp.setDuration(stp.getDuration());

				// Mettre à jour les tags avec les infos spécifiques au passager
				List<String> updatedTags = updateTagsForPax(stp.getTags(), paxFare, paxType,1);
				newStp.setTags(updatedTags);

				clonedStps.add(newStp);
			}
		}
		cloned.setSpaceTimePoints(clonedStps);

		// Cloner les tags de l'itinéraire
		if (source.getTags() != null) {
			cloned.setTags(new ArrayList<>(source.getTags()));
		}

		return cloned;
	}

	private List<String> updateTagsForPax(List<String> originalTags,
										  FareMasterPricerTravelBoardSearchReply.Recommendation.PaxFareProduct paxFare,
										  String paxType,
										  int quantity) { // FIX : ajouter quantity
		if (originalTags == null) return new ArrayList<>();

		List<String> updatedTags = new ArrayList<>(originalTags);
		String paxTypeTag = getPaxTypeTag(paxType);

		for (int i = 0; i < updatedTags.size(); i++) {
			String tag = updatedTags.get(i);
			if (tag != null && tag.startsWith("bagsDetail:")) {
				// Remplacer type pax ET quantité ex: bagsDetail:0:N:1:adult → bagsDetail:0:N:2:adult
				tag = tag.replaceAll(":(\\d+):adult$", ":" + quantity + ":" + paxTypeTag);
				tag = tag.replaceAll(":(\\d+):child$",  ":" + quantity + ":" + paxTypeTag);
				tag = tag.replaceAll(":(\\d+):infant$", ":" + quantity + ":" + paxTypeTag);
				updatedTags.set(i, tag);
			}
		}
		return updatedTags;
	}

	private String getPaxTypeLabel(String paxType) {
		switch (paxType) {
			case "ADT": return "adulte";
			case "CHD": return "enfant";
			case "INF": return "bébé";
			case "YTH": return "jeune";
			case "YCD": return "jeune avec carte";
			default:    return "passager";
		}
	}

	private String getPaxTypeTag(String paxType) {
		switch (paxType) {
			case "ADT": return "adult";
			case "CHD": return "child";
			case "INF": return "infant";
			case "YTH":
			case "YCD": return "youth";
			default:    return "adult";
		}
	}

	/**
	 * Builds list of itineraries, one per leg (outbound/inbound)
	 */
	private List<Itinerary> buildItineraries(List<ReferenceInfoType> segmentRefs,
										 Map<String, FlightGroupInfo> flightMap,
										 FareMasterPricerTravelBoardSearchReply.Recommendation rec,
										 Map<String, String> baggageMap) {

		Map<Integer, List<SpaceTimePoint>> legMap = new TreeMap<>();
		String itemNumberStr = (rec.getItemNumber() != null && rec.getItemNumber().getItemNumberId() != null)
				? rec.getItemNumber().getItemNumberId().getNumber() : "0";
		String baggageInfo = baggageMap.get(itemNumberStr);

		FareMasterPricerTravelBoardSearchReply.Recommendation.PaxFareProduct.FareDetails fareData = null;
		if (rec.getPaxFareProduct() != null && !rec.getPaxFareProduct().isEmpty()) {
			var firstPax = rec.getPaxFareProduct().get(0);
			if (firstPax.getFareDetails() != null && !firstPax.getFareDetails().isEmpty()) {
				fareData = firstPax.getFareDetails().get(0);
			}
		}

		int priority = 1;
		for (ReferenceInfoType ref : segmentRefs) {
			String sRef = null;
			if (ref.getReferencingDetail() != null) {
				for (var det : ref.getReferencingDetail()) {
					if ("S".equals(det.getRefQualifier())) {
						sRef = det.getRefNumber().toString();
						break;
					}
				}
			}

			if (sRef != null && flightMap.containsKey(sRef)) {
				var info = flightMap.get(sRef);
				if (info.gof.getFlightDetails() != null) {
					List<SpaceTimePoint> legStps = legMap.computeIfAbsent(info.legIndex, k -> new ArrayList<>());
					for (var fd : info.gof.getFlightDetails()) {
						legStps.add(buildSpaceTimePoint(fd, priority++, fareData, baggageInfo));
					}
				}
			}
		}

		List<Itinerary> results = new ArrayList<>();
		for (Map.Entry<Integer, List<SpaceTimePoint>> entry : legMap.entrySet()) {
			Itinerary itinerary = new Itinerary();
			itinerary.setSort(entry.getKey());
			itinerary.setSpaceTimePoints(entry.getValue());

			if (!entry.getValue().isEmpty()) {
				itinerary.setName(extractFlightNumber(entry.getValue().get(0).getTags()));
			}
			buildItineraryTags0(itinerary, fareData);
			results.add(itinerary);
		}

		return results;
	}

	private String extractFlightNumber(List<String> tags) {
		if (tags == null) return "";
		for (String tag : tags) {
			if (tag != null && tag.matches("^[A-Z]{2}\\d+$")) {
				return tag;
			}
		}
		return "";
	}

	// --- Index Flights (inchangé) ---
	private Map<String, FlightGroupInfo> indexFlights(FareMasterPricerTravelBoardSearchReply reply) {
		Map<String, FlightGroupInfo> map = new HashMap<>();
		if (reply.getFlightIndex() == null) return map;

		for (int i = 0; i < reply.getFlightIndex().size(); i++) {
			FareMasterPricerTravelBoardSearchReply.FlightIndex fi = reply.getFlightIndex().get(i);
			int legIndex = i; // 0 for first leg, 1 for second leg, etc.

			if (fi.getGroupOfFlights() != null) {
				for (FareMasterPricerTravelBoardSearchReply.FlightIndex.GroupOfFlights gof : fi.getGroupOfFlights()) {
					if (gof.getPropFlightGrDetail() != null && gof.getPropFlightGrDetail().getFlightProposal() != null) {
						for (var prop : gof.getPropFlightGrDetail().getFlightProposal()) {
							String ref = prop.getRef();
							if (ref != null) {
								map.put(ref, new FlightGroupInfo(gof, legIndex));
							}
						}
					}
				}
			}
		}
		return map;
	}

	private LegalPerson buildSupplier(List<ReferenceInfoType> segmentRefs,
									  Map<String, FlightGroupInfo> flightMap,
									  String currency) {
		LegalPerson supplier = new LegalPerson();
		supplier.setId(16L);
		try { supplier.setCurrency(Currency.getInstance(currency)); } catch (Exception e) { supplier.setCurrency(Currency.getInstance("EUR")); }

		if (segmentRefs != null && !segmentRefs.isEmpty()) {
			ReferenceInfoType firstRef = segmentRefs.get(0);
			String sRef = null;
			if (firstRef.getReferencingDetail() != null) {
				for (var det : firstRef.getReferencingDetail()) {
					if ("S".equals(det.getRefQualifier())) sRef = det.getRefNumber().toString();
				}
			}

			if (sRef != null && flightMap.containsKey(sRef)) {
				var info = flightMap.get(sRef);
				if (info.gof.getFlightDetails() != null && !info.gof.getFlightDetails().isEmpty()) {
					var fltInfo = info.gof.getFlightDetails().get(0).getFlightInformation();
					if (fltInfo.getCompanyId() != null) {
						String code = fltInfo.getCompanyId().getMarketingCarrier();
						supplier.setName(code);
						supplier.setLogo(code);
					}
				}
			}
		}
		return supplier;
	}

	private SpaceTimePoint buildSpaceTimePoint(FareMasterPricerTravelBoardSearchReply.FlightIndex.GroupOfFlights.FlightDetails fd,
											   int priority,
											   FareMasterPricerTravelBoardSearchReply.Recommendation.PaxFareProduct.FareDetails fareData,
											   String baggageInfo) {
		SpaceTimePoint stp = new SpaceTimePoint();
		TravelProductType fi = fd.getFlightInformation();
		stp.setPriority(priority);

		if (fi.getLocation() != null && fi.getLocation().size() >= 2) {
			String depTerm = fi.getLocation().get(0).getTerminal() != null ? ":" + fi.getLocation().get(0).getTerminal() : "";
			String arrTerm = fi.getLocation().get(1).getTerminal() != null ? ":" + fi.getLocation().get(1).getTerminal() : "";
			stp.setDfrom(fi.getLocation().get(0).getLocationId() + depTerm);
			stp.setDto(fi.getLocation().get(1).getLocationId() + arrTerm);
		}

		if (fi.getProductDateTime() != null) {
			stp.setFromDate(parseDate(fi.getProductDateTime().getDateOfDeparture()));
			stp.setFromTime(formatTime(fi.getProductDateTime().getTimeOfDeparture()));
			stp.setToDate(parseDate(fi.getProductDateTime().getDateOfArrival()));
			stp.setToTime(formatTime(fi.getProductDateTime().getTimeOfArrival()));
			stp.setDuration(computeDuration(fi.getProductDateTime().getTimeOfDeparture(), fi.getProductDateTime().getTimeOfArrival()));
		}

		stp.setTags(buildSegmentTags(fi, fareData, baggageInfo));
		return stp;
	}

	private List<String> buildSegmentTags(TravelProductType fi, FareMasterPricerTravelBoardSearchReply.Recommendation.PaxFareProduct.FareDetails fareData, String baggageInfo) {
		List<String> tags = new ArrayList<>();

		String marketingCarrier = (fi.getCompanyId() != null) ? fi.getCompanyId().getMarketingCarrier() : "";
		String operatingCarrier = (fi.getCompanyId() != null) ? fi.getCompanyId().getOperatingCarrier() : "";
		String flightNumber = fi.getFlightOrtrainNumber();
		String equipment = (fi.getProductDetail() != null) ? fi.getProductDetail().getEquipmentType() : "";

		String cabinName = "ECONOMY";
		String rbd = "Y";
		String fareBasis = "";

		if (fareData != null && fareData.getGroupOfFares() != null && !fareData.getGroupOfFares().isEmpty()) {
			var prodInfo = fareData.getGroupOfFares().get(0).getProductInformation();
			if (prodInfo != null && prodInfo.getCabinProduct() != null && !prodInfo.getCabinProduct().isEmpty()) {
				String cabinCode = prodInfo.getCabinProduct().get(0).getCabin();
				cabinName = getCabinName(cabinCode);
				rbd = prodInfo.getCabinProduct().get(0).getRbd();
			}
			if (prodInfo != null && prodInfo.getFareProductDetail() != null) {
				fareBasis = prodInfo.getFareProductDetail().getFareBasis();
			}
		}

		tags.add(null);
		tags.add(cabinName + ":" + rbd);
		tags.add(marketingCarrier + flightNumber);
		tags.add("Vol opéré par " + operatingCarrier);
		tags.add("Vol marketer par " + marketingCarrier);
		tags.add(equipment);
		tags.add(baggageInfo != null ? "Baggage : Checked , " + baggageInfo : "Baggage : Checked , quantite 0 pieces");
		tags.add(fareBasis);
		tags.add(parseBagsDetail(baggageInfo, 1, "ADT"));
		tags.add("flightNumber:" + marketingCarrier + flightNumber);
		tags.add("equipment:" + equipment);
		tags.add("fareBasis:" + fareBasis);
		tags.add("avc:" + marketingCarrier);
		tags.add("aft:" + rbd);

		return tags;
	}

	/**
	 * Build itinerary-level tags, aggregating fareBasis across all segments.
	 * Format: [flightNumber, null, CABIN, fareBasis(all), "fareBasis:csv", "fareBasisAll:csv"]
	 * For multi-segment itineraries fareBasis values are comma-joined (e.g. "KEULGTP0,KEULGTP0").
	 */
	private void buildItineraryTags0(Itinerary itinerary, FareMasterPricerTravelBoardSearchReply.Recommendation.PaxFareProduct.FareDetails fareData) {
		List<String> tags = new ArrayList<>();
		tags.add(itinerary.getName());
		tags.add(null);

		String cabin = "ECONOMY";
		List<String> fareBasisList = new ArrayList<>();

		if (fareData != null && fareData.getGroupOfFares() != null) {
			for (var gof : fareData.getGroupOfFares()) {
				var prodInfo = gof.getProductInformation();
				if (prodInfo == null) continue;
				if (prodInfo.getCabinProduct() != null && !prodInfo.getCabinProduct().isEmpty()) {
					cabin = getCabinName(prodInfo.getCabinProduct().get(0).getCabin());
				}
				if (prodInfo.getFareProductDetail() != null && prodInfo.getFareProductDetail().getFareBasis() != null) {
					fareBasisList.add(prodInfo.getFareProductDetail().getFareBasis());
				}
			}
		}

		String fareBasisCsv = String.join(",", fareBasisList);
		String firstFareBasis = fareBasisList.isEmpty() ? "" : fareBasisList.get(0);

		tags.add(cabin);
		tags.add(firstFareBasis);
		tags.add("fareBasis:" + fareBasisCsv);
		tags.add("fareBasisAll:" + fareBasisCsv);

		itinerary.setTags(tags);
	}

	private Price createPrice(BigDecimal value, String scope, String currency) {
		Price p = new Price();
		p.setValue(value.floatValue());
		if (scope.equals("PURCHASE_TOTAL_BASE")) p.setScope(PriceScope.PURCHASE_TOTAL_PRICE);
		if (scope.equals("TOTAL")) p.setScope(PriceScope.TOTAL);
		if (scope.equals("TAX_TOTAL")) p.setScope(PriceScope.TAX_TOTAL);
		p.setCurrency(Currency.getInstance(currency));
		return p;
	}

	private String getCabinName(String code) {
		if (code == null) return "ECONOMY";
		switch (code.toUpperCase()) {
			case "C": case "J": return "BUSINESS";
			case "F": return "FIRST";
			case "W": return "PREMIUM ECONOMY";
			case "M": case "Y": default: return "ECONOMY";
		}
	}

	private String parseBagsDetail(String baggageInfo, int quantity, String paxType) {
		String qty = "0";
		String type = "N";
		if (baggageInfo != null) {
			if (baggageInfo.contains("pieces")) {
				qty = baggageInfo.replaceAll("[^0-9]", "");
				type = "N";
			} else if (baggageInfo.contains("kg")) {
				qty = baggageInfo.replaceAll("[^0-9]", "");
				type = "K";
			}
		}
		// FIX : utiliser le vrai paxType et quantity
		return "bagsDetail:" + qty + ":" + type + ":" + quantity + ":" + getPaxTypeTag(paxType);
	}

	private String parseBagsDetailOld(String baggageInfo) {
		String qty = "0";
		String type = "N";
		if (baggageInfo != null) {
			if (baggageInfo.contains("pieces")) {
				qty = baggageInfo.replaceAll("[^0-9]", "");
				type = "N";
			} else if (baggageInfo.contains("kg")) {
				qty = baggageInfo.replaceAll("[^0-9]", "");
				type = "K";
			}
		}
		return "bagsDetail:" + qty + ":" + type + ":1:adult";
	}

	private Map<String, String> indexBaggage(FareMasterPricerTravelBoardSearchReply reply) {
		Map<String, String> map = new HashMap<>();
		if (reply.getServiceFeesGrp() == null) return map;
		for (var sfg : reply.getServiceFeesGrp()) {
			if (sfg.getFreeBagAllowanceGrp() != null) {
				for (var bagGrp : sfg.getFreeBagAllowanceGrp()) {
					var details = bagGrp.getFreeBagAllownceInfo().getBaggageDetails();
					String qty = details.getFreeAllowance().toString();
					String type = "N".equals(details.getQuantityCode()) ? "pieces" : "kg";
					String bagStr = "quantite " + qty + " " + type;
					if (bagGrp.getItemNumberInfo() != null && bagGrp.getItemNumberInfo().getItemNumberDetails() != null) {
						String recRef = String.valueOf(bagGrp.getItemNumberInfo().getItemNumberDetails().get(0).getNumber());
						map.put(recRef, bagStr);
					}
				}
			}
		}
		return map;
	}

	private Date parseDate(String amadeusDate) {
		try {
			return Date.from(LocalDate.parse(amadeusDate, DATE_FMT_AMADEUS).atStartOfDay(ZoneId.of("UTC")).toInstant());
		} catch (Exception e) {
			return null;
		}
	}

	private String formatTime(String t) {
		if (t == null || t.length() < 4) return t;
		return t.substring(0, 2) + ":" + t.substring(2, 4);
	}

	private String computeDuration(String dep, String arr) {
		try {
			Duration d = Duration.between(LocalTime.parse(dep, TIME_FMT), LocalTime.parse(arr, TIME_FMT));
			if (d.isNegative()) d = d.plusHours(24);
			return d.toString();
		} catch (Exception e) {
			return "PT00H00M";
		}
	}
}

/*
package com.fractalite.amad_ndc.convertersRS;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.fractalite.mercure.data.model.bo.Price;
import com.fractalite.mercure.data.model.bo.PriceScope;
import com.fractalite.mercure.data.model.person.LegalPerson;
import com.fractalite.mercure.data.model.person.NaturalPerson;
import com.fractalite.mercure.data.model.person.Person;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.amadeus.fmptbr_24_1_1a.*;
import com.amadeus.fmptbr_24_1_1a.FareMasterPricerTravelBoardSearchReply.FlightIndex;
import com.amadeus.fmptbr_24_1_1a.FareMasterPricerTravelBoardSearchReply.Recommendation;
import com.fractalite.mercure.data.model.bo.Tax;
import com.fractalite.mercure.data.model.product.*;

@Component
public class Fare_MasterPricerTravelBoardSearchRS {

	private static final Logger LOG = LoggerFactory.getLogger(Fare_MasterPricerTravelBoardSearchRS.class);
	private static final DateTimeFormatter TIME_FMT = DateTimeFormatter.ofPattern("HHmm");
	private static final DateTimeFormatter DATE_FMT_AMADEUS = DateTimeFormatter.ofPattern("ddMMyy");

	// Inner class to hold context for a specific Offer within a Recommendation
	private static class OfferContext {
		final Recommendation recommendation;
		final List<ReferenceInfoType> matchingSegmentRefs; // Only the segments for this specific Offer

		public OfferContext(Recommendation recommendation, List<ReferenceInfoType> matchingSegmentRefs) {
			this.recommendation = recommendation;
			this.matchingSegmentRefs = matchingSegmentRefs;
		}
	}

	public List<Product> buildProducts(FareMasterPricerTravelBoardSearchReply reply) {
		List<Product> products = new ArrayList<>();

		if (reply == null || reply.getOffersGroup() == null || reply.getOffersGroup().isEmpty()) {
			LOG.warn("No offersGroup found in reply");
			return products;
		}

		// 1. Extract Currency
		String currency = "MAD";
		if (reply.getConversionRate() != null &&
				reply.getConversionRate().getConversionRateDetail() != null &&
				!reply.getConversionRate().getConversionRateDetail().isEmpty()) {
			currency = reply.getConversionRate().getConversionRateDetail().get(0).getCurrency();
		}

		// 2. Index Flights
		// Map<FlightProposalRef (S), GroupOfFlights>
		// Note: Flattening the index for easier lookup. If IDs are not unique globally, keep the segment index.
		Map<String, FlightIndex.GroupOfFlights> flightMap = indexFlights(reply);

		// 3. Index Recommendations by Offer Reference (The "OI" bridge)
		// Map<OfferRef (OI), OfferContext>
		Map<String, OfferContext> offerContextMap = indexRecommendationsByOfferRef(reply);

		// 4. Index Baggage [Item Number -> Baggage String]
		Map<String, String> baggageMap = indexBaggage(reply);

		// 5. Process NDC Offers
		for (FareMasterPricerTravelBoardSearchReply.OffersGroup group : reply.getOffersGroup()) {
			if (group.getOffers() != null) {
				for (FareMasterPricerTravelBoardSearchReply.OffersGroup.Offers offer : group.getOffers()) {

					// Get the Link Key (e.g., "1") from the Offer
					String offerRefId = null;
					if (offer.getOfferDetails() != null) {
						offerRefId = offer.getOfferDetails().getReference();
					}

					// Find the Recommendation Context using the Link Key
					if (offerRefId != null && offerContextMap.containsKey(offerRefId)) {
						OfferContext ctx = offerContextMap.get(offerRefId);

						// Pass the Context (which contains specific segments) to build the product

						Product product = buildSingleProduct(offer, ctx, flightMap, baggageMap, currency,reply);
						if (product != null) {
							products.add(product);
						}
					} else {
						LOG.debug("Could not link Offer Ref {} to any Recommendation", offerRefId);
					}
				}
			}
		}

		return products;
	}

	*/
/**
	 * Scans recommendations to find which one holds the Flight References for a given Offer ID.
	 * A single Recommendation can hold multiple Offer IDs (if grouped by price).
	 *//*

	private Map<String, OfferContext> indexRecommendationsByOfferRef(FareMasterPricerTravelBoardSearchReply reply) {
		Map<String, OfferContext> map = new HashMap<>();

		if (reply.getRecommendation() == null) return map;

		for (Recommendation rec : reply.getRecommendation()) {
			if (rec.getSegmentFlightRef() == null) continue;

			// Group segment refs by their Offer Index (OI)
			// One Rec might have: {S=1, OI=1}, {S=2, OI=2} ...
			Map<String, List<ReferenceInfoType>> tempMap = new HashMap<>();

			for (ReferenceInfoType segRef : rec.getSegmentFlightRef()) {
				String oiRef = null;

				// Find the OI reference in this segment block
				if (segRef.getReferencingDetail() != null) {
					for (var detail : segRef.getReferencingDetail()) {
						if ("OI".equals(detail.getRefQualifier())) {
							oiRef = detail.getRefNumber().toString();
							break;
						}
					}
				}

				if (oiRef != null) {
					tempMap.computeIfAbsent(oiRef, k -> new ArrayList<>()).add(segRef);
				}
			}

			// Populate the main map
			for (Map.Entry<String, List<ReferenceInfoType>> entry : tempMap.entrySet()) {
				// Key = Offer Ref (e.g. "1"), Value = Context containing Rec + Specific Segments
				map.put(entry.getKey(), new OfferContext(rec, entry.getValue()));
			}
		}
		return map;
	}

	private Product buildSingleProduct(FareMasterPricerTravelBoardSearchReply.OffersGroup.Offers offer,
									   OfferContext ctx,
									   Map<String, FlightIndex.GroupOfFlights> flightMap,
									   Map<String, String> baggageMap,
									   String currency,
									   FareMasterPricerTravelBoardSearchReply reply) {
		Product product = new Product();
		Recommendation rec = ctx.recommendation;

		// --- Metadata ---
		String uniqueRef = (offer.getOfferDetails() != null) ? offer.getOfferDetails().getUniqueOfferReference() : "";
		String itemNumber = (rec.getItemNumber() != null && rec.getItemNumber().getItemNumberId() != null)
				? rec.getItemNumber().getItemNumberId().getNumber() : "0";

		String shoppingRef = (offer.getOfferDetails() != null) ? offer.getOfferItems().get(0).getOfferItemDetails().getOfferItemId() : "";
		product.setName(uniqueRef);
		String finalReference=uniqueRef+";"+shoppingRef+";"+reply.getOffersGroup().get(0).getResponse().getApplicationDetails().getInternalId();
		product.setReference(finalReference);




		// --- Product Type ---
		ProductType productType = new ProductType();
		productType.setProductTypeId(40L);
		productType.setCode("amadeus");
		productType.setName("Amadeus");
		product.setProducType(productType);

		// --- Pricing Extraction ---
		BigDecimal totalAmount = BigDecimal.ZERO;
		BigDecimal taxAmount = BigDecimal.ZERO;

		if (rec.getRecPriceInfo() != null && rec.getRecPriceInfo().getMonetaryDetail() != null) {
			List<MonetaryInformationDetailsType277475C> details = rec.getRecPriceInfo().getMonetaryDetail();
			if (!details.isEmpty()) totalAmount = details.get(0).getAmount();
			if (details.size() > 1) taxAmount = details.get(1).getAmount();
		} else if (rec.getPaxFareProduct() != null && !rec.getPaxFareProduct().isEmpty()) {
			var paxDetail = rec.getPaxFareProduct().get(0).getPaxFareDetail();
			if (paxDetail != null) {
				if (paxDetail.getTotalFareAmount() != null) totalAmount = paxDetail.getTotalFareAmount();
				if (paxDetail.getTotalTaxAmount() != null) taxAmount = paxDetail.getTotalTaxAmount();
			}
		}

		BigDecimal baseAmount = totalAmount.subtract(taxAmount);

		List<Price> prices = new ArrayList<>();
		prices.add(createPrice(baseAmount, "PURCHASE_TOTAL_BASE", currency));
		prices.add(createPrice(totalAmount, "TOTAL", currency));
		prices.add(createPrice(taxAmount, "TAX_TOTAL", currency));
		product.setPrices(prices);

		List<Tax> taxes = new ArrayList<>();
		Tax taxObj = new Tax();
		taxObj.setCode("TOTAL");
		taxObj.setDescription("Total taxes");
		taxObj.setAmount((float) taxAmount.doubleValue());
		taxObj.setCurrency(Currency.getInstance(currency));
		taxes.add(taxObj);
		product.setTax(taxes);

		// --- Itineraries ---
		// Pass the Specific Segment Refs from the Context, not the whole Rec
		String baggageInfo = baggageMap.get(itemNumber);
		List<Itinerary> itineraries = buildItineraries(ctx.matchingSegmentRefs, rec, flightMap, baggageInfo);

		if (itineraries == null || itineraries.isEmpty()) {
			LOG.warn("No itineraries built for product {}, skipping", uniqueRef);
			return null;
		}

		RateGroup rateGroup = new RateGroup();
		RateDetail rateDetail = new RateDetail();
		/// //
		rateDetail.setPrice(totalAmount.floatValue());
		rateDetail.setTaxes(taxes);
		rateDetail.setBasePrice(baseAmount.floatValue());
		/// /
		rateDetail.setItinerary(itineraries);
		rateGroup.setRates(Collections.singletonList(rateDetail));
		/////
		ProductType productTypeR = new ProductType();
		productTypeR.setProductTypeId(40L);
		productTypeR.setCode("amadeus");
		productTypeR.setName("Amadeus");
		rateGroup.setProducType(productTypeR);
		rateGroup.setTags(new ArrayList<>());
		rateGroup.getTags().add("PAX1:ADT");


		List<Person> beneficiaries = new ArrayList<>();

		NaturalPerson naturalPerson = new NaturalPerson();
		naturalPerson.setRef("PAX1:ADT");
		beneficiaries.add(naturalPerson);
		rateGroup.setBeneficiaries(beneficiaries);
		/// //
		product.setRateGroups(Collections.singletonList(rateGroup));
		product.getRateGroups().get(0).setReference(finalReference);
		// --- Supplier ---
		product.setSupplier(buildSupplier(ctx.matchingSegmentRefs, flightMap));

		return product;
	}

	*/
/**
	 * Builds Itineraries using ONLY the segment refs associated with the specific Offer.
	 *//*

	private List<Itinerary> buildItineraries(List<ReferenceInfoType> segmentRefs,
											 Recommendation rec,
											 Map<String, FlightIndex.GroupOfFlights> flightMap,
											 Map<String, String> baggageMap) {
		String itemNumber = (rec.getItemNumber() != null && rec.getItemNumber().getItemNumberId() != null)
				? rec.getItemNumber().getItemNumberId().getNumber() : "0";
		String baggageInfo = baggageMap.get(itemNumber);

		List<Itinerary> itineraryList = new ArrayList<>();

		// 1. Organize Segments by Bound (Qualifier "B")
		// Map<BoundNumber, List<SegmentRefObject>>
		Map<Integer, List<ReferenceInfoType>> boundMap = new TreeMap<>();

		for (ReferenceInfoType refInfo : segmentRefs) {
			Integer boundIndex = null;
			if (refInfo.getReferencingDetail() != null) {
				for (var detail : refInfo.getReferencingDetail()) {
					if ("B".equals(detail.getRefQualifier())) {
						boundIndex = detail.getRefNumber().intValue();
					}
				}
			}
			// If B is missing, default to 1
			if (boundIndex == null) boundIndex = 1;

			boundMap.computeIfAbsent(boundIndex, k -> new ArrayList<>()).add(refInfo);
		}

		// 2. Prepare Fare Details (RBD/Cabin)
		// Note: PaxFareProduct often contains details per segment index.
		// We might need to map based on the order of appearance or segment ref.
		// For simplicity here, we assume one cabin/farebasis applies generally or extract from first available.
		Recommendation.PaxFareProduct.FareDetails fareDetails = null;
		if(rec.getPaxFareProduct() != null && !rec.getPaxFareProduct().isEmpty()) {
			// Simply taking the first fare detail block for metadata
			if(rec.getPaxFareProduct().get(0).getFareDetails() != null &&
					!rec.getPaxFareProduct().get(0).getFareDetails().isEmpty()) {
				fareDetails = rec.getPaxFareProduct().get(0).getFareDetails().get(0);
			}
		}

		// 3. Build an Itinerary for each Bound
		for (Map.Entry<Integer, List<ReferenceInfoType>> entry : boundMap.entrySet()) {
			Integer boundId = entry.getKey();
			List<ReferenceInfoType> boundSegments = entry.getValue();

			Itinerary itinerary = new Itinerary();
			itinerary.setSort(boundId);

			List<SpaceTimePoint> stps = new ArrayList<>();
			StringBuilder nameSb = new StringBuilder();
			int priority = 1;

			for (ReferenceInfoType segRef : boundSegments) {
				// Find Flight Proposal Ref (Qualifier "S")
				String flightProposalRef = null;
				if(segRef.getReferencingDetail() != null) {
					for(var det : segRef.getReferencingDetail()) {
						if("S".equals(det.getRefQualifier())) {
							flightProposalRef = det.getRefNumber().toString();
						}
					}
				}

				if (flightProposalRef != null && flightMap.containsKey(flightProposalRef)) {
					FlightIndex.GroupOfFlights gof = flightMap.get(flightProposalRef);

					// A GroupOfFlights usually represents one leg, but could contain stops.
					// Usually FlightDetails list size is 1 for a single leg.
					if (gof.getFlightDetails() != null) {
						for (FlightIndex.GroupOfFlights.FlightDetails fd : gof.getFlightDetails()) {
							String fNum = fd.getFlightInformation().getFlightOrtrainNumber();
							if(nameSb.length() > 0) nameSb.append("/");
							nameSb.append(fNum);

							stps.add(buildSpaceTimePoint(fd, priority++, fareDetails, baggageInfo));
						}
					}
				}
			}

			if (!stps.isEmpty()) {
				itinerary.setName(nameSb.toString());
				itinerary.setSpaceTimePoints(stps);
				buildItineraryTags0(itinerary, fareDetails);
				itineraryList.add(itinerary);
			}
		}

		return itineraryList;
	}

	private Map<String, FlightIndex.GroupOfFlights> indexFlights(FareMasterPricerTravelBoardSearchReply reply) {
		Map<String, FlightIndex.GroupOfFlights> map = new HashMap<>();
		if (reply.getFlightIndex() == null) return map;

		// Flatten the flight index.
		// Key = FlightProposal Ref ("1", "2", "1605" etc from the XML <propFlightGrDetail><flightProposal><ref>)
		for (FlightIndex fi : reply.getFlightIndex()) {
			if (fi.getGroupOfFlights() != null) {
				for (FlightIndex.GroupOfFlights gof : fi.getGroupOfFlights()) {
					if (gof.getPropFlightGrDetail() != null && gof.getPropFlightGrDetail().getFlightProposal() != null) {
						// The reference is usually in the first proposal element
						String refId = gof.getPropFlightGrDetail().getFlightProposal().get(0).getRef();
						map.put(refId, gof);
					}
				}
			}
		}
		return map;
	}

	// --- Supplier Extraction ---
	private LegalPerson buildSupplier(List<ReferenceInfoType> segmentRefs, Map<String, FlightIndex.GroupOfFlights> flightMap) {
		LegalPerson supplier = new LegalPerson();
		supplier.setId(16L);
		supplier.setCurrency(Currency.getInstance("EUR"));

		// Try to find the first segment to get the Marketing Carrier
		if (segmentRefs != null && !segmentRefs.isEmpty()) {
			// Check the first one
			ReferenceInfoType firstRef = segmentRefs.get(0);
			String sRef = null;
			if (firstRef.getReferencingDetail() != null) {
				for(var det : firstRef.getReferencingDetail()) {
					if("S".equals(det.getRefQualifier())) sRef = det.getRefNumber().toString();
				}
			}

			if (sRef != null && flightMap.containsKey(sRef)) {
				var gof = flightMap.get(sRef);
				if (gof.getFlightDetails() != null && !gof.getFlightDetails().isEmpty()) {
					var info = gof.getFlightDetails().get(0).getFlightInformation();
					if (info.getCompanyId() != null) {
						String code = info.getCompanyId().getMarketingCarrier();
						supplier.setName(code);
						supplier.setLogo(code);
					}
				}
			}
		}
		return supplier;
	}

	// --- Standard Builders (Same as before) ---

	private SpaceTimePoint buildSpaceTimePoint(FlightIndex.GroupOfFlights.FlightDetails fd,
											   int priority,
											   Recommendation.PaxFareProduct.FareDetails fareData,
											   String baggageInfo) {
		SpaceTimePoint stp = new SpaceTimePoint();
		TravelProductType fi = fd.getFlightInformation();
		stp.setPriority(priority);

		if (fi.getLocation() != null && fi.getLocation().size() >= 2) {
			String depTerm = fi.getLocation().get(0).getTerminal() != null ? ":" + fi.getLocation().get(0).getTerminal() : "";
			String arrTerm = fi.getLocation().get(1).getTerminal() != null ? ":" + fi.getLocation().get(1).getTerminal() : "";
			stp.setDfrom(fi.getLocation().get(0).getLocationId() + depTerm);
			stp.setDto(fi.getLocation().get(1).getLocationId() + arrTerm);
		}

		if (fi.getProductDateTime() != null) {
			stp.setFromDate(parseDate(fi.getProductDateTime().getDateOfDeparture()));
			stp.setFromTime(formatTime(fi.getProductDateTime().getTimeOfDeparture()));
			stp.setToDate(parseDate(fi.getProductDateTime().getDateOfArrival()));
			stp.setToTime(formatTime(fi.getProductDateTime().getTimeOfArrival()));
			stp.setDuration(computeDuration(fi.getProductDateTime().getTimeOfDeparture(), fi.getProductDateTime().getTimeOfArrival()));
		}

		stp.setTags(buildSegmentTags(fi, fareData, baggageInfo));
		return stp;
	}

	private List<String> buildSegmentTags(TravelProductType fi, Recommendation.PaxFareProduct.FareDetails fareData, String baggageInfo) {
		List<String> tags = new ArrayList<>();

		String marketingCarrier = (fi.getCompanyId() != null) ? fi.getCompanyId().getMarketingCarrier() : "";
		String operatingCarrier = (fi.getCompanyId() != null) ? fi.getCompanyId().getOperatingCarrier() : "";
		String flightNumber = fi.getFlightOrtrainNumber();
		String equipment = (fi.getProductDetail() != null) ? fi.getProductDetail().getEquipmentType() : "";

		String cabinName = "ECONOMY";
		String rbd = "Y";
		String fareBasis = "";

		if (fareData != null && fareData.getGroupOfFares() != null && !fareData.getGroupOfFares().isEmpty()) {
			var prodInfo = fareData.getGroupOfFares().get(0).getProductInformation();
			if (prodInfo != null && prodInfo.getCabinProduct() != null && !prodInfo.getCabinProduct().isEmpty()) {
				String cabinCode = prodInfo.getCabinProduct().get(0).getCabin();
				cabinName = getCabinName(cabinCode);
				rbd = prodInfo.getCabinProduct().get(0).getRbd();
			}
			if (prodInfo != null && prodInfo.getFareProductDetail() != null) {
				fareBasis = prodInfo.getFareProductDetail().getFareBasis();
			}
		}

		tags.add(null);
		tags.add(cabinName + ":" + rbd);
		tags.add(marketingCarrier + flightNumber);
		tags.add("Vol opéré par " + operatingCarrier);
		tags.add("Vol marketer par " + marketingCarrier);
		tags.add(equipment);
		tags.add(baggageInfo != null ? "Baggage : Checked , " + baggageInfo : "Baggage : Checked , quantite 0 pieces");
		tags.add(fareBasis);

		tags.add(parseBagsDetail(baggageInfo));
		tags.add("flightNumber:" + marketingCarrier + flightNumber);
		tags.add("equipment:" + equipment);
		tags.add("fareBasis:" + fareBasis);
		tags.add("avc:" + marketingCarrier);
		tags.add("aft:" + rbd);

		return tags;
	}

	private void buildItineraryTags0(Itinerary itinerary, Recommendation.PaxFareProduct.FareDetails fareData) {
		List<String> tags = new ArrayList<>();
		tags.add(itinerary.getName());
		tags.add(null);

		String cabin = "ECONOMY";
		String fareBasis = "";

		if (fareData != null && fareData.getGroupOfFares() != null && !fareData.getGroupOfFares().isEmpty()) {
			var prodInfo = fareData.getGroupOfFares().get(0).getProductInformation();
			if (prodInfo.getCabinProduct() != null && !prodInfo.getCabinProduct().isEmpty()) {
				cabin = getCabinName(prodInfo.getCabinProduct().get(0).getCabin());
			}
			if (prodInfo.getFareProductDetail() != null) {
				fareBasis = prodInfo.getFareProductDetail().getFareBasis();
			}
		}

		tags.add(cabin);
		tags.add("fareBasis:" + fareBasis);
		tags.add("fareBasisAll:" + fareBasis);

		itinerary.setTags(tags);
	}

	private Price createPrice(BigDecimal value, String scope, String currency) {
		Price p = new Price();
		p.setValue(value.floatValue());
		if (scope.equals("PURCHASE_TOTAL_BASE")) p.setScope(PriceScope.PURCHASE_TOTAL_PRICE);
		if (scope.equals("TOTAL")) p.setScope(PriceScope.TOTAL);
		if (scope.equals("TAX_TOTAL")) p.setScope(PriceScope.TAX_TOTAL);
		p.setCurrency(Currency.getInstance(currency));
		return p;
	}

	private String getCabinName(String code) {
		if (code == null) return "ECONOMY";
		switch (code.toUpperCase()) {
			case "C": case "J": return "BUSINESS";
			case "F": return "FIRST";
			case "W": return "PREMIUM ECONOMY";
			case "M": case "Y": default: return "ECONOMY";
		}
	}

	private String parseBagsDetail(String baggageInfo) {
		String qty = "0";
		String type = "N";
		if (baggageInfo != null) {
			if (baggageInfo.contains("pieces")) {
				qty = baggageInfo.replaceAll("[^0-9]", "");
				type = "N";
			} else if (baggageInfo.contains("kg")) {
				qty = baggageInfo.replaceAll("[^0-9]", "");
				type = "K";
			}
		}
		return "bagsDetail:" + qty + ":" + type + ":1:adult";
	}

	private Map<String, String> indexBaggage(FareMasterPricerTravelBoardSearchReply reply) {
		Map<String, String> map = new HashMap<>();
		if (reply.getServiceFeesGrp() == null) return map;
		for (var sfg : reply.getServiceFeesGrp()) {
			if (sfg.getFreeBagAllowanceGrp() != null) {
				for (var bagGrp : sfg.getFreeBagAllowanceGrp()) {
					var details = bagGrp.getFreeBagAllownceInfo().getBaggageDetails();
					String qty = details.getFreeAllowance().toString();
					String type = "N".equals(details.getQuantityCode()) ? "pieces" : "kg";
					String bagStr = "quantite " + qty + " " + type;
					if (bagGrp.getItemNumberInfo() != null && bagGrp.getItemNumberInfo().getItemNumberDetails() != null) {
						String recRef = String.valueOf(bagGrp.getItemNumberInfo().getItemNumberDetails().get(0).getNumber());
						map.put(recRef, bagStr);
					}
				}
			}
		}
		return map;
	}

	private Date parseDate(String amadeusDate) {
		try {
			return Date.from(LocalDate.parse(amadeusDate, DATE_FMT_AMADEUS).atStartOfDay(ZoneId.systemDefault()).toInstant());
		} catch (Exception e) {
			return null;
		}
	}

	private String formatTime(String t) {
		if (t == null || t.length() < 4) return t;
		return t.substring(0, 2) + ":" + t.substring(2, 4);
	}

	private String computeDuration(String dep, String arr) {
		try {
			Duration d = Duration.between(LocalTime.parse(dep, TIME_FMT), LocalTime.parse(arr, TIME_FMT));
			if (d.isNegative()) d = d.plusHours(24);
			return d.toString();
		} catch (Exception e) {
			return "PT00H00M";
		}
	}
}*/
