package com.petshop.backend.service;

import com.petshop.backend.dto.refund.RefundItemResponse;
import com.petshop.backend.dto.refund.RefundRequest;
import com.petshop.backend.dto.refund.RefundResponse;
import com.petshop.backend.dto.sale.SaleRequest;
import com.petshop.backend.entity.Product;
import com.petshop.backend.entity.Refund;
import com.petshop.backend.entity.RefundItem;
import com.petshop.backend.entity.Sale;
import com.petshop.backend.exception.BusinessException;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.ProductRepository;
import com.petshop.backend.repository.RefundRepository;
import com.petshop.backend.repository.SaleRepository;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class RefundService {

    private final RefundRepository refundRepository;
    private final SaleRepository saleRepository;
    private final ProductRepository productRepository;
    private final SaleService saleService;

    public RefundService(RefundRepository refundRepository, 
                         SaleRepository saleRepository, 
                         ProductRepository productRepository,
                         @Lazy SaleService saleService) {
        this.refundRepository = refundRepository;
        this.saleRepository = saleRepository;
        this.productRepository = productRepository;
        this.saleService = saleService;
    }

    @Transactional
    public RefundResponse createRefund(RefundRequest request, Long customerId) {
        Sale sale = saleRepository.findById(request.getSaleId())
                .orElseThrow(() -> new BusinessException("Sale not found"));

        if (sale.getCustomer() == null) {
            throw new BusinessException("Sale has no associated customer");
        }

        if (customerId != null && !sale.getCustomer().getId().equals(customerId)) {
            throw new BusinessException("You can only create refunds for your own purchases");
        }

        Refund refund = new Refund();
        refund.setSaleId(sale.getSaleId());
        refund.setCustomerId(sale.getCustomer().getId());
        refund.setReason(request.getReason());
        refund.setStatus(Refund.RefundStatus.PENDING);

        BigDecimal totalAmount = BigDecimal.ZERO;
        for (var itemRequest : request.getItems()) {
            Product product = productRepository.findById(itemRequest.getProdId())
                    .orElseThrow(() -> new BusinessException("Product not found: " + itemRequest.getProdId()));

            BigDecimal unitPrice = sale.getItems().stream()
                    .filter(item -> item.getProduct().getProdId().equals(itemRequest.getProdId()))
                    .findFirst()
                    .map(item -> item.getUnitPrice())
                    .orElseThrow(() -> new BusinessException("Product " + itemRequest.getProdId() + " was not in original sale"));

            RefundItem refundItem = new RefundItem();
            refundItem.setProduct(product);
            refundItem.setQuantity(itemRequest.getQuantity());
            refundItem.setUnitPrice(unitPrice);
            refund.addItem(refundItem);

            totalAmount = totalAmount.add(unitPrice.multiply(BigDecimal.valueOf(itemRequest.getQuantity())));
        }
        
        refund.setAmount(totalAmount);
        Refund savedRefund = refundRepository.save(refund);
        return toResponse(savedRefund);
    }

    @Transactional(readOnly = true)
    public RefundResponse getRefundById(Long id, Long customerId) {
        Refund refund = refundRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Refund not found"));

        if (customerId != null && !refund.getCustomerId().equals(customerId)) {
            throw new ResourceNotFoundException("You can only view your own refunds");
        }

        return toResponse(refund);
    }

    @Transactional(readOnly = true)
    public List<RefundResponse> getAllRefunds(Long customerId) {
        List<Refund> refunds;

        if (customerId != null) {
            refunds = refundRepository.findByCustomerIdOrderByCreatedAtDesc(customerId);
        } else {
            refunds = refundRepository.findAllByOrderByCreatedAtDesc();
        }

        return refunds.stream()
                .map(this::toResponse)
                .collect(Collectors.toList());
    }

    @Transactional
    public RefundResponse updateRefundStatus(Long id, String status) {
        Refund refund = refundRepository.findByIdForUpdate(id)
                .orElseThrow(() -> new ResourceNotFoundException("Refund not found"));

        Refund.RefundStatus newStatus;
        try {
            newStatus = Refund.RefundStatus.valueOf(status.toUpperCase());
        } catch (IllegalArgumentException e) {
            throw new BusinessException("Invalid status: " + status);
        }

        if (refund.getStatus() == Refund.RefundStatus.PENDING && newStatus == Refund.RefundStatus.APPROVED) {
            Sale originalSale = saleRepository.findById(refund.getSaleId())
                    .orElseThrow(() -> new ResourceNotFoundException("Original sale not found"));

            SaleRequest saleRequest = new SaleRequest();
            saleRequest.setStoreId(originalSale.getStore().getStoreId());
            saleRequest.setCustomerId(refund.getCustomerId());
            saleRequest.setOriginalSaleId(refund.getSaleId());
            saleRequest.setIsRefund(true);
            saleRequest.setPaymentMethod("Card");
            saleRequest.setItems(refund.getItems().stream()
                    .map(item -> {
                        var ir = new com.petshop.backend.dto.sale.SaleItemRequest();
                        ir.setProdId(item.getProduct().getProdId());
                        ir.setQuantity(item.getQuantity());
                        return ir;
                    })
                    .collect(Collectors.toList()));
            
            saleService.createSale(saleRequest);
        }

        refund.setStatus(newStatus);
        Refund updatedRefund = refundRepository.save(refund);
        return toResponse(updatedRefund);
    }

    @Transactional
    public void deleteRefund(Long id) {
        if (!refundRepository.existsById(id)) {
            throw new ResourceNotFoundException("Refund not found");
        }
        refundRepository.deleteById(id);
    }

    private RefundResponse toResponse(Refund refund) {
        List<RefundItemResponse> itemResponses = refund.getItems().stream()
                .map(item -> new RefundItemResponse(
                        item.getId(),
                        item.getProduct().getProdId(),
                        item.getProduct().getProdName(),
                        item.getQuantity(),
                        item.getUnitPrice()
                ))
                .collect(Collectors.toList());

        return new RefundResponse(
                refund.getId(),
                refund.getSaleId(),
                refund.getCustomerId(),
                refund.getAmount(),
                refund.getReason(),
                refund.getStatus().name(),
                itemResponses,
                refund.getCreatedAt(),
                refund.getUpdatedAt()
        );
    }
}
