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

import java.io.File;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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.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.ds.other.service.SysConfService;
import com.ecep.contract.ds.project.repository.ProjectRepository;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.DeliverySignMethod;
import com.ecep.contract.model.ProductType;
import com.ecep.contract.model.ProductUsage;
import com.ecep.contract.model.Project;
import com.ecep.contract.model.ProjectIndustry;
import com.ecep.contract.model.ProjectSaleType;
import com.ecep.contract.model.ProjectType;
import com.ecep.contract.util.SpecificationUtils;

/**
 * 项目服务
 */
@Lazy
@Service
@CacheConfig(cacheNames = "project")
public class ProjectService implements IEntityService<Project> {
    private static final Logger logger = LoggerFactory.getLogger(ProjectService.class);

    public static final String KEY_BASE_PATH = "project.base.path";

    @Lazy
    @Autowired
    private ProjectRepository projectRepository;
    @Lazy
    @Autowired
    private ProjectFileService projectFileService;
    @Lazy
    @Autowired
    private DeliverySignMethodService deliverySignMethodService;
    @Lazy
    @Autowired
    private ProductTypeService productTypeService;
    @Lazy
    @Autowired
    private ProductUsageService productUsageService;
    @Lazy
    @Autowired
    private ProjectIndustryService projectIndustryService;
    @Lazy
    @Autowired
    private SaleTypeService saleTypeService;
    @Lazy
    @Autowired
    private ProjectTypeService projectTypeService;
    @Lazy
    @Autowired
    private SysConfService confService;

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

    public Project findBySaleTypeAndCodeYearAndCodeSequenceNumber(ProjectSaleType type, int year, int sequenceNumber) {
        return projectRepository.findBySaleTypeAndCodeYearAndCodeSequenceNumber(type, year, sequenceNumber)
                .orElse(null);
    }

    @CacheEvict(key = "#p0.id")
    public Project save(Project project) {
        return projectRepository.save(project);
    }

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

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

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

