package com.nebula.erp.sales.controller;

import com.nebula.erp.sales.document.SalesSwagger;
import com.nebula.erp.sales.model.Sales;
import com.nebula.erp.sales.repository.PaymentHistoryRepository;
import com.nebula.erp.sales.model.PaymentHistory;
import com.nebula.erp.sales.model.Payments;
import com.nebula.erp.sales.requestmodel.PaymentCreditRequest;
import com.nebula.erp.sales.requestmodel.PaymentDebitRequest;
import com.nebula.erp.sales.requestmodel.SalesRequest;
import com.nebula.erp.sales.service.PaymentHistoryService;
import com.nebula.erp.sales.service.SalesService;
import com.nebula.erp.sales.utility.*;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestTemplate;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api/sales")
@Tag(name = "Sales APIs", description = "API for managing Sales Items and Payments")
@SalesSwagger.GlobalErrorResponse
public class SalesController {

    @Autowired
    private SalesService salesService;

    @Autowired
    private RestTemplate restTemplate;

    @Value("${organization.api}")
    private String organizationAPI;

    @Value("${inventory.api}")
    private String inventoryAPI;

    @Value("${product.api}")
    private String productAPI;

    @Value("${service.api}")
    private String serviceAPI;

    @Value("${package.api}")
    private String packageAPI;

    @Value("${patient.api}")
    private String patientAPI;

    @Value("${room.api}")
    private String roomAPI;

    @Autowired
    private JwtRequestUtils jwtRequestUtils;

    @Autowired
    private PermissionHelper permissionHelper;

    @Autowired
    private CreateLogger createLogger;

    @Autowired
    private PaymentHistoryRepository paymentHistoryRepository;

    private static final String path = "/sales";
    @Autowired
    private PaymentHistoryService paymentHistoryService;

    // Create Sales with items and optionally handle payments
    @SalesSwagger.CreateSalesOperation
    @PostMapping
    public ResponseEntity<ApiResponseStructure<Sales>> createSales(
            @RequestBody SalesRequest salesRequest) {
        try {
            // Check user permissions
            if (!permissionHelper.hasPermission("create-sales")) {
                createLogger.createLogger("error", path, "POST",
                        "Forbidden: You do not have the required permission. Please contact the administration.",
                        "validation");
                return ResponseEntity.status(HttpStatus.FORBIDDEN)
                        .body(new ApiResponseStructure<>("error", HttpStatus.FORBIDDEN.value(),
                                "Forbidden: You do not have the required permission. Please contact the administration.",
                                null));
            }

            Sales createdSales = salesService.createSales(salesRequest);
            // createdSales.setPayment_status("UNPAID");
            // salesService.save(createdSales);

            // Extract amount_paid from request
            double amountPaid = salesRequest.getAmount_paid() == null ? 0 : salesRequest.getAmount_paid();
            String tenant = jwtRequestUtils.getTenantName();
            PaymentDebitRequest debitReq = new PaymentDebitRequest();
            debitReq.setSales_id(createdSales.getId());
            debitReq.setPatient_id(createdSales.getPatient_id());
            debitReq.setMrn_no(createdSales.getMrn_no() == null ? "-" : createdSales.getMrn_no());
            debitReq.setEncounter_id(createdSales.getEncounter_id());
            debitReq.setCustom_encounter_id(createdSales.getCustomEncounterId());
            debitReq.setAmount(createdSales.getTotal_amount());

            // paymentHistoryService.addDebit(debitReq);

            double availableAdvance = paymentHistoryRepository
                    .findOriginalAdvance(createdSales.getPatient_id(), tenant);

            double billAmount = createdSales.getTotal_amount();
            double advanceUsed = 0;

            if (availableAdvance > 0) {
                advanceUsed = Math.min(availableAdvance, billAmount);

                if (advanceUsed > 0) {
                    PaymentDebitRequest advUseReq = new PaymentDebitRequest();
                    advUseReq.setSales_id(createdSales.getId());
                    advUseReq.setPatient_id(createdSales.getPatient_id());
                    advUseReq.setMrn_no(createdSales.getMrn_no() == null ? "-" : createdSales.getMrn_no());
                    advUseReq.setEncounter_id(createdSales.getEncounter_id());
                    advUseReq.setCustom_encounter_id(createdSales.getCustomEncounterId());
                    advUseReq.setAdvance_used(advanceUsed);

                    // paymentHistoryService.addAdvanceUsed(advUseReq);
                }
            }

            double creditPaid = 0;

            if (amountPaid > 0) {

                PaymentCreditRequest creditReq = new PaymentCreditRequest();
                creditReq.setSales_id(createdSales.getId());
                creditReq.setPatient_id(createdSales.getPatient_id());
                creditReq.setMrn_no(createdSales.getMrn_no() == null ? "-" : createdSales.getMrn_no());
                creditReq.setEncounter_id(createdSales.getEncounter_id());
                creditReq.setCustom_encounter_id(createdSales.getCustomEncounterId());
                creditReq.setAmount(amountPaid);

                creditPaid = amountPaid;
            }

            double totalPaid = advanceUsed + creditPaid;

            ApiResponseStructure<Sales> response = new ApiResponseStructure<>("success", HttpStatus.CREATED.value(),
                    "Sales created successfully", createdSales);

            createLogger.createLogger("application", path, "POST", "Sales created successfully + Debit Posted", "");
            return ResponseEntity.status(HttpStatus.CREATED).body(response);

        } catch (

        Exception e) {
            createLogger.createLogger("error", path, "POST", e.getMessage(), "runtime");
            return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                    .body(new ApiResponseStructure<>("error", HttpStatus.BAD_REQUEST.value(), e.getMessage(), null));
        }
    }

