package com.nebula.erp.purchase.service;

import com.nebula.erp.purchase.model.DebitNote;
import com.nebula.erp.purchase.model.PurchaseReturn;
import com.nebula.erp.purchase.repository.DebitRepository;
import com.nebula.erp.purchase.repository.GRNRepository;
import com.nebula.erp.purchase.repository.PurchaseRepository;
import com.nebula.erp.purchase.repository.PurchaseReturnRepository;
import com.nebula.erp.purchase.requestmodel.DebitRequest;
import com.nebula.erp.purchase.utility.CreateLogger;
import com.nebula.erp.purchase.utility.JwtRequestUtils;
import jakarta.persistence.EntityNotFoundException;
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.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;

@Service
public class DebitService {

    @Autowired
    private DebitRepository debitRepository;

    @Autowired
    private PurchaseReturnRepository purchaseReturnRepository;

    @Autowired
    private GRNRepository grnRepository;

    @Autowired
    private PurchaseRepository purchaseRepository;

    @Autowired
    private JwtRequestUtils jwtRequestUtils;

    @Autowired
    private CreateLogger createLogger;

    private static final String path = "/debit-notes";

    public Page<DebitNote> getAllDebitNotes(int page, int size, String purchase_return_id) {
        String tenantName = jwtRequestUtils.getTenantName();

        Pageable pageable = PageRequest.of(page - 1, size);

        // Use a specification or dynamic query to apply filters
        Specification<DebitNote> spec = Specification.where(null);

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

        // Filter by patient ID if provided
        if (purchase_return_id != null) {
            spec = spec.and((root, query, criteriaBuilder) ->
                    criteriaBuilder.equal(root.get("purchase_return_id"), purchase_return_id));
        }

        return debitRepository.findAll(spec, pageable);
    }

    public DebitNote createDebit(DebitRequest debitRequest){
        // Extract Authorization headers from the request
        String tenantName = jwtRequestUtils.getTenantName();
        String userId = jwtRequestUtils.getUserId();

        // Check sale id is exist in the system
        grnRepository.findByIdAndTenant(debitRequest.getGrn_id(), tenantName)
                .orElseThrow(() -> new ResourceNotFoundException("GRN not found with id: " + debitRequest.getGrn_id()));

        // Check if a SalesReturn record exists with the given sale ID and tenant
        PurchaseReturn existingPurchaseReturn = purchaseReturnRepository.findByIdAndTenant(debitRequest.getPurchase_return_id(), tenantName)
                .orElseThrow(() -> new ResourceNotFoundException("Purchase return not found with id: " + debitRequest.getPurchase_return_id()));

        double debitLimit = existingPurchaseReturn.getTotal_return_amount().doubleValue();

        // Check if the invoice_number already exists
        List<DebitNote> existingDebitNotes = debitRepository.findByPurchaseOrPurchaseReturn(debitRequest.getGrn_id(), debitRequest.getPurchase_return_id(), tenantName);

        double usedDebit = existingDebitNotes.stream()
                .mapToDouble(DebitNote::getRefund_amount)
                .sum();

        if((usedDebit + debitRequest.getRefund_amount()) > debitLimit){
            createLogger.createLogger("application", path, "POST", "Debit amount is already refunded. Not able to create debit note!", "");
            throw new IllegalArgumentException("Debit amount is already refunded. Not able to create debit note!");
        }

        // Prepare sales return object
        DebitNote debitNote = new DebitNote();
        debitNote.setDebit_note_id(debitRequest.getDebit_note_id());
        debitNote.setInvoice_id(debitRequest.getInvoice_id());
        debitNote.setSupplier_id(debitRequest.getSupplier_id());
        debitNote.setDate(debitRequest.getDate());
        debitNote.setGrn_id(debitRequest.getGrn_id());
        debitNote.setPurchase_return_id(debitRequest.getPurchase_return_id());
        debitNote.setDebit_amount(debitLimit);
        debitNote.setRefund_amount(debitRequest.getRefund_amount());
        debitNote.setDiscount_amount(debitRequest.getDiscount_amount());
        debitNote.setPayment_method(debitRequest.getPayment_method());
        debitNote.setReason(debitRequest.getReason());
        debitNote.setCreated_by(userId);
        debitNote.setTenant(tenantName);

        return debitRepository.save(debitNote);
    }

    public Optional<DebitNote> getDebit(Long id){
        String tenantName = jwtRequestUtils.getTenantName();
        return debitRepository.findByIdAndTenant(id, tenantName);
    }

}