    private File makePath(Project project) {
        File basePath = getBasePath();
        String fileName = project.getName();

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

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

    @CacheEvict(key = "#p0.id")
    public void delete(Project project) {
        projectRepository.delete(project);
    }

    public int getNextCodeSequenceNumber(ProjectSaleType type, int year) {
        Optional<Project> optional = projectRepository.findFirstBySaleTypeAndCodeYearOrderByCodeSequenceNumberDesc(type,
                year);
        if (optional.isEmpty()) {
            return 1;
        }
        Project project = optional.get();
        return project.getCodeSequenceNumber() + 1;
    }

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

    protected Specification<Project> buildSearchSpecification(String searchText) {
        return (root, query, builder) -> {
            return builder.or(
                    builder.like(root.get("name"), "%" + searchText + "%"),
                    builder.like(root.get("code"), "%" + searchText + "%"),
                    builder.like(root.get("description"), "%" + searchText + "%"));
        };
    }

    public List<Project> search(String userText) {
        Specification<Project> spec = getSpecification(userText);
        return projectRepository.findAll(spec, Pageable.ofSize(10)).getContent();
    }

    public Project findByName(String name) {
        Optional<Project> optional = projectRepository.findByName(name);
        return optional.orElse(null);
    }

    /**
     * 根据合同编号查找项目
     *
     * @param code 合同编号
     * @return 项目对象
     */
    public Project findByCode(String code) {
        Optional<Project> optional = projectRepository.findByCode(code);
        if (optional.isPresent()) {
            return optional.get();
        }

        if (code.length() >= 6) {
            try {
                ProjectSaleType saleType = saleTypeService.findByCode(code.substring(0, 1));
                if (saleType == null) {
                    return null;
                }
                int codeYear = Integer.parseInt(code.substring(1, 3));
                int sequenceNumber = Integer.parseInt(code.substring(3, 6));
                optional = projectRepository.findBySaleTypeAndCodeYearAndCodeSequenceNumber(saleType, codeYear,
                        sequenceNumber);
                if (optional.isPresent()) {
                    return optional.get();
                }
            } catch (NumberFormatException e) {
                throw new RuntimeException("code:" + code, e);
            }
        }
        return null;
    }

    public void applyCode(Project project, String code) {
        project.setCode(code);

        String saleTypeCode = null;
        String seqCode = null;
        String extCode = null;
        // 第二个字符是数字时,快速处理
        if (Character.isDigit(code.charAt(1))) {
            // 只有一个字母表示合同类型
            saleTypeCode = String.valueOf(code.charAt(0));
            seqCode = code.substring(1, 6);
            extCode = code.substring(6);
        } else {
            // 使用正则找出 A-Z 1~4字母引导 合同类型
            String regex = "^([A-Z]{1,4})(\\d{4,5})([A-Z]{0,5})";
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(code);
            if (matcher.find()) {
                saleTypeCode = matcher.group(1);
                seqCode = matcher.group(2);
                extCode = matcher.group(3);
            }
        }

        ProjectSaleType saleType = saleTypeService.findByCode(saleTypeCode);
        if (saleType != null) {
            project.setSaleType(saleType);
        }

        if (seqCode != null) {
            project.setCodeYear(Integer.parseInt(seqCode.substring(0, 2)));
            project.setCodeSequenceNumber(Integer.parseInt(seqCode.substring(2)));
        }

        if (extCode != null) {
            // 4个后缀编码
            if (extCode.length() > 3) {
                DeliverySignMethod signMethod = deliverySignMethodService.findBySaleTypeAndCode(saleType,
                        String.valueOf(extCode.charAt(0)));
                if (signMethod != null) {
                    project.setDeliverySignMethod(signMethod);
                }

                ProductType productType = productTypeService.findByCode(extCode.substring(1, 2));
                if (productType != null) {
                    project.setProductType(productType);
                }
                ProjectType projectType = projectTypeService.findByCode(extCode.substring(2, 3));
                if (projectType != null) {
                    project.setProjectType(projectType);
                }

                ProjectIndustry industry = projectIndustryService.findByCode(extCode.substring(3, 4));
                if (industry != null) {
                    project.setIndustry(industry);
                }
            }
            // 扩展至 5个后缀编码
            if (extCode.length() > 4) {
                ProductUsage productUsage = productUsageService.findByCode(extCode.substring(4, 5));
                if (productUsage != null) {
                    project.setProductUsage(productUsage);
                }
            }
        }
    }

    public Project newInstanceByContract(Contract contract) {
        Project project = new Project();
        project.setName(contract.getName());
        applyCode(project, contract.getCode());
        contract.setCreated(LocalDateTime.now());
        LocalDateTime created = contract.getCreated();
        if (created != null) {
            project.setCreated(created.toLocalDate());
        } else {
            project.setCreated(LocalDate.now());
        }
        project.setCustomer(contract.getCompany());
        project.setApplicant(contract.getEmployee());
        return project;
    }

    public String makeProjectCode(Project project) {
        StringBuilder sb = new StringBuilder();
        sb.append(project.getSaleType().getCode());
        sb.append(project.getCodeYear());
        if (project.getCodeSequenceNumber() < 10) {
            sb.append("0");
        }
        if (project.getCodeSequenceNumber() < 100) {
            sb.append("0");
        }
        sb.append(project.getCodeSequenceNumber());
        return sb.toString();
    }

    public File searchPath(Project project) {
        ProjectSaleType saleType = project.getSaleType();
        if (saleType == null) {
            return null;
        }
        if (!Hibernate.isInitialized(saleType)) {
            saleType = saleTypeService.findById(saleType.getId());
            project.setSaleType(saleType);
        }
        if (!StringUtils.hasText(saleType.getPath())) {
            return null;
        }
        File path = new File(saleType.getPath());
        if (saleType.isStoreByYear()) {
            path = new File(path, "20" + project.getCodeYear());
            if (!path.exists()) {
                path.mkdir();// 创建目录
            }
        }

        String projectCode = makeProjectCode(project);
        // 检索目录
        File[] files = path.listFiles(f -> f.isDirectory() && f.getName().contains(projectCode));
        if (files != null && files.length > 0) {
            path = files[0];
        }
        return path;
    }
}
