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

import java.io.File;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import com.ecep.contract.util.CompanyUtils;
import org.hibernate.Hibernate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.ecep.contract.CompanyVendorFileType;
import com.ecep.contract.IEntityService;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.VendorType;
import com.ecep.contract.constant.CompanyVendorConstant;
import com.ecep.contract.ds.company.CompanyFileUtils;
import com.ecep.contract.ds.company.service.CompanyBasicService;
import com.ecep.contract.ds.other.service.SysConfService;
import com.ecep.contract.ds.vendor.repository.CompanyVendorRepository;
import com.ecep.contract.ds.vendor.repository.VendorClassRepository;
import com.ecep.contract.ds.vendor.repository.VendorTypeLocalRepository;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyBasicFile;
import com.ecep.contract.model.CompanyVendor;
import com.ecep.contract.model.CompanyVendorEntity;
import com.ecep.contract.model.CompanyVendorFile;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.VendorCatalog;
import com.ecep.contract.model.VendorTypeLocal;
import com.ecep.contract.util.MyStringUtils;
import com.ecep.contract.util.SpecificationUtils;

import jakarta.persistence.criteria.Path;

@Lazy
@Service
@CacheConfig(cacheNames = CompanyVendorConstant.CACHE_NAME)
public class CompanyVendorService extends CompanyBasicService implements IEntityService<CompanyVendor> {

    private static final Logger logger = LoggerFactory.getLogger(CompanyVendorService.class);

    @Lazy
    @Autowired
    private CompanyVendorRepository companyVendorRepository;
    @Lazy
    @Autowired
    private CompanyVendorFileService companyVendorFileService;
    @Lazy
    @Autowired
    private SysConfService confService;
    @Lazy
    @Autowired
    private CompanyVendorEntityService companyVendorEntityService;
    @Lazy
    @Autowired
    private VendorTypeLocalRepository vendorTypeLocalRepository;
    @Autowired
    private VendorClassRepository vendorClassRepository;

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

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


    public CompanyVendor findByCompany(Company company) {
        return companyVendorRepository.findByCompany(company).orElse(null);
    }

    @Caching(
            evict = {
                    @CacheEvict(key = "#p0.id")
            }
    )
    public CompanyVendor save(CompanyVendor companyVendor) {
        return companyVendorRepository.save(companyVendor);
    }

    @Caching(
            evict = {
                    @CacheEvict(key = "#p0.id")
            }
    )
    @Override
    public void delete(CompanyVendor entity) {
        companyVendorRepository.delete(entity);
    }


    @Override
    public Specification<CompanyVendor> getSpecification(String searchText) {
        if (!StringUtils.hasText(searchText)) {
            return null;
        }
        Specification<CompanyVendor> nameSpec = (root, query, builder) -> {
            Path<Company> company = root.get("company");
            return companyService.buildSearchPredicate(searchText, company, query, builder);
        };
        // 判断是否全是数字
        if (MyStringUtils.isAllDigit(searchText)) {
            // 全数字就按 Id 查询
            try {
                int id = Integer.parseInt(searchText);
                nameSpec = SpecificationUtils.or(nameSpec, (root, query, builder) -> {
                    return builder.equal(root.get("id"), id);
                });
            } catch (Exception ignored) {
            }
        }
        List<CompanyVendorEntity> searched = companyVendorEntityService.search(searchText);
        if (!searched.isEmpty()) {
            nameSpec = SpecificationUtils.or(nameSpec, (root, query, builder) -> {
                return builder.in(root.get("id")).value(searched.stream()
                        .map(CompanyVendorEntity::getVendor)
                        .filter(Objects::nonNull)
                        .map(CompanyVendor::getId)
                        .collect(Collectors.toSet()));
            });
        }
        return nameSpec;
    }

    @Override
    public List<CompanyVendor> search(String searchText) {
        Specification<CompanyVendor> spec = getSpecification(searchText);
        return companyVendorRepository.findAll(spec, Pageable.ofSize(10)).getContent();
    }

    @Override
    public <T, F extends CompanyBasicFile<T>, ID> void deleteFile(F file) {
        companyVendorFileService.delete((CompanyVendorFile) file);
    }

    public File getBasePath() {
        return new File(confService.getString(CompanyVendorConstant.KEY_BASE_PATH));
    }

