package com.pressing.modules.customer;

import com.pressing.common.dto.PageResponse;
import com.pressing.common.exception.ApiException;
import com.pressing.domain.customer.Customer;
import com.pressing.domain.customer.CustomerRepository;
import com.pressing.modules.audit.AuditService;
import com.pressing.modules.common.DomainSupport;
import com.pressing.modules.customer.dto.CustomerDtos.CustomerRequest;
import com.pressing.modules.customer.dto.CustomerDtos.CustomerResponse;
import jakarta.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.List;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class CustomerService {

    private final CustomerRepository repository;
    private final DomainSupport domainSupport;
    private final AuditService auditService;

    public CustomerService(CustomerRepository repository, DomainSupport domainSupport, AuditService auditService) {
        this.repository = repository;
        this.domainSupport = domainSupport;
        this.auditService = auditService;
    }

    @Transactional(readOnly = true)
    public PageResponse<CustomerResponse> search(String q, Boolean blacklisted, int page, int size) {
        Long siteId = domainSupport.siteId();
        Specification<Customer> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(cb.equal(root.get("siteId"), siteId));
            if (Boolean.TRUE.equals(blacklisted)) {
                predicates.add(cb.isTrue(root.get("blacklisted")));
            }
            if (q != null && !q.isBlank()) {
                String like = "%" + q.toLowerCase() + "%";
                predicates.add(cb.or(
                        cb.like(cb.lower(root.get("firstName")), like),
                        cb.like(cb.lower(root.get("lastName")), like),
                        cb.like(cb.lower(root.get("phone")), like),
                        cb.like(cb.lower(root.get("customerNumber")), like)
                ));
            }
            return cb.and(predicates.toArray(Predicate[]::new));
        };
        return PageResponse.from(repository.findAll(spec, PageRequest.of(page, size)).map(this::toDto));
    }

    @Transactional(readOnly = true)
    public CustomerResponse get(Long id) {
        return toDto(find(id));
    }

    @Transactional
    public CustomerResponse create(CustomerRequest req) {
        Customer customer = new Customer();
        customer.setSiteId(domainSupport.siteId());
        customer.setCustomerNumber(domainSupport.nextNumber("CLI", repository.countBySiteId(domainSupport.siteId()) + 1));
        apply(customer, req);
        Customer saved = repository.save(customer);
        auditService.log("CREATE_CUSTOMER", "Création client " + saved.getLastName(), "Customer", saved.getId());
        return toDto(saved);
    }

    @Transactional
    public CustomerResponse update(Long id, CustomerRequest req) {
        Customer customer = find(id);
        apply(customer, req);
        return toDto(repository.save(customer));
    }

    @Transactional
    public void delete(Long id) {
        repository.delete(find(id));
    }

    public Customer find(Long id) {
        return repository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Client introuvable"));
    }

    public Customer findOrCreate(CustomerRequest req, Long customerId) {
        if (customerId != null) {
            return find(customerId);
        }
        Customer customer = new Customer();
        customer.setSiteId(domainSupport.siteId());
        customer.setCustomerNumber(domainSupport.nextNumber("CLI", repository.countBySiteId(domainSupport.siteId()) + 1));
        apply(customer, req);
        return repository.save(customer);
    }

    private void apply(Customer customer, CustomerRequest req) {
        customer.setFirstName(req.firstName());
        customer.setLastName(req.lastName());
        customer.setPhone(req.phone());
        customer.setPhoneSecondary(req.phoneSecondary());
        customer.setEmail(req.email());
        customer.setAddress(req.address());
        customer.setBlacklisted(Boolean.TRUE.equals(req.blacklisted()));
        customer.setBlacklistReason(req.blacklistReason());
    }

    private CustomerResponse toDto(Customer c) {
        return new CustomerResponse(c.getId(), c.getCustomerNumber(), c.getFirstName(), c.getLastName(), c.getPhone(),
                c.getPhoneSecondary(), c.getEmail(), c.getAddress(), c.isBlacklisted(), c.getBlacklistReason(),
                c.getCreatedBy(), c.getCreatedAt());
    }
}