    @SalesSwagger.GetSalesByIdOperation
    @GetMapping("/{id}")
    public ResponseEntity<ApiResponseStructure<Map<String, Object>>> getSalesById(@PathVariable Long id) {

        try {
            // Check user permissions
            if (!permissionHelper.hasPermission("view-sales")) {
                createLogger.createLogger("error", path, "GET",
                        "Forbidden: You do not have the required permission. Please contact the administration.",
                        "validation");
                return ResponseEntity.status(HttpStatus.FORBIDDEN)
                        .body(new ApiResponseStructure<>("error", HttpStatus.FORBIDDEN.value(),
                                "Forbidden: You do not have the required permission. Please contact the administration.",
                                null));
            }

            Optional<Sales> salesOptional = salesService.getSales(id);
            if (salesOptional.isPresent()) {
                Sales salesData = salesOptional.get();

                // Create a map to hold sales data
                Map<String, Object> data = prepareSalesResponse(salesData);

                // Define columns for sales items
                Map<String, String> salesItemColumns = new LinkedHashMap<>();
                salesItemColumns.put("id", "ID");
                salesItemColumns.put("product_id", "PRODUCT ID");
                salesItemColumns.put("product_name", "PRODUCT NAME");
                salesItemColumns.put("brand_id", "BRAND ID");
                salesItemColumns.put("brand_name", "BRAND NAME");
                salesItemColumns.put("category_id", "CATEGORY ID");
                salesItemColumns.put("category_name", "CATEGORY NAME");
                salesItemColumns.put("quantity", "QUANTITY");
                salesItemColumns.put("batch_code", "BATCH CODE");
                salesItemColumns.put("unit_price", "UNIT PRICE");
                salesItemColumns.put("total_price", "TOTAL PRICE");
                salesItemColumns.put("tax_id", "TAX ID");
                salesItemColumns.put("tax_amount", "TAX AMOUNT");
                salesItemColumns.put("tax_rate", "TAX RATE");
                salesItemColumns.put("created_by", "CREATED BY");
                salesItemColumns.put("created_at", "CREATED AT");
                data.put("columns", salesItemColumns);

                // Create response structure
                ApiResponseStructure<Map<String, Object>> response = new ApiResponseStructure<>("success",
                        HttpStatus.OK.value(), "Sales retrieved successfully", data);
                createLogger.createLogger("application", path, "GET", "Sales retrieved successfully", "");
                return ResponseEntity.ok(response);
            } else {
                createLogger.createLogger("error", path, "GET", "Sales not found", "runtime");
                return ResponseEntity.status(HttpStatus.NOT_FOUND).body(
                        new ApiResponseStructure<>("error", HttpStatus.NOT_FOUND.value(), "Sales not found", null));
            }
        } catch (Exception e) {
            createLogger.createLogger("error", path, "GET", e.getMessage(), "runtime");
            return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                    .body(new ApiResponseStructure<>("error", HttpStatus.BAD_REQUEST.value(), e.getMessage(), null));
        }
    }

    @GetMapping("/by-prescription/{prescriptionId}")
    public ResponseEntity<?> getSaleByPrescription(@PathVariable String prescriptionId) {

        Sales sale = salesService.getSaleByPrescriptionId(prescriptionId);

        if (sale == null) {
            return ResponseEntity.status(404).body(Map.of(
                    "status", "error",
                    "message", "Sale not found for prescription id " + prescriptionId));
        }

        return ResponseEntity.ok(Map.of(
                "status", "success",
                "data", sale));
    }

    // Get sales count
    @GetMapping("/count")
    public ResponseEntity<ApiResponseStructure<Integer>> getSalesCount(
            @RequestParam(value = "tenant", required = true) String tenant_name) {
        try {
            // Check user permissions
            if (!permissionHelper.hasPermission("view-sales")) {
                createLogger.createLogger("error", path, "GET",
                        "Forbidden: You do not have the required permission. Please contact the administration.",
                        "validation");
                return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ApiResponseStructure<>("error",
                        HttpStatus.FORBIDDEN.value(),
                        "Forbidden: You do not have the required permission. Please contact the administration.",
                        null));
            }
            Integer salesCount = salesService.getSalesCount(tenant_name);
            ApiResponseStructure<Integer> response = new ApiResponseStructure<>("success", HttpStatus.OK.value(),
                    "Sales count retrieved successfully", salesCount);
            return ResponseEntity.ok(response);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // Get all Sales with pagination (including sales items and payments)
    @SalesSwagger.GetAllSalesOperation
    @GetMapping
    public ResponseEntity<ApiResponseStructure<Map<String, Object>>> getAllSales(
            @RequestParam(value = "page", defaultValue = "1") int page,
            @RequestParam(value = "size", defaultValue = "10") int size,
            @RequestParam(value = "type", required = false) String type,
            @RequestParam(value = "search", required = false) String search) {

        try {
            // Check user permissions
            if (!permissionHelper.hasPermission("view-sales")) {
                createLogger.createLogger("error", path, "GET",
                        "Forbidden: You do not have the required permission. Please contact the administration.",
                        "validation");
                return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ApiResponseStructure<>("error",
                        HttpStatus.FORBIDDEN.value(),
                        "Forbidden: You do not have the required permission. Please contact the administration.",
                        null));
            }

            // Validate pagination parameters
            if (page < 0 || size <= 0) {
                createLogger.createLogger("error", path, "GET",
                        "Page must be non-negative and size must be greater than 0.", "validation");
                throw new IllegalArgumentException("Page must be non-negative and size must be greater than 0.");
            }

            // Fetch paginated sales data from the service
            Page<Sales> salesPage = salesService.getAllSales(page, size, type, search);

            // Prepare response with pagination, sales items, and payments
            Map<String, Object> pagination = new LinkedHashMap<>();
            pagination.put("current_page", salesPage.getNumber() + 1);
            pagination.put("per_page", salesPage.getSize());
            pagination.put("total", salesPage.getTotalElements());
            pagination.put("last_page", salesPage.getTotalPages());
            pagination.put("next_page_url",
                    salesPage.hasNext() ? "/api/sales?page=" + (page + 1) + "&size=" + size : null);
            pagination.put("prev_page_url",
                    salesPage.hasPrevious() ? "/api/sales?page=" + (page - 1) + "&size=" + size : null);

            // Define columns for sales and nested sales items
            Map<String, String> columns = new LinkedHashMap<>();
            columns.put("id", "ID");
            columns.put("encounter_id", "ENCOUNTER ID");
            columns.put("patient_id", "PATIENT ID");
            columns.put("patient_name", "PATIENT NAME");
            columns.put("customer_name", "CUSTOMER NAME");
            columns.put("customer_mobile", "CUSTOMER MOBILE");
            columns.put("total_amount", "TOTAL AMOUNT");
            columns.put("payment_status", "PAYMENT STATUS");
            columns.put("created_by", "CREATED BY");
            columns.put("created_at", "CREATED AT");

            List<Map<String, Object>> salesItems = salesPage.getContent().stream()
                    .map(this::prepareSalesResponse)
                    .filter(Objects::nonNull) // Exclude prescriptions with no items
                    .collect(Collectors.toList());

            // Prepare data for response
            Map<String, Object> data = new LinkedHashMap<>();
            data.put("pagination", pagination);
            data.put("items", salesItems);
            data.put("columns", columns);
            Map<String, Object> totals = salesService.getSalesTotals();
            data.put("totals", totals);

            ApiResponseStructure<Map<String, Object>> response = new ApiResponseStructure<>("success",
                    HttpStatus.OK.value(), "Sales retrieved successfully", data);
            createLogger.createLogger("application", path, "GET", "Sales retrieved successfully", "");
            // Return the response entity
            return ResponseEntity.ok(response);
        } catch (IllegalArgumentException ex) {
            createLogger.createLogger("error", path, "GET", ex.getMessage(), "runtime");
            // Handle invalid input
            ApiResponseStructure<Map<String, Object>> response = new ApiResponseStructure<>("error",
                    HttpStatus.BAD_REQUEST.value(), ex.getMessage(), null);
            return ResponseEntity.badRequest().body(response);
        } catch (Exception e) {
            createLogger.createLogger("error", path, "GET", e.getMessage(), "runtime");
            // Handle generic errors and unexpected failures
            ApiResponseStructure<Map<String, Object>> response = new ApiResponseStructure<>("error",
                    HttpStatus.INTERNAL_SERVER_ERROR.value(), "An unexpected error occurred", null);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
        }
    }