    /**
     * 重置 供应商文件
     *
     * @param companyVendor 供应商对象
     * @param status        输出
     */
    public boolean reBuildingFiles(CompanyVendor companyVendor, Consumer<String> status) {
        List<CompanyVendorFile> dbFiles = companyVendorFileService.findAllByVendor(companyVendor);
        Map<String, CompanyVendorFile> map = new HashMap<>();

        boolean modified = fetchDbFiles(dbFiles, map, status);

        // 供应商目录下，准备用于移动到公司目录
        List<File> needMoveToCompanyPath = new ArrayList<>();
        // 有修改和新导入的文件
        List<CompanyVendorFile> retrieveFiles = new ArrayList<>();

        //TODO 供应商有曾用名，可能存在多个目录
        fetchFiles(companyVendor.getPath(), needMoveToCompanyPath, retrieveFiles, map, status);

        // 移动文件到公司目录下 to company path
        moveFileToCompany(companyVendor.getCompany(), needMoveToCompanyPath);

        status.accept("导入 " + retrieveFiles.size() + " 个文件");
        if (!retrieveFiles.isEmpty()) {
            //update db
            retrieveFiles.forEach(v -> v.setVendor(companyVendor));
            companyVendorFileService.saveAll(retrieveFiles);
            modified = true;
        }


        return modified;
    }


    @Override
    protected <T, F extends CompanyBasicFile<T>> boolean fillFileAsDefaultType(F dbFile, File file, Consumer<String> status) {
        dbFile.setType((T) CompanyVendorFileType.General);
        fillFile(dbFile, file, null, status);
        companyVendorFileService.save((CompanyVendorFile) dbFile);
        return true;
    }

    @Override
    protected <T, F extends CompanyBasicFile<T>> F fillFileType(File file, List<File> fileList, Consumer<String> status) {
        CompanyVendorFile vendorFile = new CompanyVendorFile();
        vendorFile.setType(CompanyVendorFileType.General);
        vendorFile.setFilePath(file.getAbsolutePath());
        fillFile(vendorFile, file, fileList, status);
        return (F) vendorFile;
    }

    @Override
    protected <T, F extends CompanyBasicFile<T>> boolean setFileTypeAsEvaluationForm(F file) {
        T type = file.getType();
        if (type != CompanyVendorFileType.EvaluationForm) {
            file.setType((T) CompanyVendorFileType.EvaluationForm);
            return true;
        }
        return false;
    }

    @Override
    protected <T, F extends CompanyBasicFile<T>> boolean fillFileAsEvaluationFile(F customerFile, File file, List<File> fileList, Consumer<String> status) {
        boolean modified = super.fillFileAsEvaluationFile(customerFile, file, fileList, status);
        // 当评价表有日期，并且未设审核时
        boolean valid = isArchiveFile(customerFile.getFilePath()) && customerFile.getSignDate() != null;
        if (valid != customerFile.isValid()) {
            customerFile.setValid(valid);
            modified = true;
        }
        return modified;
    }

    protected boolean isEvaluationFile(String fileName) {
        return fileName.contains(CompanyVendorConstant.EVALUATION_FORM_NAME1)
                || fileName.contains(CompanyVendorConstant.EVALUATION_FORM_NAME2)
                || fileName.contains(CompanyVendorConstant.EVALUATION_FORM_NAME3);
    }


    public void verify(CompanyVendor companyVendor, MessageHolder holder) {
        if (companyVendor.getType() == null) {
            holder.warn("数据异常，未设置供应商类型");
            return;
        }
        verify(companyVendor, LocalDate.now(), holder);
    }

    private boolean verifyAsTypicallyVendor(CompanyVendor companyVendor, LocalDate verifyDate, MessageHolder holder) {
        VendorType vendorType = companyVendor.getType();
        if (vendorType != VendorType.TYPICALLY) {
            return false;
        }

        boolean modified = false;
        Company company = companyVendor.getCompany();
        if (!Hibernate.isInitialized(company)) {
            company = companyService.findById(company.getId());
        }
        // 检查营业状态
        String entStatus = company.getEntStatus();
        if (StringUtils.hasText(entStatus)) {
            if (entStatus.contains("注销")) {
                holder.error("营业状态异常：" + entStatus + ", 自动设置为不合格");
                companyVendor.setType(VendorType.UNQUALIFIED);
                companyVendor.setDescription(MyStringUtils.appendIfAbsent(companyVendor.getDescription(), "自动检测到营业状态为" + entStatus + ",设置为不合格供应商."));
                modified = true;
            }
        } else {
            holder.error("营业状态异常：" + entStatus);
        }

        return modified;
    }

    private boolean verifyAsQualifiedVendor(CompanyVendor companyVendor, LocalDate verifyDate, MessageHolder status) {
        VendorType vendorType = companyVendor.getType();
        if (vendorType != VendorType.QUALIFIED) {
            return false;
        }
        // 合格供方，年度评价
        return false;
    }

