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.vo.VendorCatalogVo;
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.IEntityService;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.QueryService;
import com.ecep.contract.VendorFileType;
import com.ecep.contract.VendorType;
import com.ecep.contract.constant.CompanyVendorConstant;
import com.ecep.contract.constant.ServiceConstant;
import com.ecep.contract.ds.company.service.CompanyBasicService;
import com.ecep.contract.ds.company.service.CompanyContactService;
import com.ecep.contract.ds.other.service.SysConfService;
import com.ecep.contract.ds.vendor.repository.VendorClassRepository;
import com.ecep.contract.ds.vendor.repository.VendorRepository;
import com.ecep.contract.ds.vendor.repository.VendorTypeLocalRepository;
import com.ecep.contract.ds.company.model.Company;
import com.ecep.contract.model.CompanyBasicFile;
import com.ecep.contract.ds.contract.model.Contract;
import com.ecep.contract.ds.vendor.model.Vendor;
import com.ecep.contract.model.VendorCatalog;
import com.ecep.contract.ds.vendor.model.VendorEntity;
import com.ecep.contract.ds.vendor.model.VendorFile;
import com.ecep.contract.model.VendorTypeLocal;
import com.ecep.contract.service.VoableService;
import com.ecep.contract.util.CompanyUtils;
import com.ecep.contract.util.FileUtils;
import com.ecep.contract.util.MyStringUtils;
import com.ecep.contract.util.SpecificationUtils;
import com.ecep.contract.vo.VendorVo;
import com.fasterxml.jackson.databind.JsonNode;

import jakarta.persistence.criteria.Path;