    // Sale By Id
    @PostMapping("/{id}/pay")
    public ResponseEntity<ApiResponseStructure<String>> addPayment(
            @PathVariable Long id,
            @RequestBody PaymentCreditRequest paymentRequest) {

        try {
            Optional<Sales> saleOpt = salesService.getSales(id);
            if (saleOpt.isEmpty()) {
                return ResponseEntity.status(HttpStatus.NOT_FOUND)
                        .body(new ApiResponseStructure<>("error", 404, "Sale not found", null));
            }

            Sales sale = saleOpt.get();
            double total = sale.getTotal_amount();

            // Add credit entry first
            paymentRequest.setSales_id(id);
            paymentRequest.setPatient_id(sale.getPatient_id());
            paymentRequest.setMrn_no(sale.getMrn_no());
            paymentRequest.setEncounter_id(sale.getEncounter_id());
            paymentRequest.setCustom_encounter_id(sale.getCustomEncounterId());

            paymentHistoryService.addCredit(paymentRequest);

            // 🔥 Recalculate from ledger (credit + advance)
            double totalDebit = sale.getTotal_amount();
            double totalCredit = paymentHistoryRepository.findTotalPaidForSale(id);
            double totalAdvanceUsed = paymentHistoryRepository.findAdvanceUsedForSale(id);

            double totalSettled = totalCredit + totalAdvanceUsed;
            double remaining = totalDebit - totalSettled;

            // Update sale snapshot using full ledger (credit + advance)
            sale.setTotal_paid_for_sale(totalSettled);
            sale.setRemaining_due_amount(Math.max(remaining, 0.0));

            if (remaining <= 0.001) {
                sale.setPayment_status("PAID");
            } else if (totalSettled > 0) {
                sale.setPayment_status("PARTIAL");
            } else {
                sale.setPayment_status("UNPAID");
            }
            salesService.save(sale);

            return ResponseEntity.ok(
                    new ApiResponseStructure<>("success", 200, "Payment added successfully", null));

        } catch (Exception e) {
            return ResponseEntity.status(500)
                    .body(new ApiResponseStructure<>("error", 500, e.getMessage(), null));
        }
    }

