/*
 * File: src/main/java/reports/service/FinancialService.java
 * Description: This service class provides functionality for handling financial-related operations in the ERP reporting system.
 * It includes methods for retrieving KPI data and generating financial reports.
 */

package com.nebula.erp.reports.service;

import com.nebula.erp.reports.model.product.Tax;
import com.nebula.erp.reports.model.sales.Payments;
import com.nebula.erp.reports.model.sales.SalesItem;
import com.nebula.erp.reports.model.sales.SalesReturnItem;
import com.nebula.erp.reports.repository.product.BrandRepository;
import com.nebula.erp.reports.repository.product.CategoryRepository;
import com.nebula.erp.reports.repository.product.TaxRepository;
import com.nebula.erp.reports.repository.purchase.*;
import com.nebula.erp.reports.repository.sales.PaymentRepository;
import com.nebula.erp.reports.repository.sales.SalesRepository;
import com.nebula.erp.reports.repository.sales.SalesReturnItemRepository;
import com.nebula.erp.reports.repository.sales.SalesItemRepository;
import com.nebula.erp.reports.repository.product.ProductRepository;
import com.nebula.erp.reports.requestmodel.PurchaseRequest;
import com.nebula.erp.reports.requestmodel.SalesRequest;
import com.nebula.erp.reports.utility.CreateLogger;
import com.nebula.erp.reports.utility.JwtRequestUtils;
import com.nebula.erp.reports.utility.MoneyUtils;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import com.nebula.erp.reports.utility.ApiResponseStructure;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;

@Service
public class FinancialService {

    @Autowired
    private PurchaseRepository purchaseRepository;

    @Autowired
    private SalesRepository salesRepository;

    @Autowired
    private SalesItemRepository salesItemRepository;

    @Autowired
    private SalesReturnItemRepository salesReturnItemRepository;

    @Autowired
    private SupplierRepository supplierRepository;

    @Autowired
    private GRNRepository grnRepository;

    @Autowired
    private PurchaseReturnRepository purchaseReturnRepository;

    @Autowired
    private PurchaseReturnItemRepository purchaseReturnItemRepository;

    @Autowired
    private ProductRepository productRepository;

    @Autowired
    private GRNItemRepository grnItemRepository;

    @Autowired
    private PurchaseItemRepository purchaseItemRepository;

    @Autowired
    private CategoryRepository categoryRepository;

    @Autowired
    private TaxRepository taxRepository;

    @Autowired
    private BrandRepository brandRepository;

    @Autowired
    private PaymentRepository paymentRepository;

    @Autowired
    private JwtRequestUtils jwtRequestUtils;

    @Autowired
    private HttpServletRequest httpServletRequest;

    @Autowired
    private CreateLogger createLogger;

    private static final String path = "/reports/financial";

    public ApiResponseStructure<Map<String, Object>> getKPIData(LocalDate fromDate, LocalDate toDate) {
        // Extract Authorization headers from the request
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.AUTHORIZATION, httpServletRequest.getHeader(HttpHeaders.AUTHORIZATION));

        String tenantName = jwtRequestUtils.getTenantNameFromHeaders(headers);

