package com.ecep.contract.ds.other.service;

import java.time.LocalDate;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.ecep.contract.IEntityService;
import com.ecep.contract.ds.other.repository.InventoryRepository;
import com.ecep.contract.model.Inventory;
import com.ecep.contract.util.SpecificationUtils;

import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType;
import lombok.Getter;
import lombok.Setter;

@Lazy
@Service
@CacheConfig(cacheNames = "inventory")
public class InventoryService implements IEntityService<Inventory> {
    @Lazy
    @Autowired
    private InventoryRepository inventoryRepository;
    @Getter
    @Setter
    private int searchPageSize = 10;

    @Cacheable(key = "#p0")
    public Inventory findById(Integer id) {
        return inventoryRepository.findById(id).orElse(null);
    }

    public Page<Inventory> findAll(Specification<Inventory> spec, Pageable pageable) {
        return inventoryRepository.findAll(spec, pageable);
    }

    @Override
    public Specification<Inventory> getSpecification(String searchText) {
        if (!StringUtils.hasText(searchText)) {
            return null;
        }
        return SpecificationUtils.andWith(searchText, this::buildSearchSpecification);
    }

    protected Specification<Inventory> buildSearchSpecification(String searchText) {
        return (root, query, builder) -> {
            Join<Object, Object> catalog = root.join("catalog", JoinType.LEFT);
            query.orderBy(
                    builder.asc(builder.length(root.get("name"))),
                    builder.asc(builder.length(root.get("specification"))));
            return builder.or(
                    builder.and(
                            builder.isNotNull(catalog),
                            builder.equal(catalog.get("name"), searchText)),
                    builder.like(root.get("code"), "%" + searchText + "%"),
                    builder.like(root.get("specification"), "%" + searchText + "%"),
                    builder.like(root.get("name"), "%" + searchText + "%"));
        };
    }

    public List<Inventory> search(String searchText) {
        Pageable pageable = Pageable.ofSize(getSearchPageSize());
        pageable.getSort().and(Sort.by(Sort.Direction.DESC, "id"));
        Specification<Inventory> specification = getSpecification(searchText);
        return inventoryRepository.findAll(specification, pageable).getContent();
    }

    @Cacheable(key = "'code-'+#p0")
    public Inventory findByCode(String code) {
        return inventoryRepository.findByCode(code).orElse(null);
    }

    @Caching(evict = {
            @CacheEvict(key = "#p0.id"),
            @CacheEvict(key = "'code-'+#p0.code"),
    })
    public Inventory save(Inventory entity) {
        return inventoryRepository.save(entity);
    }

    @Caching(evict = {
            @CacheEvict(key = "#p0.id"),
            @CacheEvict(key = "'code-'+#p0.code"),
    })
    public void delete(Inventory entity) {
        inventoryRepository.delete(entity);
    }

    public long count(Specification<Inventory> spec) {
        return inventoryRepository.count(spec);
    }

    public Inventory createNewInstance() {
        Inventory inventory = new Inventory();

        inventory.setNameLock(false);
        inventory.setSpecificationLock(false);
        inventory.getSalePrice().setTaxRate(13);
        inventory.getPurchasePrice().setTaxRate(13);
        inventory.setWeightUnit("kg");
        inventory.setVolumeUnit("m³");
        inventory.setSizeUnit("mm");

        inventory.setCreateTime(LocalDate.now());
        return inventory;
    }
}