    /**
     * 合同合规检测
     *
     * @param contract 合同对象
     * @param holder   状态输出对象
     */
    public void verify(Contract contract, MessageHolder holder) {
        Company company = contract.getCompany();
        if (company == null) {
            holder.error("合同未关联公司");
            return;
        }
        CompanyVendor companyVendor = findByCompany(company);
        if (companyVendor == null) {
            holder.error("合同未关联供应商");
            return;
        }
        if (companyVendor.getType() == null) {
            holder.error("供应商未设置类型");
            return;
        }
        if (companyVendor.getType() == VendorType.UNQUALIFIED) {
            holder.error("不合格供应商");
            return;
        }
        verify(companyVendor, contract.getSetupDate(), holder);
    }

    /**
     * 供应商合规检测
     *
     * @param companyVendor 供应商对象
     * @param verifyDate    检测日期
     * @param holder        状态输出对象
     */
    private void verify(CompanyVendor companyVendor, LocalDate verifyDate, MessageHolder holder) {
        boolean modified = false;
        if (verifyAsQualifiedVendor(companyVendor, verifyDate, holder)) {
            modified = true;
        }
        if (verifyAsTypicallyVendor(companyVendor, verifyDate, holder)) {
            modified = true;
        }

        if (!StringUtils.hasText(companyVendor.getPurchase())) {
            holder.error("未设置采购的产品");
        }

        if (!StringUtils.hasText(companyVendor.getPath())) {
            holder.error("未设置文件夹");
        }

        companyVendorFileService.verify(companyVendor, verifyDate, holder);

        if (modified) {
            companyVendorRepository.save(companyVendor);
        }
    }

    public File getEvaluationFormTemplate() {
        String path = confService.getString(CompanyVendorConstant.KEY_EVALUATION_FORM_TEMPLATE);
        if (path == null) {
            return null;
        }
        return new File(path);
    }

    public File getVendorApprovedListTemplate() {
        String path = confService.getString(CompanyVendorConstant.KEY_APPROVED_LIST_TEMPLATE);
        if (path == null) {
            return null;
        }
        return new File(path);
    }


    public boolean makePathAbsent(CompanyVendor companyVendor) {
        String path = companyVendor.getPath();
        if (StringUtils.hasText(path)) {
            File file = new File(path);
            if (file.exists()) {
                return false;
            }
        }

        File dir = makePath(companyVendor);
        if (dir == null) {
            return false;
        }
        if (!dir.exists()) {
            return false;
        }
        companyVendor.setPath(dir.getAbsolutePath());
        return true;
    }

    public File makePath(CompanyVendor companyVendor) {
        File basePath = getBasePath();
        Company company = companyVendor.getCompany();
        if (!Hibernate.isInitialized(company)) {
            company = companyService.findById(company.getId());
        }

        String companyName = company.getName();
        String fileName = CompanyUtils.formatCompanyVendorId(companyVendor.getId()) + "-" + CompanyFileUtils.escapeFileName(companyName);

        File dir = new File(basePath, fileName);
        if (!dir.exists()) {
            if (!dir.mkdir()) {
                return null;
            }
        }
        return dir;
    }

    public void resetTo(Company from, Company to) {
        Optional<CompanyVendor> optional = companyVendorRepository.findByCompany(from);
        optional.ifPresent(companyVendor -> {
            companyVendor.setDescription(MyStringUtils.appendIfAbsent(companyVendor.getDescription(), "转自 " + from.getId()));
            companyVendor.setCompany(to);
            companyVendorRepository.save(companyVendor);
        });
    }


    public void deleteByCompany(Company company) {
        int deleted = companyVendorRepository.deleteAllByCompany(company);
        if (deleted > 0) {
            if (logger.isInfoEnabled()) {
                logger.info("Delete {} records by company:#{}", deleted, company.getId());
            }
        }
    }

    @Cacheable(key = "'catalog-'+#p0")
    public VendorCatalog findCatalogById(Integer id) {
        return vendorClassRepository.findById(id).orElse(null);
    }

    public List<VendorCatalog> findAllCatalogs() {
        return vendorClassRepository.findAll();
    }

    @Cacheable(key = "'catalog-code-'+#p0")
    public VendorCatalog findCatalogByCode(String code) {
        return vendorClassRepository.findByCode(code).orElse(null);
    }

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

    @Cacheable(key = "'types'+#p0")
    public List<VendorTypeLocal> findAllTypes(String lang) {
        Map<VendorType, VendorTypeLocal> map = vendorTypeLocalRepository.getCompleteMapByLocal(lang);
        List<VendorTypeLocal> list = new ArrayList<>(map.values());
        list.sort((o1, o2) -> Objects.compare(o1.getValue(), o2.getValue(), String::compareTo));
        return list;
    }

    @Caching(
            evict = {
                    @CacheEvict(key = "'type-'+#p0.lang"),
            }
    )
    public VendorTypeLocal save(VendorTypeLocal type) {
        return vendorTypeLocalRepository.save(type);
    }

    public long count(Specification<CompanyVendor> spec) {
        return companyVendorRepository.count(spec);
    }

   
}
