package com.pressing.modules.catalog;

import com.pressing.common.exception.ApiException;
import com.pressing.domain.catalog.Article;
import com.pressing.domain.catalog.ArticleColor;
import com.pressing.domain.catalog.ArticleColorRepository;
import com.pressing.domain.catalog.ArticleRepository;
import com.pressing.domain.catalog.ArticleType;
import com.pressing.domain.catalog.ArticleTypeRepository;
import com.pressing.domain.catalog.Pricing;
import com.pressing.domain.catalog.PricingRepository;
import com.pressing.domain.catalog.ServiceOffering;
import com.pressing.domain.catalog.ServiceOfferingRepository;
import com.pressing.domain.catalog.StorageLocation;
import com.pressing.domain.catalog.StorageLocationRepository;
import com.pressing.modules.audit.AuditService;
import com.pressing.modules.catalog.dto.CatalogDtos.ArticleRequest;
import com.pressing.modules.catalog.dto.CatalogDtos.ArticleResponse;
import com.pressing.modules.catalog.dto.CatalogDtos.ArticleTypeRequest;
import com.pressing.modules.catalog.dto.CatalogDtos.ArticleTypeResponse;
import com.pressing.modules.catalog.dto.CatalogDtos.ColorRequest;
import com.pressing.modules.catalog.dto.CatalogDtos.ColorResponse;
import com.pressing.modules.catalog.dto.CatalogDtos.LocationRequest;
import com.pressing.modules.catalog.dto.CatalogDtos.LocationResponse;
import com.pressing.modules.catalog.dto.CatalogDtos.PricingRequest;
import com.pressing.modules.catalog.dto.CatalogDtos.PricingResponse;
import com.pressing.modules.catalog.dto.CatalogDtos.ServiceRequest;
import com.pressing.modules.catalog.dto.CatalogDtos.ServiceResponse;
import com.pressing.modules.common.DomainSupport;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

    private final ArticleTypeRepository articleTypeRepository;
    private final ArticleRepository articleRepository;
    private final ArticleColorRepository colorRepository;
    private final StorageLocationRepository locationRepository;
    private final ServiceOfferingRepository serviceRepository;
    private final PricingRepository pricingRepository;
    private final DomainSupport domainSupport;
    private final AuditService auditService;

    public CatalogService(
            ArticleTypeRepository articleTypeRepository,
            ArticleRepository articleRepository,
            ArticleColorRepository colorRepository,
            StorageLocationRepository locationRepository,
            ServiceOfferingRepository serviceRepository,
            PricingRepository pricingRepository,
            DomainSupport domainSupport,
            AuditService auditService
    ) {
        this.articleTypeRepository = articleTypeRepository;
        this.articleRepository = articleRepository;
        this.colorRepository = colorRepository;
        this.locationRepository = locationRepository;
        this.serviceRepository = serviceRepository;
        this.pricingRepository = pricingRepository;
        this.domainSupport = domainSupport;
        this.auditService = auditService;
    }

    // --- TYPES D'ARTICLE ---

    public List<ArticleTypeResponse> articleTypes() {
        return articleTypeRepository.findBySiteIdOrderByLabelAsc(domainSupport.siteId()).stream().map(this::toType).toList();
    }

    @Transactional
    public ArticleTypeResponse createType(ArticleTypeRequest req) {
        ArticleType entity = new ArticleType();
        entity.setSiteId(domainSupport.siteId());
        applyType(entity, req);
        ArticleType saved = articleTypeRepository.save(entity);
        auditService.log("CREATE_ARTICLE_TYPE", "Création type " + saved.getLabel(), "ArticleType", saved.getId());
        return toType(saved);
    }

    @Transactional
    public ArticleTypeResponse updateType(Long id, ArticleTypeRequest req) {
        ArticleType entity = articleTypeRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Type d'article introuvable"));
        applyType(entity, req);
        return toType(articleTypeRepository.save(entity));
    }

    @Transactional
    public void deleteType(Long id) {
        ArticleType entity = articleTypeRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Type d'article introuvable"));
        articleTypeRepository.delete(entity);
    }

    // --- ARTICLES ---

    public List<ArticleResponse> articles(Long typeId) {
        Long siteId = domainSupport.siteId();
        List<Article> list = typeId == null
                ? articleRepository.findBySiteIdOrderByLabelAsc(siteId)
                : articleRepository.findBySiteIdAndArticleTypeIdOrderByLabelAsc(siteId, typeId);
        return list.stream().map(this::toArticle).toList();
    }

    @Transactional
    public ArticleResponse createArticle(ArticleRequest req) {
        Article entity = new Article();
        entity.setSiteId(domainSupport.siteId());
        applyArticle(entity, req);
        Article saved = articleRepository.save(entity);
        auditService.log("CREATE_ARTICLE", "Création article " + saved.getLabel(), "Article", saved.getId());
        return toArticle(saved);
    }

    @Transactional
    public ArticleResponse updateArticle(Long id, ArticleRequest req) {
        Article entity = articleRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Article introuvable"));
        applyArticle(entity, req);
        return toArticle(articleRepository.save(entity));
    }

    @Transactional
    public void deleteArticle(Long id) {
        Article entity = articleRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Article introuvable"));
        articleRepository.delete(entity);
    }

    // --- COULEURS ---

    public List<ColorResponse> colors() {
        return colorRepository.findBySiteIdOrderByLabelAsc(domainSupport.siteId()).stream().map(this::toColor).toList();
    }

    @Transactional
    public ColorResponse createColor(ColorRequest req) {
        ArticleColor entity = new ArticleColor();
        entity.setSiteId(domainSupport.siteId());
        applyColor(entity, req);
        return toColor(colorRepository.save(entity));
    }

    @Transactional
    public ColorResponse updateColor(Long id, ColorRequest req) {
        ArticleColor entity = colorRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Couleur introuvable"));
        applyColor(entity, req);
        return toColor(colorRepository.save(entity));
    }

    @Transactional
    public void deleteColor(Long id) {
        ArticleColor entity = colorRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Couleur introuvable"));
        colorRepository.delete(entity);
    }


    // --- EMPLACEMENTS ---