@Lazy
@Service
@CacheConfig(cacheNames = CompanyVendorConstant.CACHE_NAME)
public class VendorService extends CompanyBasicService
        implements IEntityService<Vendor>, QueryService<VendorVo>, VoableService<Vendor, VendorVo> {
    @Autowired
    private VendorCatalogService vendorCatalogService;
    @Autowired
    private CompanyContactService companyContactService;

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

    @Lazy
    @Autowired
    private VendorRepository repository;
    @Lazy
    @Autowired
    private VendorFileService vendorFileService;
    @Lazy
    @Autowired
    private SysConfService confService;
    @Lazy
    @Autowired
    private VendorEntityService vendorEntityService;
    @Lazy
    @Autowired
    private VendorTypeLocalRepository vendorTypeLocalRepository;
    @Autowired
    private VendorClassRepository vendorClassRepository;

    @Cacheable(key = "#p0")
    public VendorVo findById(Integer id) {
        return repository.findById(id).map(Vendor::toVo).orElse(null);
    }

    @Override
    public Vendor getById(Integer id) {
        return repository.findById(id).orElse(null);
    }

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

    @Override
    public Page<VendorVo> findAll(JsonNode paramsNode, Pageable pageable) {
        Specification<Vendor> spec = null;
        if (paramsNode.has(ServiceConstant.KEY_SEARCH_TEXT)) {
            spec = getSpecification(paramsNode.get(ServiceConstant.KEY_SEARCH_TEXT).asText());
        }
        // 添加额外的参数过滤
        if (paramsNode.has("type")) {
            VendorType type = VendorType.valueOf(paramsNode.get("type").asText());
            spec = SpecificationUtils.and(spec, (root, query, builder) -> builder.equal(root.get("type"), type));
        }
        spec = SpecificationUtils.andParam(spec, paramsNode, "company", "catalog");
        return findAll(spec, pageable).map(Vendor::toVo);
    }

    public Vendor findByCompany(Company company) {
        return repository.findByCompany(company).orElse(null);
    }

    @Caching(evict = {
            @CacheEvict(key = "#p0.id")
    })
    public Vendor save(Vendor vendor) {
        return repository.save(vendor);
    }

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

    @Override
    public Specification<Vendor> getSpecification(String searchText) {
        if (!StringUtils.hasText(searchText)) {
            return null;
        }
        Specification<Vendor> 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<VendorEntity> searched = vendorEntityService.search(searchText);
        if (!searched.isEmpty()) {
            nameSpec = SpecificationUtils.or(nameSpec, (root, query, builder) -> {
                return builder.in(root.get("id")).value(searched.stream()
                        .map(VendorEntity::getVendor)
                        .filter(Objects::nonNull)
                        .map(Vendor::getId)
                        .collect(Collectors.toSet()));
            });
        }
        return nameSpec;
    }

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

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

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

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

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

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

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

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

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

        return modified;
    }

    @SuppressWarnings("unchecked")
    @Override
    protected <T, F extends CompanyBasicFile<T>> boolean fillFileAsDefaultType(F dbFile, File file,
            Consumer<String> status) {
        dbFile.setType((T) VendorFileType.General);
        fillFile(dbFile, file, null, status);
        vendorFileService.save((VendorFile) dbFile);
        return true;
    }

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

    @SuppressWarnings("unchecked")
    @Override
    protected <T, F extends CompanyBasicFile<T>> boolean setFileTypeAsEvaluationForm(F file) {
        T type = file.getType();
        if (type != VendorFileType.EvaluationForm) {
            file.setType((T) VendorFileType.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(Vendor vendor, MessageHolder holder) {
        if (vendor.getType() == null) {
            holder.warn("数据异常，未设置供应商类型");
            return;
        }
        verify(vendor, LocalDate.now(), holder);
    }

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

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

        return modified;
    }

    private boolean verifyAsQualifiedVendor(Vendor vendor, LocalDate verifyDate, MessageHolder status) {
        VendorType vendorType = vendor.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;
        }
        Vendor vendor = findByCompany(company);
        if (vendor == null) {
            holder.error("合同未关联供应商");
            return;
        }
        if (vendor.getType() == null) {
            holder.error("供应商未设置类型");
            return;
        }
        if (vendor.getType() == VendorType.UNQUALIFIED) {
            holder.error("不合格供应商");
            return;
        }
        verify(vendor, contract.getSetupDate(), holder);
    }

    /**
     * 供应商合规检测
     *
     * @param vendor     供应商对象
     * @param verifyDate 检测日期
     * @param holder     状态输出对象
     */
    public boolean verify(Vendor vendor, LocalDate verifyDate, MessageHolder holder) {
        boolean modified = false;

        if (verifyAsQualifiedVendor(vendor, verifyDate, holder)) {
            modified = true;
        }
        if (verifyAsTypicallyVendor(vendor, verifyDate, holder)) {
            modified = true;
        }

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

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

        vendorFileService.verify(vendor, verifyDate, holder);

        if (modified) {
            repository.save(vendor);
        }
        return modified;
    }

    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(Vendor vendor) {
        String path = vendor.getPath();
        if (StringUtils.hasText(path)) {
            File file = new File(path);
            if (file.exists()) {
                return false;
            }
        }

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

    public File makePath(Vendor vendor) {
        File basePath = getBasePath();
        Company company = vendor.getCompany();
        if (!Hibernate.isInitialized(company)) {
            company = companyService.getById(company.getId());
        }

        String companyName = company.getName();
        String fileName = CompanyUtils.formatCompanyVendorId(vendor.getId()) + "-"
                + FileUtils.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<Vendor> optional = repository.findByCompany(from);
        optional.ifPresent(companyVendor -> {
            companyVendor
                    .setDescription(MyStringUtils.appendIfAbsent(companyVendor.getDescription(), "转自 " + from.getId()));
            companyVendor.setCompany(to);
            repository.save(companyVendor);
        });
    }

    public void deleteByCompany(Company company) {
        int deleted = repository.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 VendorCatalogVo findCatalogByCode(String code) {
        return vendorClassRepository.findByCode(code).map(VendorCatalog::toVo).orElse(null);
    }

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

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

    public long count(Specification<Vendor> spec) {
        return repository.count(spec);
    }

    @Override
    public void updateByVo(Vendor vendor, VendorVo vo) {
        if (vendor == null || vo == null) {
            return;
        }

        vendor.setType(vo.getType());
        vendor.setProtocolProvider(vo.isProtocolProvider());
        vendor.setDevelopDate(vo.getDevelopDate());
        vendor.setPath(vo.getPath());
        vendor.setPurchase(vo.getPurchase());
        vendor.setDescription(vo.getDescription());

        // 处理关联实体 - 供应商分类
        if (vo.getCatalogId() == null) {
            vendor.setCatalog(null);
        } else {
            if (vendor.getCatalog() == null || !vendor.getCatalog().getId().equals(vo.getCatalogId())) {
                vendor.setCatalog(vendorCatalogService.getById(vo.getCatalogId()));
            }
        }

        // 处理关联实体 - 联系人
        if (vo.getContactId() == null) {
            vendor.setContact(null);
        } else {
            if (vendor.getContact() == null || !vendor.getContact().getId().equals(vo.getContactId())) {
                vendor.setContact(companyContactService.getById(vo.getContactId()));
            }
        }
    }

}
