package com.pressing.modules.discount;

import com.pressing.common.dto.PageResponse;
import com.pressing.common.exception.ApiException;
import com.pressing.domain.customer.CustomerRepository;
import com.pressing.domain.discount.Discount;
import com.pressing.domain.discount.DiscountRepository;
import com.pressing.modules.audit.AuditService;
import com.pressing.modules.common.DomainSupport;
import com.pressing.modules.discount.dto.DiscountDtos.DiscountRequest;
import com.pressing.modules.discount.dto.DiscountDtos.DiscountResponse;
import jakarta.persistence.criteria.Predicate;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly = true)
public class DiscountService {

    private final DiscountRepository discountRepository;
    private final CustomerRepository customerRepository;
    private final DomainSupport domainSupport;
    private final AuditService auditService;

    public DiscountService(
            DiscountRepository discountRepository,
            CustomerRepository customerRepository,
            DomainSupport domainSupport,
            AuditService auditService
    ) {
        this.discountRepository = discountRepository;
        this.customerRepository = customerRepository;
        this.domainSupport = domainSupport;
        this.auditService = auditService;
    }

    public PageResponse<DiscountResponse> searchDiscounts(String q, int page, int size) {
        Long siteId = domainSupport.siteId();
        Specification<Discount> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(cb.equal(root.get("siteId"), siteId));
            if (q != null && !q.isBlank()) {
                predicates.add(cb.like(cb.lower(root.get("name")), "%" + q.toLowerCase() + "%"));
            }
            return cb.and(predicates.toArray(Predicate[]::new));
        };
        return PageResponse.from(discountRepository.findAll(spec, PageRequest.of(page, size)).map(this::toDto));
    }

    // Bonus/réduction actif le plus avantageux pour un client donné (utilisé pour le
    // pré-remplissage automatique au dépôt). Priorité au bonus spécifique au client
    // (customerId renseigné) plutôt qu'à un bonus global, puis au taux le plus élevé.
    public DiscountResponse findActiveDiscountForCustomer(Long customerId) {
        Long siteId = domainSupport.siteId();
        List<Discount> candidates = discountRepository.findActiveDiscountsForCustomer(siteId, customerId, LocalDate.now());
        return candidates.stream()
                .filter(Discount::isActive)
                .max(Comparator
                        .comparing((Discount d) -> d.getCustomerId() != null)
                        .thenComparing(Discount::getRate))
                .map(this::toDto)
                .orElse(null);
    }

    @Transactional
    public DiscountResponse createDiscount(DiscountRequest req) {
        Long siteId = domainSupport.siteId();
        Discount discount = new Discount();
        discount.setSiteId(siteId);
        discount.setCustomerId(req.customerId());
        discount.setName(req.name());
        discount.setRate(req.rate());
        discount.setStartDate(req.startDate());
        discount.setEndDate(req.endDate());
        discount.setCreatedBy(SecurityContextHolder.getContext().getAuthentication().getName());

        Discount saved = discountRepository.save(discount);
        auditService.log("CREATE_DISCOUNT", "Création réduction: " + saved.getName() + " (" + saved.getRate() + "%)", "Discount", saved.getId());
        return toDto(saved);
    }

    @Transactional
    public DiscountResponse updateDiscount(Long id, DiscountRequest req) {
        Discount discount = findById(id);
        discount.setName(req.name());
        discount.setRate(req.rate());
        discount.setStartDate(req.startDate());
        discount.setEndDate(req.endDate());
        discount.setCustomerId(req.customerId());

        Discount updated = discountRepository.save(discount);
        auditService.log("UPDATE_DISCOUNT", "Modification réduction ID: " + id, "Discount", id);
        return toDto(updated);
    }

    @Transactional
    public void deleteDiscount(Long id) {
        Discount discount = findById(id);
        discountRepository.delete(discount);
        auditService.log("DELETE_DISCOUNT", "Suppression réduction ID: " + id, "Discount", id);
    }

    private Discount findById(Long id) {
        return discountRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Réduction introuvable"));
    }

    private DiscountResponse toDto(Discount d) {
        String customerName = "GLOBAL";
        if (d.getCustomerId() != null) {
            customerName = customerRepository.findById(d.getCustomerId())
                    .map(c -> c.getFirstName() + " " + c.getLastName())
                    .orElse("Client Inconnu");
        }
        return new DiscountResponse(
                d.getId(),
                d.getCustomerId(),
                customerName,
                d.getName(),
                d.getRate(),
                d.getStartDate(),
                d.getEndDate(),
                d.isActive(),
                d.getCreatedBy()
        );
    }
}