public List<LocationResponse> locations() {
    return locationRepository.findBySiteIdOrderByRowCodeAscLabelAsc(domainSupport.siteId())
            .stream()
            .map(this::toLocation)
            .toList();
}

@Transactional
public LocationResponse createLocation(LocationRequest req) {
    StorageLocation entity = new StorageLocation();
    entity.setSiteId(domainSupport.siteId());
    applyLocation(entity, req);
    StorageLocation saved = locationRepository.save(entity);
    auditService.log("CREATE_LOCATION", "Création emplacement " + saved.getLabel(), "StorageLocation", saved.getId());
    return toLocation(saved);
}

@Transactional
public LocationResponse updateLocation(Long id, LocationRequest req) {
    StorageLocation entity = locationRepository.findByIdAndSiteId(id, domainSupport.siteId())
            .orElseThrow(() -> ApiException.notFound("Emplacement introuvable"));
    applyLocation(entity, req);
    return toLocation(locationRepository.save(entity));
}

@Transactional
public void deleteLocation(Long id) {
    StorageLocation entity = locationRepository.findByIdAndSiteId(id, domainSupport.siteId())
            .orElseThrow(() -> ApiException.notFound("Emplacement introuvable"));
    locationRepository.delete(entity);
    auditService.log("DELETE_LOCATION", "Suppression emplacement " + entity.getLabel(), "StorageLocation", id);
}

private void applyLocation(StorageLocation entity, LocationRequest req) {
    entity.setRowCode(req.rowCode().toUpperCase().trim());
    entity.setLabel(req.label() != null && !req.label().isBlank() ? req.label().trim() : entity.getRowCode());
    entity.setActive(req.active() == null || req.active());
}

