package com.fractalite.amad_ndc.contollers;

import com.amadeus.travelManagement.amadeus.xml._2010._06.travel_ordercancelrq_v1.AMATravelOrderCancelRQ;
import com.amadeus.travelManagement.amadeus.xml._2010._06.travel_ordercancelrs_v1.AMATravelOrderCancelRS;
import com.amadeus.travelManagement.amadeus.xml._2010._06.travel_ordercreaterq_v1.AMATravelOrderCreateRQ;
import com.amadeus.travelManagement.amadeus.xml._2010._06.travel_orderretrieverq_v1.AMATravelOrderRetrieveRQ;
import com.amadeus.travelManagement.amadeus.xml._2010._06.travel_orderviewrs_v1.AMATravelOrderViewRS;
import com.amadeus.travelManagement.amadeus.xml._2010._06.travel_orderpayrq_v1.AMATravelOrderPayRQ;
import com.fractalite.amad_ndc.convertersRQ.AMA_TravelOrderCancelBuilder;
import com.fractalite.amad_ndc.convertersRQ.AMA_TravelOrderCreateRQ;
import com.fractalite.amad_ndc.convertersRQ.AMA_TravelOrderPayRQ;
import com.fractalite.amad_ndc.convertersRQ.AMA_TravelOrderRetrieveBuilder;
import com.fractalite.amad_ndc.convertersRS.AMA_TravelOrderPayRS;
import com.fractalite.amad_ndc.convertersRS.TravelOrderCreateRS;
import com.fractalite.amad_ndc.services.CrmService;
import com.fractalite.amad_ndc.services.HeadersServices;
import com.fractalite.mercure.data.model.bo.Item;
import com.fractalite.mercure.data.model.bo.Order;
import com.fractalite.mercure.data.model.bo.OrderStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;
import javax.xml.soap.SOAPMessage;
import java.util.Collections;
import java.util.Map;

@RestController
@CrossOrigin(origins = "*")
@RequestMapping(value = "/api", produces = MediaType.APPLICATION_JSON_VALUE)
public class OrderController {

    private static final Logger LOG = LoggerFactory.getLogger(OrderController.class);

    private final AMA_TravelOrderCreateRQ amaTravelOrderCreateRQ;
    private final HeadersServices headersServices;
    private final TravelOrderCreateRS travelOrderCreateRS;
    private final CrmService crmService;
    private final AMA_TravelOrderRetrieveBuilder amaTravelOrderRetrieveBuilder;
    private final AMA_TravelOrderCancelBuilder amaTravelOrderCancelBuilder;
    private final AMA_TravelOrderPayRQ amaTravelOrderPayRQ;
    private final AMA_TravelOrderPayRS amaTravelOrderPayRS;

    public OrderController(AMA_TravelOrderCreateRQ amaTravelOrderCreateRQ,
                           HeadersServices headersServices,
                           TravelOrderCreateRS travelOrderCreateRS,
                           CrmService crmService,
                           AMA_TravelOrderRetrieveBuilder amaTravelOrderRetrieveBuilder,
                           AMA_TravelOrderCancelBuilder amaTravelOrderCancelBuilder,
                           AMA_TravelOrderPayRQ amaTravelOrderPayRQ,
                           AMA_TravelOrderPayRS amaTravelOrderPayRS) {
        this.amaTravelOrderCreateRQ = amaTravelOrderCreateRQ;
        this.headersServices = headersServices;
        this.travelOrderCreateRS = travelOrderCreateRS;
        this.crmService = crmService;
        this.amaTravelOrderRetrieveBuilder = amaTravelOrderRetrieveBuilder;
        this.amaTravelOrderCancelBuilder = amaTravelOrderCancelBuilder;
        this.amaTravelOrderPayRQ = amaTravelOrderPayRQ;
        this.amaTravelOrderPayRS = amaTravelOrderPayRS;
    }

    // =========================================================================
    // CREATE ORDER
    // =========================================================================