        if (fromDate != null && toDate != null) {
            // Convert LocalDate to LocalDateTime for comparison with created_at
            LocalDateTime fromDateTime = fromDate.atStartOfDay();
            LocalDateTime toDateTime = toDate.atTime(23, 59, 59);

            // Initialize totals
            BigDecimal totalRevenue = BigDecimal.ZERO;
            BigDecimal totalTaxesCollected = BigDecimal.ZERO;
            BigDecimal totalPaymentsReceived = BigDecimal.ZERO;
            BigDecimal totalCost = BigDecimal.ZERO;
            int totalOrders = 0;

            // Fetch all sales items based on date range and tenant name
            List<SalesItem> salesItems = salesItemRepository.findAllSalesItemsByDateRange(fromDateTime, toDateTime,
                    tenantName);

            for (SalesItem item : salesItems) {
                // Total Revenue Calculation
                totalRevenue = totalRevenue.add(BigDecimal.valueOf(item.getTotal_price()));

                if (item.getTax_id() != null) {
                    // Fetch tax rate using tax ID and tenant name
                    BigDecimal taxRate = taxRepository.findTaxRateById(item.getTax_id(), tenantName);
                    if (taxRate != null) {
                        // Calculate tax amount
                        BigDecimal taxAmount = BigDecimal.valueOf(item.getTotal_price()).multiply(taxRate)
                                .divide(BigDecimal.valueOf(100));
                        totalTaxesCollected = totalTaxesCollected.add(taxAmount);
                    }
                }

                totalOrders++; // Increment total orders
            }

            // Fetch total payments received (assuming there's a method in the payment
            // repository)
            totalPaymentsReceived = paymentRepository.sumPaymentsReceivedByTenantAndDateRange(tenantName, fromDateTime,
                    toDateTime);

            // Calculate profit margin
            // This assumes you have a method to get total costs for the items; adapt
            // accordingly
            totalCost = salesItems.stream()
                    .map(item -> BigDecimal.valueOf(item.getUnit_price())
                            .multiply(BigDecimal.valueOf(item.getQuantity()))) // Replace with actual cost calculation
                                                                               // if different
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            BigDecimal profitMargin = BigDecimal.ZERO; // Default value for profit margin
            if (totalRevenue.compareTo(BigDecimal.ZERO) > 0) { // Check if totalRevenue is greater than zero
                profitMargin = totalRevenue.subtract(totalCost).divide(totalRevenue, RoundingMode.HALF_UP)
                        .multiply(BigDecimal.valueOf(100)); // Profit margin in percentage
            }

            // Prepare response data
            Map<String, Object> kpiData = new LinkedHashMap<>();
            kpiData.put("total_revenue", MoneyUtils.truncateToTwoDecimals(totalRevenue.doubleValue()));
            kpiData.put("total_tax_collected", MoneyUtils.truncateToTwoDecimals(totalTaxesCollected.doubleValue()));
            kpiData.put("total_payments_received",
                    MoneyUtils.truncateToTwoDecimals(totalPaymentsReceived.doubleValue()));
            kpiData.put("profit_margin", MoneyUtils.truncateToTwoDecimals(profitMargin.doubleValue()));
            kpiData.put("total_orders", totalOrders);

            // Create and return the structured ApiResponseStructure
            return new ApiResponseStructure<>("Success", 200, "Data retrieved.", kpiData);
        }
        createLogger.createLogger("error", path, "GET", "Please provide start-date and end-date with valid dates.",
                "runtime");
        return new ApiResponseStructure<>("Failure", 500, "Please provide start-date and end-date with valid dates.",
                Collections.emptyMap());
    }

    public ApiResponseStructure<Map<String, Object>> getFinancialReport(SalesRequest salesRequest, String reportType) {
        // Extract Authorization headers from the request
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.AUTHORIZATION, httpServletRequest.getHeader(HttpHeaders.AUTHORIZATION));

        String tenantName = jwtRequestUtils.getTenantNameFromHeaders(headers);

        // Validate presence of fromDate and toDate
        if (salesRequest.getStartDate() == null || salesRequest.getEndDate() == null) {
            createLogger.createLogger("error", path, "POST", "Both 'startDate' and 'endDate' must be provided.",
                    "bad request");
            return new ApiResponseStructure<>("Error", HttpStatus.BAD_REQUEST.value(),
                    "Both 'startDate' and 'endDate' must be provided.", new HashMap<>());
        }

        // Convert LocalDate to LocalDateTime
        LocalDateTime fromDateTime = salesRequest.getStartDate().atStartOfDay();
        LocalDateTime toDateTime = salesRequest.getEndDate().atTime(23, 59, 59);

        // Create a specification for the query
        Specification<SalesItem> spec = Specification.where(null);

        // Add tenant name filtering
        spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("tenant"), tenantName));

        // Add date range filtering
        spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.between(root.get("created_at"), fromDateTime,
                toDateTime));
        BigDecimal totalPaid = BigDecimal.ZERO;
        // Switch cases to handle various report type
        switch (reportType) {
            case "revenueReport":
                // Add dynamic conditions
                if (salesRequest.getConditions() != null) {
                    for (SalesRequest.Condition condition : salesRequest.getConditions()) {
                        switch (condition.getOperator()) {
                            case "equals":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder
                                        .equal(root.get(condition.getField()), condition.getValue()));
                                break;
                            case "greaterThan":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.greaterThan(
                                        root.get(condition.getField()), (Comparable) condition.getValue()));
                                break;
                            case "lessThan":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder
                                        .lessThan(root.get(condition.getField()), (Comparable) condition.getValue()));
                                break;
                            case "greaterThanOrEquals":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.greaterThanOrEqualTo(
                                        root.get(condition.getField()), (Comparable) condition.getValue()));
                                break;
                            case "lessThanOrEquals":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.lessThanOrEqualTo(
                                        root.get(condition.getField()), (Comparable) condition.getValue()));
                                break;
                            default:
                                throw new IllegalArgumentException(
                                        "Operator " + condition.getOperator() + " is not supported.");
                        }
                    }
                }

                // Fetch the data with pagination for sales items
                Pageable pageable = PageRequest.of(salesRequest.getPage() != null ? salesRequest.getPage() : 0, // Page
                        salesRequest.getSize() != null ? salesRequest.getSize() : 20);

                // Prepare response data
                List<Map<String, Object>> reportData = new ArrayList<>();
                BigDecimal totalRevenue = BigDecimal.ZERO;
                BigDecimal totalTaxCollected = BigDecimal.ZERO;

                // Fetch all sales items based on tenant name and date range
                Page<SalesItem> salesItems = salesItemRepository.findAll(spec, pageable);

                for (SalesItem item : salesItems) {
                    if (item.getTax_id() != null) {
                        // Fetch the Tax entity using the tax ID
                        Tax tax = taxRepository.findById(item.getTax_id()).orElse(null);
                        if (tax != null) {
                            BigDecimal gstAmount = BigDecimal.valueOf(item.getTotal_price())
                                    .multiply(tax.getRate())
                                    .divide(BigDecimal.valueOf(100));

                            totalRevenue = totalRevenue.add(BigDecimal.valueOf(item.getTotal_price()));
                            totalTaxCollected = totalTaxCollected.add(gstAmount);
                            Double paid = item.getSales().getTotal_paid_for_sale();
                            if (paid != null) {
                                totalPaid = totalPaid.add(BigDecimal.valueOf(paid));
                            }

                            // Create report entry
                            Map<String, Object> reportEntry = new LinkedHashMap<>();
                            reportEntry.put("sale_id", item.getSales().getId());
                            reportEntry.put("sale_item_id", item.getId());
                            reportEntry.put("total_revenue", MoneyUtils.truncateToTwoDecimals(item.getTotal_price()));
                            reportEntry.put("tax_collected", MoneyUtils.truncateToTwoDecimals(gstAmount.doubleValue()));

                            // Fetch payment method from Payments model
                            String paymentMethod = item.getSales().getPayment_method();
                            String paymentSubMethod = item.getSales().getPayment_sub_method();

                            reportEntry.put("payment_method",
                                    (paymentMethod != null && !paymentMethod.isEmpty())
                                            ? paymentMethod
                                            : "N/A");

                            reportEntry.put("payment_sub_method",
                                    (paymentSubMethod != null && !paymentSubMethod.isEmpty())
                                            ? paymentSubMethod
                                            : "N/A");

                            reportEntry.put("sale_date", String.valueOf(item.getCreated_at())); // Adjust based on your
                                                                                                // actual date field

                            if ((item.getProduct_id() != null) && "product".equals(item.getType())) {
                                reportEntry.put("product_id", item.getProduct_id());
                                reportEntry.put("product_name", productRepository
                                        .findProductNameById(Long.valueOf(item.getProduct_id()), tenantName));
                                Long categoryId = productRepository
                                        .findCategoryIdByProductId(Long.valueOf(item.getProduct_id()), tenantName);
                                reportEntry.put("category_id", categoryId);
                                reportEntry.put("category_name",
                                        categoryRepository.findCategoryNameById(categoryId, tenantName));
                                Long brandId = productRepository
                                        .findBrandIdByProductId(Long.valueOf(item.getProduct_id()), tenantName);
                                reportEntry.put("brand_id", brandId);
                                reportEntry.put("brand_name", brandRepository.findBrandNameById(brandId, tenantName));
                            }

                            reportData.add(reportEntry);
                        }
                    }
                }

                // Prepare column details
                Map<String, Object> column = new LinkedHashMap<>();
                column.put("sale_id", "SALE ID");
                column.put("sale_item_id", "SALE ITEM ID");
                column.put("total_revenue", "TOTAL REVENUE");
                column.put("tax_collected", "TAX COLLECTED");
                column.put("payment_method", "PAYMENT METHOD");
                column.put("payment_sub_method", "PAYMENT SUB METHOD");
                column.put("sale_date", "SALE DATE");
                column.put("product_id", "PRODUCT ID");
                column.put("product_name", "PRODUCT NAME");
                column.put("category_id", "CATEGORY ID");
                column.put("category_name", "CATEGORY NAME");
                column.put("brand_id", "BRAND ID");
                column.put("brand_name", "BRAND NAME");

                // Prepare pagination details
                Map<String, Object> pagination = new LinkedHashMap<>();
                pagination.put("currentPage", salesItems.getNumber() + 1); // Page number is 0-based
                pagination.put("totalPages", salesItems.getTotalPages());
                pagination.put("pageSize", pageable.getPageSize());
                pagination.put("totalRecords", salesItems.getTotalElements()); // Total records from the Page object

                // Prepare summary details
                Map<String, Object> summary = new LinkedHashMap<>();
                summary.put("totalRevenue", MoneyUtils.truncateToTwoDecimals(totalRevenue.doubleValue()));
                summary.put("totalTaxCollected", MoneyUtils.truncateToTwoDecimals(totalTaxCollected.doubleValue()));
                summary.put("totalPaymentsReceived",
                        MoneyUtils.truncateToTwoDecimals(totalPaid.doubleValue()));

                // Prepare revenue response data
                Map<String, Object> revenueResponseData = new LinkedHashMap<>();
                revenueResponseData.put("module", "financial");
                revenueResponseData.put("reportType", reportType);
                revenueResponseData.put("startDate", String.valueOf(salesRequest.getStartDate()));
                revenueResponseData.put("endDate", String.valueOf(salesRequest.getEndDate()));
                revenueResponseData.put("reportData", reportData);
                revenueResponseData.put("column", column);
                revenueResponseData.put("pagination", pagination);
                revenueResponseData.put("summary", summary);

                // Create and return the structured ApiResponseStructure
                return new ApiResponseStructure<>("Success", 200, "Data retrieved.", revenueResponseData);

            case "taxCollectionReport":
                // Add dynamic conditions
                if (salesRequest.getConditions() != null) {
                    for (SalesRequest.Condition condition : salesRequest.getConditions()) {
                        switch (condition.getOperator()) {
                            case "equals":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder
                                        .equal(root.get(condition.getField()), condition.getValue()));
                                break;
                            case "greaterThan":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.greaterThan(
                                        root.get(condition.getField()), (Comparable) condition.getValue()));
                                break;
                            case "lessThan":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder
                                        .lessThan(root.get(condition.getField()), (Comparable) condition.getValue()));
                                break;
                            case "greaterThanOrEquals":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.greaterThanOrEqualTo(
                                        root.get(condition.getField()), (Comparable) condition.getValue()));
                                break;
                            case "lessThanOrEquals":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.lessThanOrEqualTo(
                                        root.get(condition.getField()), (Comparable) condition.getValue()));
                                break;
                            default:
                                createLogger.createLogger("error", path, "POST", "", "");
                                throw new IllegalArgumentException(
                                        "Operator " + condition.getOperator() + " is not supported.");
                        }
                    }
                }

                // Fetch the data with pagination for sales items
                pageable = PageRequest.of(salesRequest.getPage() != null ? salesRequest.getPage() : 0, // Page number is

                        salesRequest.getSize() != null ? salesRequest.getSize() : 20);
                // Initialize summary variables
                BigDecimal totalTaxesCollected = BigDecimal.ZERO;
                BigDecimal gstCollected = BigDecimal.ZERO;
                BigDecimal vatCollected = BigDecimal.ZERO;

                // Fetch all sales items based on tenant name and date range
                salesItems = salesItemRepository.findAll(spec, pageable);

                List<Map<String, Object>> taxDataList = new ArrayList<>();

                for (SalesItem item : salesItems) {
                    if (item.getTax_id() != null) {
                        // Fetch tax rate using tax ID
                        Tax tax = taxRepository.findById(item.getTax_id()).orElse(null);
                        if (tax != null) {
                            BigDecimal taxAmount = BigDecimal.valueOf(item.getTotal_price())
                                    .multiply(tax.getRate())
                                    .divide(BigDecimal.valueOf(100));

                            totalTaxesCollected = totalTaxesCollected.add(taxAmount);

                            // Prepare tax data for report
                            Map<String, Object> taxData = new LinkedHashMap<>();
                            taxData.put("sale_id", item.getSales().getId());
                            taxData.put("sale_item_id", item.getId());
                            taxData.put("tax_type", tax.getType());
                            taxData.put("tax_collected", MoneyUtils.truncateToTwoDecimals(taxAmount.doubleValue()));
                            taxData.put("sale_date", String.valueOf(item.getCreated_at()));

                            if ((item.getProduct_id() != null) && "product".equals(item.getType())) {
                                taxData.put("product_id", item.getProduct_id());
                                taxData.put("product_name", productRepository
                                        .findProductNameById(Long.valueOf(item.getProduct_id()), tenantName));
                                Long categoryId = productRepository
                                        .findCategoryIdByProductId(Long.valueOf(item.getProduct_id()), tenantName);
                                taxData.put("category_id", categoryId);
                                taxData.put("category_name",
                                        categoryRepository.findCategoryNameById(categoryId, tenantName));
                                Long brandId = productRepository
                                        .findBrandIdByProductId(Long.valueOf(item.getProduct_id()), tenantName);
                                taxData.put("brand_id", brandId);
                                taxData.put("brand_name", brandRepository.findBrandNameById(brandId, tenantName));
                            }

                            taxDataList.add(taxData);

                            // Check if the tax is GST or VAT
                            if ("GST".equalsIgnoreCase(tax.getType())) {
                                gstCollected = gstCollected.add(taxAmount);
                            } else if ("VAT".equalsIgnoreCase(tax.getType())) {
                                vatCollected = vatCollected.add(taxAmount);
                            }
                        }
                    }
                }

                // Prepare column details
                column = new LinkedHashMap<>();
                column.put("sale_id", "SALE ID");
                column.put("sale_item_id", "SALE ITEM ID");
                column.put("tax_type", "TAX TYPE");
                column.put("tax_collected", "TAX COLLECTED");
                column.put("sale_date", "SALE DATE");
                column.put("product_id", "PRODUCT ID");
                column.put("product_name", "PRODUCT NAME");
                column.put("category_id", "CATEGORY ID");
                column.put("category_name", "CATEGORY NAME");
                column.put("brand_id", "BRAND ID");
                column.put("brand_name", "BRAND NAME");

                // Prepare pagination details
                pagination = new LinkedHashMap<>();
                pagination.put("currentPage", salesItems.getNumber() + 1);
                pagination.put("totalPages", salesItems.getTotalPages());
                pagination.put("pageSize", salesRequest.getSize());
                pagination.put("totalRecords", salesItems.getTotalElements());

                // Prepare summary details
                summary = new LinkedHashMap<>();
                summary.put("totalTaxesCollected", MoneyUtils.truncateToTwoDecimals(totalTaxesCollected.doubleValue()));
                summary.put("gstCollected", MoneyUtils.truncateToTwoDecimals(gstCollected.doubleValue()));
                summary.put("vatCollected", MoneyUtils.truncateToTwoDecimals(vatCollected.doubleValue()));

                // Prepare response data
                Map<String, Object> taxResponseData = new LinkedHashMap<>();
                taxResponseData.put("module", "financial");
                taxResponseData.put("reportType", reportType);
                taxResponseData.put("startDate", String.valueOf(salesRequest.getStartDate()));
                taxResponseData.put("endDate", String.valueOf(salesRequest.getEndDate()));
                taxResponseData.put("reportData", taxDataList);
                taxResponseData.put("column", column);
                taxResponseData.put("pagination", pagination);
                taxResponseData.put("summary", summary);

                // Create and return the structured ApiResponseStructure
                return new ApiResponseStructure<>("Success", 200, "Data retrieved.", taxResponseData);

            case "paymentsReceivedReport":
                // Add dynamic conditions
                if (salesRequest.getConditions() != null) {
                    for (SalesRequest.Condition condition : salesRequest.getConditions()) {
                        switch (condition.getOperator()) {
                            case "equals":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder
                                        .equal(root.get(condition.getField()), condition.getValue()));
                                break;
                            case "greaterThan":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.greaterThan(
                                        root.get(condition.getField()), (Comparable) condition.getValue()));
                                break;
                            case "lessThan":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder
                                        .lessThan(root.get(condition.getField()), (Comparable) condition.getValue()));
                                break;
                            case "greaterThanOrEquals":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.greaterThanOrEqualTo(
                                        root.get(condition.getField()), (Comparable) condition.getValue()));
                                break;
                            case "lessThanOrEquals":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.lessThanOrEqualTo(
                                        root.get(condition.getField()), (Comparable) condition.getValue()));
                                break;
                            default:
                                createLogger.createLogger("error", path, "POST", "", "IllegalArgumentException");
                                throw new IllegalArgumentException(
                                        "Operator " + condition.getOperator() + " is not supported.");
                        }
                    }
                }

                // Fetch the data with pagination for sales items
                pageable = PageRequest.of(salesRequest.getPage() != null ? salesRequest.getPage() : 0, // Page number is
                                                                                                       // zero-based
                        salesRequest.getSize() != null ? salesRequest.getSize() : 20);

                // Prepare response data
                reportData = new ArrayList<>();
                totalRevenue = BigDecimal.ZERO;
                totalTaxCollected = BigDecimal.ZERO;

                // Fetch all sales items based on tenant name and date range
                salesItems = salesItemRepository.findAll(spec, pageable);

                for (SalesItem item : salesItems) {
                    if (item.getTax_id() != null) {
                        // Fetch the Tax entity using the tax ID
                        Tax tax = taxRepository.findById(item.getTax_id()).orElse(null);
                        if (tax != null) {
                            BigDecimal gstAmount = BigDecimal.valueOf(item.getTotal_price())
                                    .multiply(tax.getRate())
                                    .divide(BigDecimal.valueOf(100));

                            totalRevenue = totalRevenue.add(BigDecimal.valueOf(item.getTotal_price()));
                            totalTaxCollected = totalTaxCollected.add(gstAmount);
                            Double paid = item.getSales().getTotal_paid_for_sale();
                            if (paid != null) {
                                totalPaid = totalPaid.add(BigDecimal.valueOf(paid));
                            }

                            // Create report entry
                            Map<String, Object> reportEntry = new LinkedHashMap<>();
                            reportEntry.put("sale_id", item.getSales().getId());
                            reportEntry.put("sale_item_id", item.getId());
                            reportEntry.put("total_price", MoneyUtils.truncateToTwoDecimals(item.getTotal_price()));
                            reportEntry.put("payment_received",
                                    paid != null
                                            ? MoneyUtils.truncateToTwoDecimals(paid)
                                            : 0.00);

                            // Fetch payment method from Payments model
                            String paymentMethod = item.getSales().getPayment_method();
                            String paymentSubMethod = item.getSales().getPayment_sub_method();

                            reportEntry.put("payment_method",
                                    (paymentMethod != null && !paymentMethod.isEmpty())
                                            ? paymentMethod
                                            : "N/A");

                            reportEntry.put("payment_sub_method",
                                    (paymentSubMethod != null && !paymentSubMethod.isEmpty())
                                            ? paymentSubMethod
                                            : "N/A");

                            reportEntry.put("sale_date", String.valueOf(item.getCreated_at())); // Adjust based on your
                                                                                                // actual date field

                            if ((item.getProduct_id() != null) && "product".equals(item.getType())) {
                                reportEntry.put("product_id", item.getProduct_id());
                                reportEntry.put("product_name", productRepository
                                        .findProductNameById(Long.valueOf(item.getProduct_id()), tenantName));
                                Long categoryId = productRepository
                                        .findCategoryIdByProductId(Long.valueOf(item.getProduct_id()), tenantName);
                                reportEntry.put("category_id", categoryId);
                                reportEntry.put("category_name",
                                        categoryRepository.findCategoryNameById(categoryId, tenantName));
                                Long brandId = productRepository
                                        .findBrandIdByProductId(Long.valueOf(item.getProduct_id()), tenantName);
                                reportEntry.put("brand_id", brandId);
                                reportEntry.put("brand_name", brandRepository.findBrandNameById(brandId, tenantName));
                            }

                            reportData.add(reportEntry);
                        }
                    }
                }

                // Prepare column details
                column = new LinkedHashMap<>();
                column.put("sale_id", "SALE ID");
                column.put("sale_item_id", "SALE ITEM ID");
                column.put("total_price", "TOTAL PRICE");
                column.put("payment_received", "PAYMENT RECEIVED");
                column.put("payment_method", "PAYMENT METHOD");
                column.put("payment_sub_method", "PAYMENT SUB METHOD");
                column.put("sale_date", "SALE DATE");
                column.put("product_id", "PRODUCT ID");
                column.put("product_name", "PRODUCT NAME");
                column.put("category_id", "CATEGORY ID");
                column.put("category_name", "CATEGORY NAME");
                column.put("brand_id", "BRAND ID");
                column.put("brand_name", "BRAND NAME");

                // Prepare pagination details
                pagination = new LinkedHashMap<>();
                pagination.put("currentPage", salesItems.getNumber() + 1); // Page number is 0-based
                pagination.put("totalPages", salesItems.getTotalPages());
                pagination.put("pageSize", pageable.getPageSize());
                pagination.put("totalRecords", salesItems.getTotalElements()); // Total records from the Page object

                // Prepare summary details
                summary = new LinkedHashMap<>();
                summary.put("totalRevenue", MoneyUtils.truncateToTwoDecimals(totalRevenue.doubleValue()));
                summary.put("totalTaxCollected", MoneyUtils.truncateToTwoDecimals(totalTaxCollected.doubleValue()));
                summary.put("totalPaymentsReceived",
                        MoneyUtils.truncateToTwoDecimals(totalPaid.doubleValue()));

                // Prepare response data
                Map<String, Object> paymentsResponseData = new LinkedHashMap<>();
                paymentsResponseData.put("module", "financial");
                paymentsResponseData.put("reportType", reportType);
                paymentsResponseData.put("startDate", String.valueOf(salesRequest.getStartDate()));
                paymentsResponseData.put("endDate", String.valueOf(salesRequest.getEndDate()));
                paymentsResponseData.put("reportData", reportData);
                paymentsResponseData.put("column", column);
                paymentsResponseData.put("pagination", pagination);
                paymentsResponseData.put("summary", summary);

                // Create and return the structured ApiResponseStructure
                return new ApiResponseStructure<>("Success", 200, "Data retrieved.", paymentsResponseData);

            case "profitabilityReport":
                // Add dynamic conditions
                if (salesRequest.getConditions() != null) {
                    for (SalesRequest.Condition condition : salesRequest.getConditions()) {
                        switch (condition.getOperator()) {
                            case "equals":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder
                                        .equal(root.get(condition.getField()), condition.getValue()));
                                break;
                            case "greaterThan":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.greaterThan(
                                        root.get(condition.getField()), (Comparable) condition.getValue()));
                                break;
                            case "lessThan":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder
                                        .lessThan(root.get(condition.getField()), (Comparable) condition.getValue()));
                                break;
                            case "greaterThanOrEquals":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.greaterThanOrEqualTo(
                                        root.get(condition.getField()), (Comparable) condition.getValue()));
                                break;
                            case "lessThanOrEquals":
                                spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.lessThanOrEqualTo(
                                        root.get(condition.getField()), (Comparable) condition.getValue()));
                                break;
                            default:
                                createLogger.createLogger("error", path, "POST", "", "");
                                throw new IllegalArgumentException(
                                        "Operator " + condition.getOperator() + " is not supported.");
                        }
                    }
                }

                // Fetch the data with pagination for sales items
                pageable = PageRequest.of(salesRequest.getPage() != null ? salesRequest.getPage() : 0, // Page number is
                                                                                                       // zero-based
                        salesRequest.getSize() != null ? salesRequest.getSize() : 20);

                // Prepare response data
                reportData = new ArrayList<>();
                totalRevenue = BigDecimal.ZERO;
                totalTaxCollected = BigDecimal.ZERO;

                // Fetch all sales items based on tenant name and date range
                salesItems = salesItemRepository.findAll(spec, pageable);

                for (SalesItem item : salesItems) {
                    if (item.getTax_id() != null) {
                        // Fetch the Tax entity using the tax ID
                        Tax tax = taxRepository.findById(item.getTax_id()).orElse(null);
                        if (tax != null) {
                            BigDecimal gstAmount = BigDecimal.valueOf(item.getTotal_price())
                                    .multiply(tax.getRate())
                                    .divide(BigDecimal.valueOf(100));

                            totalRevenue = totalRevenue.add(BigDecimal.valueOf(item.getTotal_price()));
                            totalTaxCollected = totalTaxCollected.add(gstAmount);
                            Double paid = item.getSales().getTotal_paid_for_sale();
                            if (paid != null) {
                                totalPaid = totalPaid.add(BigDecimal.valueOf(paid));
                            }

                            // Calculate total cost (adjust as necessary for your cost structure)
                            BigDecimal totalCost = BigDecimal.valueOf(item.getUnit_price())
                                    .multiply(BigDecimal.valueOf(item.getQuantity())); // Calculate total cost for the
                                                                                       // item

                            // Calculate profit earned for this item
                            BigDecimal profitEarned = BigDecimal.valueOf(item.getTotal_price()).subtract(totalCost); // Profit
                                                                                                                     // calculation

                            // Create report entry
                            Map<String, Object> reportEntry = new LinkedHashMap<>();
                            reportEntry.put("sale_id", item.getSales().getId());
                            reportEntry.put("sale_item_id", item.getId());
                            reportEntry.put("total_price", MoneyUtils.truncateToTwoDecimals(item.getTotal_price()));
                            reportEntry.put("tax_amount", MoneyUtils.truncateToTwoDecimals(gstAmount.doubleValue()));
                            reportEntry.put("payment_received",
                                    paid != null
                                            ? MoneyUtils.truncateToTwoDecimals(paid)
                                            : 0.00);
                            reportEntry.put("profit_earned", profitEarned);

                            if ((item.getProduct_id() != null) && "product".equals(item.getType())) {
                                reportEntry.put("product_id", item.getProduct_id());
                                reportEntry.put("product_name", productRepository
                                        .findProductNameById(Long.valueOf(item.getProduct_id()), tenantName));
                                Long categoryId = productRepository
                                        .findCategoryIdByProductId(Long.valueOf(item.getProduct_id()), tenantName);
                                reportEntry.put("category_id", categoryId);
                                reportEntry.put("category_name",
                                        categoryRepository.findCategoryNameById(categoryId, tenantName));
                                Long brandId = productRepository
                                        .findBrandIdByProductId(Long.valueOf(item.getProduct_id()), tenantName);
                                reportEntry.put("brand_id", brandId);
                                reportEntry.put("brand_name", brandRepository.findBrandNameById(brandId, tenantName));
                            }

                            // Fetch payment method from Payments model
                            String paymentMethod = item.getSales().getPayment_method();
                            String paymentSubMethod = item.getSales().getPayment_sub_method();

                            reportEntry.put("payment_method",
                                    (paymentMethod != null && !paymentMethod.isEmpty())
                                            ? paymentMethod
                                            : "N/A");

                            reportEntry.put("payment_sub_method",
                                    (paymentSubMethod != null && !paymentSubMethod.isEmpty())
                                            ? paymentSubMethod
                                            : "N/A");

                            reportEntry.put("sale_date", String.valueOf(item.getCreated_at())); // Adjust based on your
                                                                                                // actual date field
                            reportData.add(reportEntry);
                        }
                    }
                }

                // Prepare column details
                column = new LinkedHashMap<>();
                column.put("sale_id", "SALE ID");
                column.put("sale_item_id", "SALE ITEM ID");
                column.put("total_price", "TOTAL PRICE");
                column.put("tax_amount", "TAX AMOUNT");
                column.put("payment_received", "PAYMENT RECEIVED");
                column.put("profit_earned", "PROFIT EARNED");
                column.put("payment_method", "PAYMENT METHOD");
                column.put("payment_sub_method", "PAYMENT SUB METHOD");
                column.put("sale_date", "SALE DATE");
                column.put("product_id", "PRODUCT ID");
                column.put("product_name", "PRODUCT NAME");
                column.put("category_id", "CATEGORY ID");
                column.put("category_name", "CATEGORY NAME");
                column.put("brand_id", "BRAND ID");
                column.put("brand_name", "BRAND NAME");

                // Prepare pagination details
                pagination = new LinkedHashMap<>();
                pagination.put("currentPage", salesItems.getNumber() + 1); // Page number is 0-based
                pagination.put("totalPages", salesItems.getTotalPages());
                pagination.put("pageSize", pageable.getPageSize());
                pagination.put("totalRecords", salesItems.getTotalElements()); // Total records from the Page object

                // Prepare summary details
                summary = new LinkedHashMap<>();
                summary.put("totalRevenue", MoneyUtils.truncateToTwoDecimals(totalRevenue.doubleValue()));
                summary.put("totalTaxCollected", MoneyUtils.truncateToTwoDecimals(totalTaxCollected.doubleValue()));
                summary.put("totalPaymentsReceived",
                        MoneyUtils.truncateToTwoDecimals(totalPaid.doubleValue()));

                // Prepare profitability response data
                Map<String, Object> profitabilityResponseData = new LinkedHashMap<>();
                profitabilityResponseData.put("module", "financial");
                profitabilityResponseData.put("reportType", reportType);
                profitabilityResponseData.put("startDate", String.valueOf(salesRequest.getStartDate()));
                profitabilityResponseData.put("endDate", String.valueOf(salesRequest.getEndDate()));
                profitabilityResponseData.put("reportData", reportData);
                profitabilityResponseData.put("column", column);
                profitabilityResponseData.put("pagination", pagination);
                profitabilityResponseData.put("summary", summary);

                // Create and return the structured ApiResponseStructure
                return new ApiResponseStructure<>("Success", 200, "Data retrieved.", profitabilityResponseData);

            default:
                createLogger.createLogger("error", path, "POST", "Invalid report type provided.", "");
                return new ApiResponseStructure<>("Error", 400, "Invalid report type provided.", new HashMap<>());
        }
    }

}