    // Get all sales by patient Id (including sales items and payments)
    @GetMapping("/by-patient/{patientId}")
    public ResponseEntity<ApiResponseStructure<List<Map<String, Object>>>> getSalesByPatientId(
            @PathVariable String patientId) {
        try {
            if (!permissionHelper.hasPermission("view-sales")) {
                createLogger.createLogger("error", path, "GET", "Forbidden: You do not have the required permission.",
                        "validation");
                return ResponseEntity.status(HttpStatus.FORBIDDEN).body(
                        new ApiResponseStructure<>("error", HttpStatus.FORBIDDEN.value(),
                                "Forbidden: You do not have the required permission.", null));
            }

            List<Sales> salesList = salesService.getSalesByPatientId(patientId);
            List<Map<String, Object>> responseItems = salesList.stream()
                    .map(this::prepareSalesResponse)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());

            return ResponseEntity.ok(new ApiResponseStructure<>("success", HttpStatus.OK.value(),
                    "Sales retrieved successfully", responseItems));
        } catch (Exception e) {
            createLogger.createLogger("error", path, "GET", e.getMessage(), "runtime");
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(
                    new ApiResponseStructure<>("error", HttpStatus.BAD_REQUEST.value(), e.getMessage(), null));
        }
    }

    @SalesSwagger.ExportSalesByIdOperation
    @GetMapping("/export/{id}")
    public ResponseEntity<ApiResponseStructure<Map<String, Object>>> exportById(@PathVariable Long id) {

        try {
            // Check user permissions
            if (!permissionHelper.hasPermission("view-sales")) {
                createLogger.createLogger("error", path, "GET",
                        "Forbidden: You do not have the required permission. Please contact the administration.",
                        "validation");
                return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ApiResponseStructure<>("error",
                        HttpStatus.FORBIDDEN.value(),
                        "Forbidden: You do not have the required permission. Please contact the administration.",
                        null));
            }

            Optional<Sales> salesOptional = salesService.getSales(id);
            if (salesOptional.isPresent()) {
                Sales salesData = salesOptional.get();

                // Create a map to hold sales data
                Map<String, Object> data = prepareExportResponse(salesData);

                // Define columns for sales items
                Map<String, String> salesItemColumns = new LinkedHashMap<>();
                salesItemColumns.put("id", "ID");
                salesItemColumns.put("product_id", "PRODUCT ID");
                salesItemColumns.put("product_name", "PRODUCT NAME");
                salesItemColumns.put("brand_id", "BRAND ID");
                salesItemColumns.put("brand_name", "BRAND NAME");
                salesItemColumns.put("category_id", "CATEGORY ID");
                salesItemColumns.put("category_name", "CATEGORY NAME");
                salesItemColumns.put("quantity", "QUANTITY");
                salesItemColumns.put("batch_code", "BATCH CODE");
                salesItemColumns.put("unit_price", "UNIT PRICE");
                salesItemColumns.put("total_price", "TOTAL PRICE");
                salesItemColumns.put("tax_id", "TAX ID");
                salesItemColumns.put("tax_amount", "TAX AMOUNT");
                salesItemColumns.put("tax_rate", "TAX RATE");
                salesItemColumns.put("created_by", "CREATED BY");
                salesItemColumns.put("created_at", "CREATED AT");
                data.put("columns", salesItemColumns);

                // Create response structure
                ApiResponseStructure<Map<String, Object>> response = new ApiResponseStructure<>("success",
                        HttpStatus.OK.value(), "Sales retrieved successfully", data);
                createLogger.createLogger("application", path, "GET", "Sales retrieved successfully", "");
                return ResponseEntity.ok(response);
            } else {
                createLogger.createLogger("error", path, "GET", "Sales not found", "runtime");
                return ResponseEntity.status(HttpStatus.NOT_FOUND).body(
                        new ApiResponseStructure<>("error", HttpStatus.NOT_FOUND.value(), "Sales not found", null));
            }
        } catch (Exception e) {
            createLogger.createLogger("error", path, "GET", e.getMessage(), "runtime");
            return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                    .body(new ApiResponseStructure<>("error", HttpStatus.BAD_REQUEST.value(), e.getMessage(), null));
        }
    }

    // UPDATE SALES (NEW)
    @PutMapping("/{id}")
    public ResponseEntity<ApiResponseStructure<Sales>> updateSales(
            @PathVariable Long id,
            @RequestBody SalesRequest request) {

        try {
            // Permission check
            if (!permissionHelper.hasPermission("update-sales")) {
                return ResponseEntity.status(HttpStatus.FORBIDDEN)
                        .body(new ApiResponseStructure<>("error", 403,
                                "Forbidden: You do not have permission to update sales.", null));
            }

            Optional<Sales> saleOpt = salesService.getSales(id);
            if (saleOpt.isEmpty()) {
                return ResponseEntity.status(HttpStatus.NOT_FOUND)
                        .body(new ApiResponseStructure<>("error", 404, "Sale not found", null));
            }

            Sales sale = saleOpt.get();

            if (request.getCustomer_name() != null)
                sale.setCustomer_name(request.getCustomer_name());

            if (request.getCustomer_mobile() != null)
                sale.setCustomer_mobile(request.getCustomer_mobile());

            if (request.getCustomer_email() != null)
                sale.setCustomer_email(request.getCustomer_email());

            if (request.getCustomer_address() != null)
                sale.setCustomer_address(request.getCustomer_address());

            if (request.getDiscount() != null)
                sale.setDiscount(request.getDiscount());

            if (request.getCoupon_code() != null)
                sale.setCoupon_code(request.getCoupon_code());

            if (request.getCoupon_amount() != null)
                sale.setCoupon_amount(request.getCoupon_amount());
            if (request.getPayment_method() != null)
                sale.setPayment_method(request.getPayment_method());

            if (request.getAmount_paid() != null
                    && request.getAmount_paid() > 0) {

                String tenant = jwtRequestUtils.getTenantName();

                double alreadyPaid = paymentHistoryRepository
                        .findTotalPaidForSale(sale.getId());
                double saleTotal = sale.getTotal_amount();
                double remainingDue = saleTotal - alreadyPaid;

                if (remainingDue > 0) {

                    double amountToApply = Math.min(request.getAmount_paid(), remainingDue);

                    PaymentCreditRequest creditReq = new PaymentCreditRequest();

                    creditReq.setSales_id(sale.getId());
                    creditReq.setPatient_id(sale.getPatient_id());
                    creditReq.setMrn_no(sale.getMrn_no());
                    creditReq.setEncounter_id(sale.getEncounter_id());
                    creditReq.setCustom_encounter_id(sale.getCustomEncounterId());
                    creditReq.setAmount(amountToApply);

                    paymentHistoryService.addCredit(creditReq);

                    alreadyPaid += amountToApply;
                }

                double newRemaining = saleTotal - alreadyPaid;

                sale.setTotal_paid_for_sale(alreadyPaid);
                sale.setRemaining_due_amount(
                        Math.max(newRemaining, 0));

                if (newRemaining <= 0) {
                    sale.setPayment_status("PAID");
                } else if (alreadyPaid > 0) {
                    sale.setPayment_status("PARTIAL");
                } else {
                    sale.setPayment_status("UNPAID");
                }
            }

            Sales updated = salesService.save(sale);

            return ResponseEntity.ok(
                    new ApiResponseStructure<>("success", 200,
                            "Sales updated successfully", updated));

        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                    .body(new ApiResponseStructure<>("error", 400, e.getMessage(), null));
        }
    }

    private Map<String, Object> calculateLedgerSnapshotForSale(Sales sale) {

        Long saleId = sale.getId();
        String patientId = sale.getPatient_id();
        String tenant = jwtRequestUtils.getTenantName();

        List<PaymentHistory> ledger = paymentHistoryRepository.findByPatient(patientId, tenant);
        ledger.sort(Comparator.comparing(PaymentHistory::getCreated_at));
        double previousDuePaid = sale.getPrevious_due_paid() != null
                ? sale.getPrevious_due_paid()
                : 0.0;

        double advanceAddedBeforeSale = 0.0;
        double advanceUsedBeforeSale = 0.0;
        double refundBeforeSale = 0.0;

        double advanceUsedForThisSale = 0.0;
        double directPaymentsForThisSale = 0.0;

        for (PaymentHistory ph : ledger) {

            // STOP when reaching this sale's debit entry
            if ("DEBIT".equals(ph.getEntry_type()) && ph.getSales_id().equals(saleId)) {
                break;
            }

            switch (ph.getEntry_type()) {
                case "ADVANCE_ADDED":
                    advanceAddedBeforeSale += ph.getAdvance();
                    break;

                case "ADVANCE_USED":
                    advanceUsedBeforeSale += ph.getAdvance_used();
                    break;

                case "REFUND":
                    refundBeforeSale += Math.abs(ph.getCredit());
                    break;
            }
        }

        double advanceAvailableBeforeSale = advanceAddedBeforeSale - advanceUsedBeforeSale - refundBeforeSale;

        if (advanceAvailableBeforeSale < 0)
            advanceAvailableBeforeSale = 0;

        // Now find entries FOR THIS SALE only
        for (PaymentHistory ph : ledger) {
            if (ph.getSales_id() != null && ph.getSales_id().equals(saleId)) {

                if ("ADVANCE_USED".equals(ph.getEntry_type())) {
                    advanceUsedForThisSale += ph.getAdvance_used();
                }

                if ("PAYMENT".equals(ph.getEntry_type())) {
                    directPaymentsForThisSale += ph.getCredit();
                }
            }
        }

        double billAmount = sale.getTotal_amount();

        double totalPaidForSale = advanceUsedForThisSale + directPaymentsForThisSale;

        double remainingDue = billAmount - totalPaidForSale;
        if (remainingDue < 0)
            remainingDue = 0;

        double advanceBalanceAfterSale = advanceAvailableBeforeSale - advanceUsedForThisSale;
        if (advanceBalanceAfterSale < 0)
            advanceBalanceAfterSale = 0;

        String status = remainingDue == 0 ? "PAID"
                : (totalPaidForSale > 0 ? "PARTIAL" : "UNPAID");

        Map<String, Object> out = new LinkedHashMap<>();
        out.put("total_advance_amount", MoneyUtils.truncateToTwoDecimals(advanceAvailableBeforeSale));
        out.put("advance_used_for_this_sale", MoneyUtils.truncateToTwoDecimals(advanceUsedForThisSale));
        out.put("advance_balance_after_sale", MoneyUtils.truncateToTwoDecimals(advanceBalanceAfterSale));
        out.put("total_debit_amount", MoneyUtils.truncateToTwoDecimals(billAmount));
        out.put("remaining_due_amount", MoneyUtils.truncateToTwoDecimals(remainingDue));
        out.put("refund_amount", MoneyUtils.truncateToTwoDecimals(refundBeforeSale));
        out.put("total_paid_for_sale", MoneyUtils.truncateToTwoDecimals(totalPaidForSale));
        out.put("previous_due_paid", MoneyUtils.truncateToTwoDecimals(previousDuePaid));
        out.put("payment_status", status);

        return out;
    }

    // Helper method to prepare response data for a sale (including sales items and
    // payments)
    private Map<String, Object> prepareSalesResponse(Sales salesData) {
        Map<String, Object> data = new LinkedHashMap<>();
        data.put("id", salesData.getId());
        data.put("prescription_id", salesData.getPrescription_id());
        data.put("encounter_id", salesData.getEncounter_id());
        data.put("custom_encounter_id", salesData.getCustomEncounterId());
        data.put("has_custom_encounter", salesData.getCustomEncounterId() != null);
        data.put("patient_id", salesData.getPatient_id());
        data.put("patient_name", salesData.getPatient_name());
        data.put("customer_name", salesData.getCustomer_name());
        data.put("customer_mobile", salesData.getCustomer_mobile());
        data.put("customer_email", salesData.getCustomer_email());
        data.put("customer_dob", salesData.getCustomer_dob());
        data.put("customer_gender", salesData.getCustomer_gender());
        data.put("customer_address", salesData.getCustomer_address());
        data.put("total_amount", MoneyUtils.truncateToTwoDecimals(salesData.getTotal_amount()));
        data.put("discount", salesData.getDiscount());
        data.put("coupon_amount", salesData.getCoupon_amount());
        data.put("coupon_code", salesData.getCoupon_code());
        data.put("payment_status", salesData.getPayment_status());
        data.put("payment_method", salesData.getPayment_method());
        // Razorpay sub-method for accounting (UPI, CREDIT_CARD, DEBIT_CARD)
        data.put("payment_sub_method", salesData.getPayment_sub_method());
        data.put("created_by", salesData.getCreated_by());
        data.put("created_at", String.valueOf(salesData.getCreated_at()));

        // Extract Authorization headers from the request
        HttpHeaders headers = jwtRequestUtils.getAuthorizationHeaders();

        // Create an HttpEntity with the extracted headers
        HttpEntity<String> entity = new HttpEntity<>(headers);

        // Map sales items
        List<Map<String, Object>> salesItemsData = salesData.getSales_items().stream()
                .map(item -> {

                    final double[] item_rate = { 0.00 };
                    final double[] item_tax_rate = { 0.00 };
                    final double[] item_quantity = { 0.00 };
                    final double[] itemAmount = { 0.00 };
                    final double[] itemGstAmount = { 0.00 };

                    Map<String, Object> itemData = new LinkedHashMap<>();
                    itemData.put("id", item.getId());
                    itemData.put("type", item.getType());
                    itemData.put("product_id", item.getProduct_id());

                    if ((item.getProduct_id() != null) && "product".equals(item.getType())) {
                        try {
                            Map<String, Object> productData = restTemplate
                                    .exchange(productAPI + item.getProduct_id(), HttpMethod.GET, entity, Map.class)
                                    .getBody();
                            List<Map<String, Object>> items = (List<Map<String, Object>>) ((Map<String, Object>) productData
                                    .get("data")).get("items");

                            if (items != null && !items.isEmpty()) {
                                // Extract the first product's details
                                Map<String, Object> product = items.isEmpty() ? null : items.get(0);

                                // Add brand and category details if available
                                itemData.put("product_name", product.get("name"));
                                itemData.put("brand_id", product.get("brand_id"));
                                itemData.put("brand_name", product.get("brand_name"));
                                itemData.put("category_id", product.get("category_id"));
                                itemData.put("category_name", product.get("category_name"));
                                itemData.put("unit_price", product.get("selling_price"));
                                itemData.put("tax_id", product.get("tax_id"));
                                itemData.put("tax_rate", product.get("tax_rate"));
                                item_rate[0] = (double) product.get("selling_price");
                                item_tax_rate[0] = (double) product.get("tax_rate");
                                item_quantity[0] = ((Number) item.getQuantity()).doubleValue();
                                itemAmount[0] = item_rate[0] * item_quantity[0];
                                itemGstAmount[0] = itemAmount[0] * item_tax_rate[0] / 100;
                            }
                        } catch (HttpClientErrorException | HttpServerErrorException ex) {
                            throw ex;
                        }

                        try {
                            Map<String, Object> inventoryData = restTemplate
                                    .exchange(inventoryAPI + item.getProduct_id(), HttpMethod.GET, entity, Map.class)
                                    .getBody();
                            List<Map<String, Object>> inventoryItems = (List<Map<String, Object>>) ((Map<String, Object>) inventoryData
                                    .get("data")).get("items");
                            Map<String, Object> inventoryItem = inventoryItems.isEmpty() ? null : inventoryItems.get(0);
                            itemData.put("stock_quantity",
                                    inventoryItem != null ? inventoryItem.get("total_quantity") : 0);
                        } catch (HttpClientErrorException | HttpServerErrorException ex) {
                            throw ex;
                        }
                    } else if ("bed".equals(item.getType())) {

                        itemData.put("room_id", item.getRoom_id());
                        itemData.put("location_id", item.getLocation_id());

                        if (item.getProduct_id() == null || item.getProduct_id().trim().isEmpty()) {
                            itemData.put("product_id", null);
                            itemData.put("product_name", "Room Not Available");
                            itemData.put("unit_price", 0);
                            itemData.put("tax_id", null);
                            itemData.put("tax_rate", 0);
                            itemData.put("total_price", 0);
                            return itemData;
                        }

                        try {
                            // Correct URL pattern
                            String url = roomAPI + "?limit=1000&room_category=admission";

                            Map<String, Object> resp = restTemplate.exchange(url, HttpMethod.GET, entity, Map.class)
                                    .getBody();

                            if (resp == null || !(resp.get("data") instanceof Map)) {
                                itemData.put("product_name", "Room Not Found");
                                itemData.put("unit_price", 0);
                                itemData.put("total_price", 0);
                                return itemData;
                            }

Map<String, Object> dataObj2 = (Map<String, Object>) resp.get("data");
                            List<Map<String, Object>> roomsList = (List<Map<String, Object>>) dataObj2.get("items");

                            Map<String, Object> matchedRoom = roomsList.stream()
                                    .filter(r -> item.getProduct_id().equals(r.get("location_id")))
                                    .findFirst()
                                    .orElse(null);

                            if (matchedRoom == null) {
                                itemData.put("product_name", item.getProduct_name());
                                itemData.put("unit_price", item.getUnit_price());
                                itemData.put("tax_id", null);
                                itemData.put("tax_rate", 0.0);
                                itemData.put("total_price", item.getTotal_price());
                                return itemData;
                            }

                            String roomName = matchedRoom.get("room_name") != null
                                    ? matchedRoom.get("room_name").toString() : "Bed";

                            double finalRate = (item.getUnit_price() != null && item.getUnit_price() > 0)
                                    ? item.getUnit_price()
                                    : (matchedRoom.get("bed_rate") != null
                                            ? Double.parseDouble(matchedRoom.get("bed_rate").toString())
                                            : 0.0);

                            int qtyValue = item.getQuantity() != null ? item.getQuantity() : 1;
                            double totalPriceValue = item.getTotal_price() != null
                                    ? item.getTotal_price()
                                    : finalRate * qtyValue;

                            Object taxId = matchedRoom.get("tax_id");
                            Object taxRate = matchedRoom.get("tax_rate");

                            itemData.put("room_id", item.getRoom_id());
                            itemData.put("location_id", item.getLocation_id());
                            itemData.put("product_name", roomName);
                            itemData.put("unit_price", finalRate);
                            itemData.put("quantity", qtyValue);
                            itemData.put("total_price", totalPriceValue);
                            itemData.put("tax_id", taxId);
                            itemData.put("tax_rate", taxRate != null ? Double.parseDouble(taxRate.toString()) : 0.0);
                        } catch (Exception ex) {
                            itemData.put("product_name", "Room Not Found");
                            itemData.put("unit_price", 0);
                            itemData.put("total_price", 0);
                            itemData.put("tax_id", null);
                            itemData.put("tax_rate", 0);
                        }
                    } else if ((item.getProduct_id() != null) && "service".equals(item.getType())) {
                        try {
                            if (ObjectUtils.isEmpty(item.getProduct_id())) {
                                itemData.put("product_name", item.getProduct_name());
                                itemData.put("tax_id", null);
                                itemData.put("tax_rate", null);
                                itemData.put("unit_price", item.getUnit_price());
                            } else {
                                Map<String, Object> serviceData = restTemplate
                                        .exchange(serviceAPI + item.getProduct_id(), HttpMethod.GET, entity, Map.class)
                                        .getBody();
                                Map<String, Object> serviceItem = (Map<String, Object>) serviceData.get("data");

                                if (serviceItem != null) {
                                    itemData.put("product_name", serviceItem.get("service_name"));
                                    itemData.put("tax_id", serviceItem.get("tax_id"));
                                    itemData.put("tax_rate", serviceItem.get("tax_rate"));
                                    itemData.put("unit_price", serviceItem.get("rate"));
                                    item_rate[0] = (double) item.getUnit_price();
                                    item_tax_rate[0] = ((Number) serviceItem.get("tax_rate")).doubleValue();
                                    item_quantity[0] = ((Number) item.getQuantity()).doubleValue();
                                    itemAmount[0] = item_rate[0] * item_quantity[0];
                                    itemGstAmount[0] = itemAmount[0] * item_tax_rate[0] / 100;
                                }
                            }
                        } catch (HttpClientErrorException | HttpServerErrorException ex) {
                            throw ex;
                        }
                    } else if ((item.getProduct_id() != null) && "package".equals(item.getType())) {
                        try {
                            Map<String, Object> packageData = restTemplate
                                    .exchange(packageAPI + item.getProduct_id(), HttpMethod.GET, entity, Map.class)
                                    .getBody();
                            Map<String, Object> packageItem = packageData != null
                                    ? (Map<String, Object>) packageData.get("data")
                                    : null;

                            if (packageItem != null) {
                                itemData.put("product_name", packageItem.get("package_name"));
                                itemGstAmount[0] = 0;
                            }
                        } catch (HttpClientErrorException | HttpServerErrorException ex) {
                            throw ex;
                        }
                    }

                    itemData.put("quantity", item.getQuantity());
                    itemData.put("batch_code", item.getBatch_code());
                    itemData.put("total_price", MoneyUtils.truncateToTwoDecimals(item.getTotal_price()));
                    itemData.put("tax_amount", MoneyUtils.truncateToTwoDecimals(itemGstAmount[0]));
                    itemData.put("created_by", item.getCreated_by());
                    itemData.put("created_at", String.valueOf(item.getCreated_at()));
                    return itemData;
                }).collect(Collectors.toList());

        data.put("sales_items", salesItemsData);

        // Get the sales payment details
        Payments payment = salesData.getPayments();
        if (payment != null) {
            Map<String, Object> paymentData = new LinkedHashMap<>();
            paymentData.put("amount_paid", MoneyUtils.truncateToTwoDecimals(payment.getAmount_paid()));
            paymentData.put("payment_method", payment.getPayment_method());
            paymentData.put("date", String.valueOf(payment.getDate()));
            data.put("payments", paymentData);
        }
        return data;
    }

    private Map<String, Object> prepareExportResponse(Sales salesData) {
        Map<String, Object> data = new LinkedHashMap<>();
        data.put("id", salesData.getId());
        data.put("prescription_id", salesData.getPrescription_id());
        data.put("encounter_id", salesData.getEncounter_id());
        data.put("custom_encounter_id", salesData.getCustomEncounterId());
        data.put("patient_id", salesData.getPatient_id());
        data.put("patient_name", salesData.getPatient_name());
        data.put("customer_name", salesData.getCustomer_name());
        data.put("customer_mobile", salesData.getCustomer_mobile());
        data.put("customer_email", salesData.getCustomer_email());
        data.put("customer_dob", salesData.getCustomer_dob());
        data.put("customer_gender", salesData.getCustomer_gender());
        data.put("customer_address", salesData.getCustomer_address());
        data.put("total_amount", MoneyUtils.truncateToTwoDecimals(salesData.getTotal_amount()));
        data.put("discount", salesData.getDiscount());
        data.put("coupon_amount", salesData.getCoupon_amount());
        data.put("coupon_code", salesData.getCoupon_code());
        data.put("payment_status", salesData.getPayment_status());
        data.put("payment_method", salesData.getPayment_method());
        // Razorpay sub-method for accounting (UPI, CREDIT_CARD, DEBIT_CARD)
        data.put("payment_sub_method", salesData.getPayment_sub_method());
        data.put("created_by", salesData.getCreated_by());
        data.put("created_at", String.valueOf(salesData.getCreated_at()));
        Map<String, Object> ledger = calculateLedgerSnapshotForSale(salesData);
        data.put("total_advance_amount", ledger.get("total_advance_amount"));
        data.put("advance_used_for_this_sale", ledger.get("advance_used_for_this_sale"));
        data.put("advance_balance_after_sale", ledger.get("advance_balance_after_sale"));
        data.put("total_debit_amount", MoneyUtils.truncateToTwoDecimals(salesData.getTotal_amount()));
        data.put("previous_due_paid", MoneyUtils.truncateToTwoDecimals(
                salesData.getPrevious_due_paid() != null ? salesData.getPrevious_due_paid() : 0.0));

        double paid = salesData.getTotal_paid_for_sale() != null
                ? salesData.getTotal_paid_for_sale()
                : 0.0;

        double remaining = salesData.getRemaining_due_amount() != null
                ? salesData.getRemaining_due_amount()
                : salesData.getTotal_amount();

        // but Razorpay credit came later and snapshot wasn't refreshed yet
        boolean isRazorpay = "razorpay".equalsIgnoreCase(salesData.getPayment_method())
                || "RAZORPAY_WHATSAPP".equalsIgnoreCase(salesData.getPayment_method())
                || "RAZORPAY_SMS".equalsIgnoreCase(salesData.getPayment_method());

        if (isRazorpay && remaining > 0) {
            double ledgerCredit = paymentHistoryRepository.findTotalPaidForSale(salesData.getId());
            double ledgerAdvance = paymentHistoryRepository.findAdvanceUsedForSale(salesData.getId());
            double ledgerTotal = ledgerCredit + ledgerAdvance;
            double ledgerRemaining = Math.max(salesData.getTotal_amount() - ledgerTotal, 0.0);

            // Only override if ledger shows more paid than snapshot
            if (ledgerTotal > paid) {
                paid = ledgerTotal;
                remaining = ledgerRemaining;
            }
        }

        String paymentStatus;
        if (remaining <= 0.001) {
            paymentStatus = "PAID";
        } else if (paid > 0) {
            paymentStatus = "PARTIAL";
        } else {
            paymentStatus = "UNPAID";
        }

        data.put("total_paid_for_sale", MoneyUtils.truncateToTwoDecimals(paid));
        data.put("remaining_due_amount", MoneyUtils.truncateToTwoDecimals(remaining));
        data.put("payment_status", paymentStatus);

        // Extract Authorization headers from the request
        HttpHeaders headers = jwtRequestUtils.getAuthorizationHeaders();

        // Create an HttpEntity with the extracted headers
        HttpEntity<String> entity = new HttpEntity<>(headers);

        // Initialize total tax amount
        final double[] totalTaxAmount = { 0 };

        // Get patient information by patient ID
        try {
            Map<String, Object> patientData = restTemplate
                    .exchange(patientAPI + salesData.getPatient_id(), HttpMethod.GET, entity, Map.class).getBody();
            Map<String, Object> patientItem = (Map<String, Object>) patientData.get("data");

            if (patientItem != null) {
                data.put("mrn_no", patientItem.get("mrn_no"));
            } else {
                data.put("mrn_no", "-");
            }
        } catch (HttpClientErrorException | HttpServerErrorException ex) {
            if (ex.getStatusCode().value() == 500 || ex.getStatusCode().value() == 404) {
                data.put("mrn_no", "-");
            } else {
                throw ex;
            }
        }

        // Map sales items
        List<Map<String, Object>> salesItemsData = salesData.getSales_items().stream()
                .map(item -> {

                    final double[] item_rate = { 0.00 };
                    final double[] item_tax_rate = { 0.00 };
                    final double[] item_quantity = { 0.00 };
                    final double[] itemAmount = { 0.00 };
                    final double[] itemGstAmount = { 0.00 };

                    Map<String, Object> itemData = new LinkedHashMap<>();
                    itemData.put("id", item.getId());
                    itemData.put("type", item.getType());
                    itemData.put("product_id", item.getProduct_id());

                    if ((item.getProduct_id() != null) && "product".equals(item.getType())) {
                        try {
                            Map<String, Object> productData = restTemplate
                                    .exchange(productAPI + item.getProduct_id(), HttpMethod.GET, entity, Map.class)
                                    .getBody();
                            List<Map<String, Object>> items = (List<Map<String, Object>>) ((Map<String, Object>) productData
                                    .get("data")).get("items");

                            if (items != null && !items.isEmpty()) {
                                Map<String, Object> product = items.get(0);

                                itemData.put("product_name", product.get("name"));
                                itemData.put("brand_id", product.get("brand_id"));
                                itemData.put("brand_name", product.get("brand_name"));
                                itemData.put("category_id", product.get("category_id"));
                                itemData.put("category_name", product.get("category_name"));
                                itemData.put("unit_price", product.get("selling_price"));
                                itemData.put("tax_id", product.get("tax_id"));
                                itemData.put("tax_rate", product.get("tax_rate"));
                                item_rate[0] = (double) product.get("selling_price");
                                item_tax_rate[0] = (double) product.get("tax_rate");
                                item_quantity[0] = ((Number) item.getQuantity()).doubleValue();
                                itemAmount[0] = item_rate[0] * item_quantity[0];
                                itemGstAmount[0] = itemAmount[0] * item_tax_rate[0] / 100;
                            }
                        } catch (HttpClientErrorException | HttpServerErrorException ex) {
                            throw ex;
                        }

                        try {
                            Map<String, Object> inventoryData = restTemplate
                                    .exchange(inventoryAPI + item.getProduct_id(), HttpMethod.GET, entity, Map.class)
                                    .getBody();
                            List<Map<String, Object>> inventoryItems = (List<Map<String, Object>>) ((Map<String, Object>) inventoryData
                                    .get("data")).get("items");
                            Map<String, Object> inventoryItem = inventoryItems.isEmpty() ? null : inventoryItems.get(0);
                            itemData.put("stock_quantity",
                                    inventoryItem != null ? inventoryItem.get("total_quantity") : 0);
                        } catch (HttpClientErrorException | HttpServerErrorException ex) {
                            throw ex;
                        }
                    } else if ((item.getProduct_id() != null) && "service".equals(item.getType())) {
                        try {
                            if (ObjectUtils.isEmpty(item.getProduct_id())) {
                                itemData.put("product_name", item.getProduct_name());
                                itemData.put("tax_id", null);
                                itemData.put("tax_rate", null);
                                itemData.put("unit_price", item.getUnit_price());
                            } else {
                                Map serviceData = restTemplate
                                        .exchange(serviceAPI + item.getProduct_id(), HttpMethod.GET, entity, Map.class)
                                        .getBody();
                                Map<String, Object> serviceItem = (Map<String, Object>) serviceData.get("data");

                                if (serviceItem != null) {
                                    itemData.put("product_name", serviceItem.get("service_name"));
                                    itemData.put("tax_id", serviceItem.get("tax_id"));
                                    itemData.put("tax_rate", serviceItem.get("tax_rate"));
                                    itemData.put("unit_price", serviceItem.get("rate"));
                                    item_rate[0] = (double) item.getUnit_price();
                                    item_tax_rate[0] = ((Number) serviceItem.get("tax_rate")).doubleValue();
                                    item_quantity[0] = ((Number) item.getQuantity()).doubleValue();
                                    itemAmount[0] = item_rate[0] * item_quantity[0];
                                    itemGstAmount[0] = itemAmount[0] * item_tax_rate[0] / 100;
                                }
                            }
                        } catch (HttpClientErrorException | HttpServerErrorException ex) {
                            throw ex;
                        }
                    } else if ("bed".equals(item.getType())) {

                        // Preserve stored room and location ids (if any)
                        itemData.put("room_id", item.getRoom_id());
                        itemData.put("location_id", item.getLocation_id());

                        if (item.getProduct_id() == null || item.getProduct_id().trim().isEmpty()) {
                            itemData.put("product_id", null);
                            itemData.put("product_name", "Room Not Available");
                            itemData.put("unit_price", 0.0);
                            itemData.put("tax_id", null);
                            itemData.put("tax_rate", 0);
                            itemData.put("total_price", MoneyUtils.truncateToTwoDecimals(item.getTotal_price()));
                            return itemData;
                        }

                        try {
                            String url = roomAPI + "?limit=1000&room_category=admission";

                            Map<String, Object> roomResp = restTemplate.exchange(url, HttpMethod.GET, entity, Map.class)
                                    .getBody();

                            if (roomResp == null || !(roomResp.get("data") instanceof Map)) {
                                itemData.put("product_name", "Room Not Found");
                                itemData.put("unit_price", 0.0);
                                itemData.put("total_price", MoneyUtils.truncateToTwoDecimals(item.getTotal_price()));
                                return itemData;
                            }

                            Map<String, Object> dataObj = (Map<String, Object>) roomResp.get("data");
                            List<Map<String, Object>> itemsList = (List<Map<String, Object>>) dataObj.get("items");

                            if (itemsList == null || itemsList.isEmpty()) {
                                itemData.put("product_name", "Room Not Found");
                                itemData.put("unit_price", 0.0);
                                itemData.put("total_price", MoneyUtils.truncateToTwoDecimals(item.getTotal_price()));
                                return itemData;
                            }

Map<String, Object> matchedRoom = itemsList.stream()
                                    .filter(r -> item.getProduct_id().equals(r.get("location_id")))
                                    .findFirst()
                                    .orElse(null);

                            if (matchedRoom == null) {
                                itemData.put("product_name", item.getProduct_name());
                                itemData.put("unit_price", item.getUnit_price());
                                itemData.put("tax_id", null);
                                itemData.put("tax_rate", 0.0);
                                itemData.put("total_price", MoneyUtils.truncateToTwoDecimals(item.getTotal_price()));
                                return itemData;
                            }

                            String roomName = matchedRoom.get("room_name") != null
                                    ? matchedRoom.get("room_name").toString() : "Bed";

                            double bedRate = (item.getUnit_price() != null && item.getUnit_price() > 0)
                                    ? item.getUnit_price()
                                    : (matchedRoom.get("bed_rate") != null
                                            ? Double.parseDouble(matchedRoom.get("bed_rate").toString())
                                            : 0.0);

                            int qty = item.getQuantity() != null ? item.getQuantity() : 1;
                            double totalPrice = item.getTotal_price() != null
                                    ? item.getTotal_price()
                                    : bedRate * qty;

                            Object taxId = matchedRoom.get("tax_id");
                            Object taxRate = matchedRoom.get("tax_rate");
                            double taxRateVal = taxRate != null ? Double.parseDouble(taxRate.toString()) : 0.0;

                            itemData.put("room_id", item.getRoom_id());
                            itemData.put("location_id", item.getLocation_id());
                            itemData.put("product_name", roomName);
                            itemData.put("unit_price", bedRate);
                            itemData.put("tax_id", taxId);
                            itemData.put("tax_rate", taxRateVal);
                            itemData.put("total_price", MoneyUtils.truncateToTwoDecimals(totalPrice));

                            item_rate[0] = bedRate;
                            item_tax_rate[0] = taxRateVal;
                            item_quantity[0] = ((Number) item.getQuantity()).doubleValue();
                            itemAmount[0] = item_rate[0] * item_quantity[0];
                            itemGstAmount[0] = itemAmount[0] * item_tax_rate[0] / 100;
                        } catch (Exception ex) {
                            itemData.put("product_name", "Room Not Found");
                            itemData.put("unit_price", 0.0);
                            itemData.put("tax_id", null);
                            itemData.put("tax_rate", 0);
                            itemData.put("total_price", MoneyUtils.truncateToTwoDecimals(item.getTotal_price()));
                        }
                    } else if ((item.getProduct_id() != null) && "package".equals(item.getType())) {
                        try {
                            Map<String, Object> packageData = restTemplate
                                    .exchange(packageAPI + item.getProduct_id(), HttpMethod.GET, entity, Map.class)
                                    .getBody();
                            Map<String, Object> packageItem = packageData != null
                                    ? (Map<String, Object>) packageData.get("data")
                                    : null;

                            if (packageItem != null) {
                                itemData.put("product_name", packageItem.get("package_name"));
                                itemData.put("unit_price", packageItem.get("final_amount"));
                                item_rate[0] = (double) item.getUnit_price();
                                item_quantity[0] = ((Number) item.getQuantity()).doubleValue();
                                itemAmount[0] = item_rate[0] * item_quantity[0];
                                itemGstAmount[0] = 0;
                            }
                        } catch (HttpClientErrorException | HttpServerErrorException ex) {
                            throw ex;
                        }
                    }

                    itemData.put("quantity", item.getQuantity());
                    itemData.put("batch_code", item.getBatch_code());
                    itemData.put("total_price", MoneyUtils.truncateToTwoDecimals(item.getTotal_price()));
                    itemData.put("tax_amount", MoneyUtils.truncateToTwoDecimals(itemGstAmount[0]));
                    itemData.put("created_by", item.getCreated_by());
                    itemData.put("created_at", String.valueOf(item.getCreated_at()));

                    // Add to the total tax amount
                    totalTaxAmount[0] = totalTaxAmount[0] + Double.valueOf(String.valueOf(itemGstAmount[0]));
                    return itemData;
                }).collect(Collectors.toList());
        // No grouping — keep original batch-wise data
        data.put("sales_items", salesItemsData);

        // Calculate total tax amount properly
        double totalTax = salesItemsData.stream()
                .mapToDouble(i -> Double.parseDouble(i.get("tax_amount").toString()))
                .sum();

        data.put("total_tax_amount", MoneyUtils.truncateToTwoDecimals(totalTax));

        // Get the sales payment details
        Payments payment = salesData.getPayments();
        if (payment != null) {
            Map<String, Object> paymentData = new LinkedHashMap<>();
            paymentData.put("amount_paid", MoneyUtils.truncateToTwoDecimals(payment.getAmount_paid()));
            paymentData.put("payment_method", payment.getPayment_method());
            paymentData.put("date", String.valueOf(payment.getDate()));
            data.put("payments", paymentData);
        }

        try {
            Map<String, Object> organizationData = restTemplate
                    .exchange(organizationAPI, HttpMethod.GET, entity, Map.class).getBody();
            Map<String, Object> organizationItem = (Map<String, Object>) organizationData.get("data");

            if (organizationItem != null) {
                data.put("organization", organizationItem);
            }
        } catch (HttpClientErrorException | HttpServerErrorException ex) {
            throw ex;
        }

        try {
            Map<String, Object> organizationData = restTemplate
                    .exchange(organizationAPI, HttpMethod.GET, entity, Map.class).getBody();
            Map<String, Object> organizationItem = (Map<String, Object>) organizationData.get("data");

            if (organizationItem != null) {
                data.put("organization", organizationItem);
            }
        } catch (HttpClientErrorException | HttpServerErrorException ex) {
            throw ex;
        }

        return data;
    }

}