    /**
     * Create a booking in Amadeus and persist to CRM.
     * POST /api/create-order?crm=9
     */
    @PostMapping("/create-order")
    public ResponseEntity<?> createOrder(@RequestBody Order order,
                                         @RequestParam("crm") String crm) throws Exception {

        AMATravelOrderCreateRQ request = amaTravelOrderCreateRQ.buildAMA_TravelOrderCreateRQ(order);
        SOAPMessage soapResponse = headersServices.getCreateOrder(request);
        AMATravelOrderViewRS reply = unmarshalResponse(soapResponse);

        if (reply == null) {
            return ResponseEntity.internalServerError()
                    .body(Map.of("error", "Amadeus returned no response for create-order"));
        }

        travelOrderCreateRS.buildOrderView(order, reply);
        crmService.putOrder(order, crm);   // ← use actual crm param

        return ResponseEntity.ok(order);
    }

    // =========================================================================
    // RETRIEVE ORDER
    // =========================================================================

    /**
     * Retrieve a booking from Amadeus by PNR and refresh the CRM record.
     * GET /api/order/{orderId}?crm=9
     */
    @GetMapping(path = "/order/{orderId}", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<?> orderRetrieve(@PathVariable("orderId") String orderId,
                                           @RequestParam("crm") String crm) throws Exception {
        LOG.info("---------- Retrieve order {} ---------------", orderId);

        Order order = crmService.getOrderById(orderId, crm);
        if (order == null) {
            return ResponseEntity.notFound().build();
        }

        // Derive ownerCode from the first item tag, e.g. "vol operé par AF" → "AF"
        String ownerCode = extractOwnerCode(order);

        String bookingId = extractBookingId(order);
        AMATravelOrderRetrieveRQ request = amaTravelOrderRetrieveBuilder.buildOrderRetriveRequest("1A", bookingId);
        SOAPMessage soapResponse = headersServices.getOrder(request);
        AMATravelOrderViewRS reply = unmarshalResponse(soapResponse);

        if (reply != null && reply.getResponse() != null && reply.getResponse().getOrder() != null) {
            travelOrderCreateRS.buildOrderView(order, reply);
            crmService.putOrder(order, crm);
        }

        return ResponseEntity.ok(order);
    }

    // =========================================================================
    // CONFIRM PAYMENT
    // =========================================================================

    /**
     * Confirm payment for an existing order.
     * POST /api/confirm-payment?crm=9
     */
    @PostMapping("/confirm-payment")
    public ResponseEntity<?> confirmPayment(@RequestBody Order order,
                                            @RequestParam("crm") String crm) throws Exception {
        LOG.info("---------- Confirm payment for order {} ---------------", order.getNumber());

        AMATravelOrderPayRQ request = amaTravelOrderPayRQ.buildAMA_TravelOrderPayRQ(order);
        SOAPMessage soapResponse = headersServices.getPayOrder(request);

        if (soapResponse == null) {
            return ResponseEntity.internalServerError()
                    .body(Map.of("error", "Amadeus returned no response for confirm-payment"));
        }

        AMATravelOrderViewRS reply = unmarshalResponse(soapResponse);
        if (reply != null) {
            amaTravelOrderPayRS.buildOrderView(reply, order);
        }

        // Mark all items as Confirmed
        if (order.getItems() != null) {
            for (Item item : order.getItems()) {
                item.setStatus(OrderStatus.Confirmed);
            }
        }

        crmService.putOrder(order, crm);
        return ResponseEntity.ok(order);
    }

    // =========================================================================
    // CANCEL ORDER
    // =========================================================================

    /**
     * Cancel an order without requiring prior confirmation.
     * DELETE /api/services/cancel/{ref}?crm=9
     */
    @DeleteMapping("/services/cancel/{ref}")
    public ResponseEntity<?> cancelPnr(@PathVariable(name = "ref") String orderNumber,
                                       @RequestParam(name = "crm") String crm) throws Exception {
        LOG.info("------------ Cancel order {} ------------------", orderNumber);

        Order order = crmService.getOrderById(orderNumber, crm);
        if (order == null) {
            return ResponseEntity.notFound().build();
        }

        // Derive ownerCode dynamically (avoid hardcoding "AF")
        String ownerCode = extractOwnerCode(order);
        String amadeusPnr = (order.getItems() != null && !order.getItems().isEmpty())
                ? order.getItems().get(0).getNumber()
                : orderNumber;

        AMATravelOrderCancelRQ request = amaTravelOrderCancelBuilder
                .buildOrderCancelRequest(ownerCode, amadeusPnr);
        SOAPMessage soapResponse = headersServices.cancelOrder(request);
        AMATravelOrderCancelRS reply = unmarshalResponseCancel(soapResponse);

        if (reply != null && reply.getResponse() != null && reply.getResponse().getOrderRefID() != null) {
            if (order.getItems() != null) {
                for (Item item : order.getItems()) {
                    item.setStatus(OrderStatus.Cancelled);
                }
            }
            crmService.putOrder(order, crm);
            return ResponseEntity.ok(Map.of("status", "cancelled", "orderId", orderNumber));
        }

        String errorMsg = (reply != null && reply.getError() != null && !reply.getError().isEmpty())
                ? reply.getError().get(0).getDescText()
                : "Unknown error from Amadeus";
        return ResponseEntity.badRequest().body(Map.of("error", errorMsg));
    }

    // =========================================================================
    // SOAP unmarshallers
    // =========================================================================

    public AMATravelOrderViewRS unmarshalResponse(SOAPMessage soapResponse) {
        try {
            if (soapResponse == null) { LOG.error("SOAP Response is null"); return null; }
            Node bodyNode = findFirstElement(soapResponse.getSOAPBody().getChildNodes());
            if (bodyNode == null) throw new Exception("No XML element found inside SOAP Body");
            JAXBContext ctx = JAXBContext.newInstance(AMATravelOrderViewRS.class);
            Unmarshaller u = ctx.createUnmarshaller();
            return u.unmarshal(bodyNode, AMATravelOrderViewRS.class).getValue();
        } catch (Exception e) {
            LOG.error("Error Unmarshalling OrderViewRS: ", e);
            return null;
        }
    }

    public AMATravelOrderCancelRS unmarshalResponseCancel(SOAPMessage soapResponse) {
        try {
            if (soapResponse == null) { LOG.error("SOAP Response (cancel) is null"); return null; }
            Node bodyNode = findFirstElement(soapResponse.getSOAPBody().getChildNodes());
            if (bodyNode == null) throw new Exception("No XML element found inside SOAP Body (cancel)");
            JAXBContext ctx = JAXBContext.newInstance(AMATravelOrderCancelRS.class);
            Unmarshaller u = ctx.createUnmarshaller();
            return u.unmarshal(bodyNode, AMATravelOrderCancelRS.class).getValue();
        } catch (Exception e) {
            LOG.error("Error Unmarshalling OrderCancelRS: ", e);
            return null;
        }
    }

    // =========================================================================
    // Helpers
    // =========================================================================

    /**
     * Extract airline owner code from order item tags.
     * Tags contain strings like "Vol opéré par AF" — we parse the last token.
     * Falls back to "1A" (Amadeus GDS code).
     */
    private String extractOwnerCode(Order order) {
        if (order.getItems() == null || order.getItems().isEmpty()) return "1A";
        try {
            Item firstItem = order.getItems().get(0);
            if (firstItem.getItinerary() != null && !firstItem.getItinerary().isEmpty()) {
                var stps = firstItem.getItinerary().get(0).getSpaceTimePoints();
                if (stps != null && !stps.isEmpty() && stps.get(0).getTags() != null) {
                    for (String tag : stps.get(0).getTags()) {
                        if (tag != null && tag.toLowerCase().contains("op")) {
                            String[] parts = tag.split(" ");
                            if (parts.length > 0) return parts[parts.length - 1].trim().toUpperCase();
                        }
                    }
                }
            }
        } catch (Exception e) {
            LOG.warn("Could not extract ownerCode from order tags, using 1A", e);
        }
        return "1A";
    }

    private String extractBookingId(Order order) {

        for (Item item : order.getItems()) {
            if(item.getProductType().getCode().equalsIgnoreCase("amadeus")){

                return item.getRef();

            }
        }

        return null;

    }

    private static Node findFirstElement(NodeList nodeList) {
        for (int i = 0; i < nodeList.getLength(); i++) {
            if (nodeList.item(i).getNodeType() == Node.ELEMENT_NODE) return nodeList.item(i);
        }
        return null;
    }
}