private LocationResponse toLocation(StorageLocation l) {
    return new LocationResponse(l.getId(), l.getRowCode(), l.getLabel(), l.isActive());
}


    // --- SERVICES ---

    public List<ServiceResponse> services() {
        return serviceRepository.findBySiteIdOrderByLabelAsc(domainSupport.siteId()).stream().map(this::toService).toList();
    }

    @Transactional
    public ServiceResponse createService(ServiceRequest req) {
        ServiceOffering entity = new ServiceOffering();
        entity.setSiteId(domainSupport.siteId());
        applyService(entity, req);
        ServiceOffering saved = serviceRepository.save(entity);
        auditService.log("CREATE_SERVICE", "Création service " + saved.getLabel(), "Service", saved.getId());
        return toService(saved);
    }

    @Transactional
    public ServiceResponse updateService(Long id, ServiceRequest req) {
        ServiceOffering entity = serviceRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Service introuvable"));
        applyService(entity, req);
        return toService(serviceRepository.save(entity));
    }

    @Transactional
    public void deleteService(Long id) {
        ServiceOffering entity = serviceRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Service introuvable"));
        serviceRepository.delete(entity);
    }

    // --- TARIFS ---

    public List<PricingResponse> pricings() {
        return pricingRepository.findBySiteIdOrderByIdDesc(domainSupport.siteId()).stream().map(this::toPricing).toList();
    }

    @Transactional
    public PricingResponse createPricing(PricingRequest req) {
        Pricing entity = new Pricing();
        entity.setSiteId(domainSupport.siteId());
        applyPricing(entity, req);
        return toPricing(pricingRepository.save(entity));
    }


    @Transactional
    public PricingResponse getPricingForServiceAndType(Long serviceId, Long articleTypeId) {
    return pricingRepository.findBySiteIdAndServiceIdAndArticleTypeId(domainSupport.siteId(), serviceId, articleTypeId)
            .map(this::toPricing)
            .orElseThrow(() -> ApiException.notFound("Aucun tarif configuré pour ce service et ce type d'article"));
    }

    @Transactional
    public PricingResponse updatePricing(Long id, PricingRequest req) {
        Pricing entity = pricingRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Tarif introuvable"));
        applyPricing(entity, req);
        return toPricing(pricingRepository.save(entity));
    }

    @Transactional
    public void deletePricing(Long id) {
        Pricing entity = pricingRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Tarif introuvable"));
        pricingRepository.delete(entity);
    }

    // --- METHODES PRIVEES MAPPING ---

    private void applyType(ArticleType entity, ArticleTypeRequest req) {
        entity.setLabel(req.label());
        entity.setDescription(req.description());
        entity.setActive(req.active() == null || req.active());
    }


    private void applyArticle(Article entity, ArticleRequest req) {
    ArticleType type = articleTypeRepository.findByIdAndSiteId(req.articleTypeId(), domainSupport.siteId())
            .orElseThrow(() -> ApiException.notFound("Type d'article introuvable"));
    entity.setArticleType(type);
    entity.setLabel(req.label());
    entity.setActive(req.active() == null || req.active());
}

    private ArticleResponse toArticle(Article a) {
        return new ArticleResponse(
                a.getId(),
                a.getArticleType().getId(),
                a.getArticleType().getLabel(),
                a.getLabel(),
                a.isActive()
        );
    }

    private void applyColor(ArticleColor entity, ColorRequest req) {
        entity.setLabel(req.label());
        entity.setHexCode(req.hexCode());
        entity.setActive(req.active() == null || req.active());
    }

 

   private void applyService(ServiceOffering entity, ServiceRequest req) {
    // Vérification de l'unicité globale du code dans toute la table
    if (entity.getId() == null) {
        if (serviceRepository.existsByCode(req.code())) {
            throw ApiException.badRequest("Le code service '" + req.code() + "' existe déjà.");
        }
    } else {
        if (serviceRepository.existsByCodeAndIdNot(req.code(), entity.getId())) {
            throw ApiException.badRequest("Le code service '" + req.code() + "' est déjà utilisé.");
        }
    }

    entity.setCode(req.code());
    entity.setLabel(req.label());
    entity.setDescription(req.description());
    entity.setActive(req.active() == null || req.active());
}

    private void applyPricing(Pricing entity, PricingRequest req) {
        ServiceOffering service = serviceRepository.findByIdAndSiteId(req.serviceId(), domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Service introuvable"));
        ArticleType type = articleTypeRepository.findByIdAndSiteId(req.articleTypeId(), domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Type d'article introuvable"));
        entity.setService(service);
        entity.setArticleType(type);
        entity.setUnitPrice(req.unitPrice());
    }

    private ArticleTypeResponse toType(ArticleType t) {
        return new ArticleTypeResponse(t.getId(), t.getLabel(), t.getDescription(), t.isActive());
    }



    private ColorResponse toColor(ArticleColor c) {
        return new ColorResponse(c.getId(), c.getLabel(), c.getHexCode(), c.isActive());
    }


    

    private ServiceResponse toService(ServiceOffering s) {
        return new ServiceResponse(s.getId(), s.getCode(), s.getLabel(), s.getDescription(), s.isActive());
    }

    private PricingResponse toPricing(Pricing p) {
        return new PricingResponse(p.getId(), p.getService().getId(), p.getService().getLabel(),
                p.getArticleType().getId(), p.getArticleType().getLabel(), p.getUnitPrice());
    }
}


/*package com.pressing.modules.catalog;

import com.pressing.common.exception.ApiException;
import com.pressing.domain.catalog.Article;
import com.pressing.domain.catalog.ArticleColor;
import com.pressing.domain.catalog.ArticleRepository;
import com.pressing.domain.catalog.ArticleType;
import com.pressing.domain.catalog.ArticleTypeRepository;
import com.pressing.domain.catalog.ArticleColorRepository;
import com.pressing.domain.catalog.Pricing;
import com.pressing.domain.catalog.PricingRepository;
import com.pressing.domain.catalog.ServiceOffering;
import com.pressing.domain.catalog.ServiceOfferingRepository;
import com.pressing.domain.catalog.StorageLocation;
import com.pressing.domain.catalog.StorageLocationRepository;
import com.pressing.domain.site.Site;
import com.pressing.domain.site.SiteRepository;
import com.pressing.modules.audit.AuditService;
import com.pressing.modules.catalog.dto.CatalogDtos.ArticleRequest;
import com.pressing.modules.catalog.dto.CatalogDtos.ArticleResponse;
import com.pressing.modules.catalog.dto.CatalogDtos.ArticleTypeRequest;
import com.pressing.modules.catalog.dto.CatalogDtos.ArticleTypeResponse;
import com.pressing.modules.catalog.dto.CatalogDtos.ColorRequest;
import com.pressing.modules.catalog.dto.CatalogDtos.ColorResponse;
import com.pressing.modules.catalog.dto.CatalogDtos.LocationRequest;
import com.pressing.modules.catalog.dto.CatalogDtos.LocationResponse;
import com.pressing.modules.catalog.dto.CatalogDtos.PricingRequest;
import com.pressing.modules.catalog.dto.CatalogDtos.PricingResponse;
import com.pressing.modules.catalog.dto.CatalogDtos.ServiceRequest;
import com.pressing.modules.catalog.dto.CatalogDtos.ServiceResponse;
import com.pressing.modules.catalog.dto.CatalogDtos.SiteRequest;
import com.pressing.modules.catalog.dto.CatalogDtos.SiteResponse;
import com.pressing.modules.common.DomainSupport;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

    private final SiteRepository siteRepository;
    private final ArticleTypeRepository articleTypeRepository;
    private final ArticleRepository articleRepository;
    private final ArticleColorRepository colorRepository;
    private final StorageLocationRepository locationRepository;
    private final ServiceOfferingRepository serviceRepository;
    private final PricingRepository pricingRepository;
    private final DomainSupport domainSupport;
    private final AuditService auditService;

    public CatalogService(
            SiteRepository siteRepository,
            ArticleTypeRepository articleTypeRepository,
            ArticleRepository articleRepository,
            ArticleColorRepository colorRepository,
            StorageLocationRepository locationRepository,
            ServiceOfferingRepository serviceRepository,
            PricingRepository pricingRepository,
            DomainSupport domainSupport,
            AuditService auditService
    ) {
        this.siteRepository = siteRepository;
        this.articleTypeRepository = articleTypeRepository;
        this.articleRepository = articleRepository;
        this.colorRepository = colorRepository;
        this.locationRepository = locationRepository;
        this.serviceRepository = serviceRepository;
        this.pricingRepository = pricingRepository;
        this.domainSupport = domainSupport;
        this.auditService = auditService;
    }

    public List<SiteResponse> sites(String q) {
        return siteRepository.findAll().stream()
                .filter(s -> q == null || q.isBlank() || contains(s.getName(), q) || contains(s.getCity(), q) || contains(s.getCode(), q))
                .map(this::toSite)
                .toList();
    }

    @Transactional
    public SiteResponse createSite(SiteRequest req) {
        Site site = applySite(new Site(), req);
        Site saved = siteRepository.save(site);
        return toSite(saved);
    }

    @Transactional
    public SiteResponse updateSite(Long id, SiteRequest req) {
        Site site = siteRepository.findById(id).orElseThrow(() -> ApiException.notFound("Site introuvable"));
        Site saved = siteRepository.save(applySite(site, req));
        return toSite(saved);
    }

    @Transactional
    public void deleteSite(Long id) {
        siteRepository.deleteById(id);
    }

    public List<ArticleTypeResponse> articleTypes() {
        return articleTypeRepository.findBySiteIdOrderByLabelAsc(domainSupport.siteId()).stream().map(this::toType).toList();
    }

    @Transactional
    public ArticleTypeResponse createType(ArticleTypeRequest req) {
        ArticleType entity = new ArticleType();
        entity.setSiteId(domainSupport.siteId());
        applyType(entity, req);
        ArticleType saved = articleTypeRepository.save(entity);
        auditService.log("CREATE_ARTICLE_TYPE", "Création type " + saved.getLabel(), "ArticleType", saved.getId());
        return toType(saved);
    }

    @Transactional
    public ArticleTypeResponse updateType(Long id, ArticleTypeRequest req) {
        ArticleType entity = articleTypeRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Type d'article introuvable"));
        applyType(entity, req);
        return toType(articleTypeRepository.save(entity));
    }

    @Transactional
    public void deleteType(Long id) {
        ArticleType entity = articleTypeRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Type d'article introuvable"));
        articleTypeRepository.delete(entity);
    }

    public List<ArticleResponse> articles(Long typeId) {
        Long siteId = domainSupport.siteId();
        List<Article> list = typeId == null
                ? articleRepository.findBySiteIdOrderByLabelAsc(siteId)
                : articleRepository.findBySiteIdAndArticleTypeIdOrderByLabelAsc(siteId, typeId);
        return list.stream().map(this::toArticle).toList();
    }

    @Transactional
    public ArticleResponse createArticle(ArticleRequest req) {
        Article entity = new Article();
        entity.setSiteId(domainSupport.siteId());
        applyArticle(entity, req);
        Article saved = articleRepository.save(entity);
        auditService.log("CREATE_ARTICLE", "Création article " + saved.getLabel(), "Article", saved.getId());
        return toArticle(saved);
    }

    @Transactional
    public ArticleResponse updateArticle(Long id, ArticleRequest req) {
        Article entity = articleRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Article introuvable"));
        applyArticle(entity, req);
        return toArticle(articleRepository.save(entity));
    }

    @Transactional
    public void deleteArticle(Long id) {
        Article entity = articleRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Article introuvable"));
        articleRepository.delete(entity);
    }

    public List<ColorResponse> colors() {
        return colorRepository.findBySiteIdOrderByLabelAsc(domainSupport.siteId()).stream().map(this::toColor).toList();
    }

    @Transactional
    public ColorResponse createColor(ColorRequest req) {
        ArticleColor entity = new ArticleColor();
        entity.setSiteId(domainSupport.siteId());
        applyColor(entity, req);
        return toColor(colorRepository.save(entity));
    }

    @Transactional
    public ColorResponse updateColor(Long id, ColorRequest req) {
        ArticleColor entity = colorRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Couleur introuvable"));
        applyColor(entity, req);
        return toColor(colorRepository.save(entity));
    }

    @Transactional
    public void deleteColor(Long id) {
        ArticleColor entity = colorRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Couleur introuvable"));
        colorRepository.delete(entity);
    }

    public List<LocationResponse> locations() {
        return locationRepository.findBySiteIdOrderByRowCodeAscSlotNumberAsc(domainSupport.siteId()).stream().map(this::toLocation).toList();
    }

    @Transactional
    public LocationResponse createLocation(LocationRequest req) {
        StorageLocation entity = new StorageLocation();
        entity.setSiteId(domainSupport.siteId());
        applyLocation(entity, req);
        return toLocation(locationRepository.save(entity));
    }

    @Transactional
    public LocationResponse updateLocation(Long id, LocationRequest req) {
        StorageLocation entity = locationRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Emplacement introuvable"));
        applyLocation(entity, req);
        return toLocation(locationRepository.save(entity));
    }

    @Transactional
    public void deleteLocation(Long id) {
        StorageLocation entity = locationRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Emplacement introuvable"));
        locationRepository.delete(entity);
    }

    public List<ServiceResponse> services() {
        return serviceRepository.findBySiteIdOrderByLabelAsc(domainSupport.siteId()).stream().map(this::toService).toList();
    }

    @Transactional
    public ServiceResponse createService(ServiceRequest req) {
        ServiceOffering entity = new ServiceOffering();
        entity.setSiteId(domainSupport.siteId());
        applyService(entity, req);
        ServiceOffering saved = serviceRepository.save(entity);
        auditService.log("CREATE_SERVICE", "Création service " + saved.getLabel(), "Service", saved.getId());
        return toService(saved);
    }

    @Transactional
    public ServiceResponse updateService(Long id, ServiceRequest req) {
        ServiceOffering entity = serviceRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Service introuvable"));
        applyService(entity, req);
        return toService(serviceRepository.save(entity));
    }

    @Transactional
    public void deleteService(Long id) {
        ServiceOffering entity = serviceRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Service introuvable"));
        serviceRepository.delete(entity);
    }

    public List<PricingResponse> pricings() {
        return pricingRepository.findBySiteIdOrderByIdDesc(domainSupport.siteId()).stream().map(this::toPricing).toList();
    }

    @Transactional
    public PricingResponse createPricing(PricingRequest req) {
        Pricing entity = new Pricing();
        entity.setSiteId(domainSupport.siteId());
        applyPricing(entity, req);
        return toPricing(pricingRepository.save(entity));
    }

    @Transactional
    public PricingResponse updatePricing(Long id, PricingRequest req) {
        Pricing entity = pricingRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Tarif introuvable"));
        applyPricing(entity, req);
        return toPricing(pricingRepository.save(entity));
    }

    @Transactional
    public void deletePricing(Long id) {
        Pricing entity = pricingRepository.findByIdAndSiteId(id, domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Tarif introuvable"));
        pricingRepository.delete(entity);
    }

    private Site applySite(Site site, SiteRequest req) {
        site.setName(req.name());
        site.setCode(req.code());
        site.setPhone(req.phone());
        site.setPhoneSecondary(req.phoneSecondary());
        site.setEmail(req.email());
        site.setAddress(req.address());
        site.setCity(req.city());
        site.setActivityDomain(req.activityDomain() == null ? "PRESSING" : req.activityDomain());
        site.setActive(req.active() == null || req.active());
        site.setDefaultTaxRate(req.defaultTaxRate() == null ? 18.0 : req.defaultTaxRate());
        return site;
    }

    private void applyType(ArticleType entity, ArticleTypeRequest req) {
        entity.setLabel(req.label());
        entity.setDescription(req.description());
        entity.setActive(req.active() == null || req.active());
    }

    private void applyArticle(Article entity, ArticleRequest req) {
        ArticleType type = articleTypeRepository.findByIdAndSiteId(req.articleTypeId(), domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Type d'article introuvable"));
        entity.setArticleType(type);
        entity.setLabel(req.label());
        entity.setSku(req.sku());
        entity.setDescription(req.description());
        entity.setActive(req.active() == null || req.active());
    }

    private void applyColor(ArticleColor entity, ColorRequest req) {
        entity.setLabel(req.label());
        entity.setHexCode(req.hexCode());
        entity.setActive(req.active() == null || req.active());
    }

    private void applyLocation(StorageLocation entity, LocationRequest req) {
        entity.setRowCode(req.rowCode().toUpperCase());
        entity.setSlotNumber(req.slotNumber());
        entity.setLabel(req.label() != null ? req.label() : req.rowCode() + "-" + req.slotNumber());
        entity.setOccupied(Boolean.TRUE.equals(req.occupied()));
        entity.setActive(req.active() == null || req.active());
    }

    private void applyService(ServiceOffering entity, ServiceRequest req) {
        entity.setCode(req.code());
        entity.setLabel(req.label());
        entity.setDescription(req.description());
        entity.setCategory(req.category() == null ? "WASH" : req.category());
        entity.setActive(req.active() == null || req.active());
    }

    private void applyPricing(Pricing entity, PricingRequest req) {
        ServiceOffering service = serviceRepository.findByIdAndSiteId(req.serviceId(), domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Service introuvable"));
        ArticleType type = articleTypeRepository.findByIdAndSiteId(req.articleTypeId(), domainSupport.siteId())
                .orElseThrow(() -> ApiException.notFound("Type d'article introuvable"));
        entity.setService(service);
        entity.setArticleType(type);
        entity.setUnitPrice(req.unitPrice());
    }

    private SiteResponse toSite(Site s) {
        return new SiteResponse(s.getId(), s.getName(), s.getCode(), s.getPhone(), s.getPhoneSecondary(), s.getEmail(),
                s.getAddress(), s.getCity(), s.getActivityDomain(), s.isActive(), s.getDefaultTaxRate());
    }

    private ArticleTypeResponse toType(ArticleType t) {
        return new ArticleTypeResponse(t.getId(), t.getLabel(), t.getDescription(), t.isActive());
    }

    private ArticleResponse toArticle(Article a) {
        return new ArticleResponse(a.getId(), a.getArticleType().getId(), a.getArticleType().getLabel(), a.getLabel(), a.getSku(), a.getDescription(), a.isActive());
    }

    private ColorResponse toColor(ArticleColor c) {
        return new ColorResponse(c.getId(), c.getLabel(), c.getHexCode(), c.isActive());
    }

    private LocationResponse toLocation(StorageLocation l) {
        return new LocationResponse(l.getId(), l.getRowCode(), l.getSlotNumber(), l.getLabel(), l.isOccupied(), l.isActive());
    }

    private ServiceResponse toService(ServiceOffering s) {
        return new ServiceResponse(s.getId(), s.getCode(), s.getLabel(), s.getDescription(), s.getCategory(), s.isActive());
    }

    private PricingResponse toPricing(Pricing p) {
        return new PricingResponse(p.getId(), p.getService().getId(), p.getService().getLabel(),
                p.getArticleType().getId(), p.getArticleType().getLabel(), p.getUnitPrice());
    }

    private boolean contains(String value, String q) {
        return value != null && value.toLowerCase().contains(q.toLowerCase());
    }
}
*/