package cn.ey.biz.module.system.service.codeblock;

import cn.ey.biz.common.pojo.PageResult;
import cn.ey.biz.module.system.api.codeblock.dto.*;
import cn.ey.biz.module.system.controller.admin.codeblock.vo.*;
import cn.ey.biz.module.system.convert.codeblock.CodeBlockLegalEntityConvert;
import cn.ey.biz.module.system.dal.dataobject.codeblock.CodeBlockDO;
import cn.ey.biz.module.system.dal.dataobject.codeblock.CodeBlockLegalEntityDO;
import cn.ey.biz.module.system.dal.dataobject.engagement.code.SystemEngagementCodeRuleDO;
import cn.ey.biz.module.system.dal.mapper.codeblock.CodeBlockLegalEntityMapper;
import cn.ey.biz.module.system.dal.mapper.codeblock.CodeBlockMapper;
import cn.ey.biz.module.system.dal.mapper.codeblock.GdsRateCodeBlockMapper;
import cn.ey.biz.module.system.dal.mapper.engagement.code.SystemEngagementCodeRuleMapper;
import cn.ey.biz.module.system.dal.mapper.rate.DictGdsRateValueMapper;
import cn.ey.biz.module.system.dal.redis.codeblock.CodeBlockRedisDAO;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import static cn.ey.biz.common.exception.util.ServiceExceptionUtil.exception;
import static cn.ey.biz.module.system.enums.ErrorCodeConstants.*;

@Service
public class CodeBlockLegalEntityServiceImpl implements CodeBlockLegalEntityService {

    @Resource
    private CodeBlockLegalEntityMapper codeBlockLegalEntityMapper;

    @Resource
    private CodeBlockMapper codeBlockMapper;

    @Resource
    private GdsRateCodeBlockMapper gdsRateCodeBlockMapper;

    @Resource
    private SystemEngagementCodeRuleMapper codeRuleMapper;

    @Resource
    private CodeBlockRedisDAO codeBlockRedisDAO;

    @Resource
    private DictGdsRateValueMapper dictGdsRateValueMapper;


    @Override
    public List<Long> createLegalEntity(CodeBlockLegalEntityCreateReqVO reqVO) {
        if (StringUtils.isBlank(reqVO.getSlCode())) {
            reqVO.setSlCode("-");
        }
        if (StringUtils.isBlank(reqVO.getSslCode())) {
            reqVO.setSslCode("-");
        }
        List<CodeBlockLegalEntityParamReqVO> list = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(reqVO.getSmuCode())) {
            for (String smuCode : reqVO.getSmuCode()) {
                if (smuCode.length() > 16) {
                    throw exception(CODE_BLOCK_LEGAL_ENTITY_SMU_CODE);
                }
                CodeBlockLegalEntityParamReqVO paramReqVO = CodeBlockLegalEntityParamReqVO.builder().legalEntityCode(reqVO.getLegalEntityCode()).sslCode(reqVO.getSslCode()).smuCode(smuCode).slCode(reqVO.getSlCode()).deleted(false).build();
                list.add(paramReqVO);
            }

        } else {
            CodeBlockLegalEntityParamReqVO paramReqVO = CodeBlockLegalEntityParamReqVO.builder().legalEntityCode(reqVO.getLegalEntityCode()).sslCode(reqVO.getSslCode()).slCode(reqVO.getSlCode()).smuCode("-").build();
            list.add(paramReqVO);
        }
        List<CodeBlockLegalEntityDO> codeBlockLegalEntityDOS = CodeBlockLegalEntityConvert.INSTANCE.paramToDo(list);
        for (CodeBlockLegalEntityDO entityDO : codeBlockLegalEntityDOS) {
            CodeBlockLegalEntityDO entityDOExist = codeBlockLegalEntityMapper.selectExistByParam(entityDO.getLegalEntityCode(), entityDO.getSlCode(), entityDO.getSslCode(), entityDO.getSmuCode());
            if (null != entityDOExist) {
                throw exception(CODE_BLOCK_LEGAL_ENTITY_EXISTS);
            }
        }

        codeBlockLegalEntityMapper.insertBatch(codeBlockLegalEntityDOS);
        codeBlockRedisDAO.delete();
        return codeBlockLegalEntityDOS.stream().map(CodeBlockLegalEntityDO::getId).collect(Collectors.toList());
    }

    @Override
    public void deleteLegalEntity(List<Long> ids) {
        for (Long id : ids) {
            checkExist(id);
        }
        codeBlockLegalEntityMapper.deleteBatchByIds(ids);
        codeBlockRedisDAO.delete();
    }

    @Override
    public CodeBlockLegalEntityDetailRespVO getLegalEntity(Long id) {
        checkExist(id);
        CodeBlockLegalEntityDO entityDO = codeBlockLegalEntityMapper.selectById(id);
        return CodeBlockLegalEntityConvert.INSTANCE.doToVo(entityDO);

    }

    private void checkExist(Long id) {
        CodeBlockLegalEntityDO codeBlockLegalEntityDO = codeBlockLegalEntityMapper.selectById(id);
        if (null == codeBlockLegalEntityDO) {
            throw exception(CODE_BLOCK_LEGAL_ENTITY_NOT_EXISTS);
        }
    }

    @Override
    public PageResult<CodeBlockLegalEntityDO> getDictDataPage(CodeBlockLegalEntityPageReqVO reqVO) {
        return codeBlockLegalEntityMapper.selectPage(reqVO);
    }

    @Override
    public List<String> pullDown() {
        return codeBlockMapper.pullDownLegalEntityCode();
    }

    @Override
    public List<CodeBlockCodeValueVO> pullDownSl(String legalEntityCode) {
        return codeBlockMapper.pullDownSl(legalEntityCode);
    }

    @Override
    public List<CodeBlockCodeValueVO> pullDownSlList(List<String> legalEntityCode) {
        return codeBlockMapper.pullDownSlList(legalEntityCode);
    }

    @Override
    public List<CodeBlockCodeValueVO> pullDownSmu(String geoRegionCode, String slCode, String sslCode) {
        return codeBlockMapper.pullDownSmu(geoRegionCode, slCode, sslCode);
    }

    @Override
    public List<CodeBlockCodeValueVO> pullDownSmuList(List<String> legalEntityCode, List<String> slCode, List<String> sslCode) {
        return codeBlockMapper.pullDownSmuList(legalEntityCode, slCode, sslCode);
    }

    @Override
    public List<CodeBlockCodeValueVO> pullDownSslCodeList(List<String> legalEntityCode, List<String> slCode) {
        return codeBlockMapper.pullDownSslCodeList(legalEntityCode, slCode);
    }

    private List<CodeBlockDO> getCodeBlockLegalEntityDOS(CodeBlockWideTableReqDTO vo, List<CodeBlockDO> codeBlockLegalEntityDOS) {
        if (CollectionUtil.isNotEmpty(codeBlockLegalEntityDOS)) {
            codeBlockLegalEntityDOS = codeBlockLegalEntityDOS.stream().filter(c ->
                            (StringUtils.isBlank(vo.getCountryRegionCode()) || vo.getCountryRegionCode().equals(c.getCountryRegionCode()))
                                    && (StringUtils.isBlank(vo.getAreaCode()) || vo.getAreaCode().equals(c.getAreaCode()))
                                    && (StringUtils.isBlank(vo.getGeoRegionCode()) || vo.getGeoRegionCode().equals(c.getGeoRegionCode()))
                                    && (StringUtils.isBlank(vo.getLegalEntityCode()) || vo.getLegalEntityCode().equals(c.getLegalEntityCode()))
                                    && (StringUtils.isBlank(vo.getBuCode()) || vo.getBuCode().equals(c.getBuCode()))
                                    && (StringUtils.isBlank(vo.getOuCode()) || vo.getOuCode().equals(c.getOuCode()))
                                    && (StringUtils.isBlank(vo.getMuCode()) || vo.getMuCode().equals(c.getMuCode()))
                                    && (StringUtils.isBlank(vo.getSmuCode()) || vo.getSmuCode().equals(c.getSmuCode()))
                                    && (StringUtils.isBlank(vo.getSlCode()) || vo.getSlCode().equals(c.getSlCode()))
                                    && (StringUtils.isBlank(vo.getSslCode()) || vo.getSslCode().equals(c.getSslCode()))
                                    && (StringUtils.isBlank(vo.getCompetencyCode()) || vo.getCompetencyCode().equals(c.getCompetencyCode()))
                                    && (StringUtils.isBlank(vo.getMarketSegmentCode()) || vo.getMarketSegmentCode().equals(c.getMarketSegmentCode()))
                                    && (StringUtils.isBlank(vo.getMrCode()) || vo.getMrCode().equals(c.getMrCode()))
                                    && (StringUtils.isBlank(vo.getCityCode()) || vo.getCityCode().equals(c.getCityCode()))
                                    && (StringUtils.isBlank(vo.getMercuryCompetencyCode()) || vo.getMercuryCompetencyCode().equals(c.getMercuryCompetencyCode()))
                                    && (StringUtils.isBlank(vo.getCategoryCode()) || vo.getCategoryCode().equals(c.getCategoryCode())))
                    .collect(Collectors.toList());
        }
        return codeBlockLegalEntityDOS;
    }

    @Override
    public CodeBlockWideTableDTO filterList(CodeBlockWideTableReqDTO vo) {
        List<CodeBlockDO> codeBlockLegalEntityDOS = getCodeBlockDOList();
        if (null != vo) {
            codeBlockLegalEntityDOS = getCodeBlockLegalEntityDOS(vo, codeBlockLegalEntityDOS);
        }
        CodeBlockWideTableDTO codeBlockWideTableDTO = null;
        if (CollectionUtil.isNotEmpty(codeBlockLegalEntityDOS)) {
            codeBlockWideTableDTO = this.fillData(codeBlockLegalEntityDOS);
        }
        return codeBlockWideTableDTO;
    }

    @Override
    public CodeBlockWideTableDTO financeFilterList(CodeBlockWideTableReqDTO vo) {
        List<CodeBlockDO> codeBlockDOS = codeBlockMapper.financeFilterList();
        if (CollectionUtil.isNotEmpty(codeBlockDOS)) {
            codeBlockDOS = getCodeBlockLegalEntityDOS(vo, codeBlockDOS);
        }
        CodeBlockWideTableDTO codeBlockWideTableDTO = null;
        if (CollectionUtil.isNotEmpty(codeBlockDOS)) {
            codeBlockWideTableDTO = this.fillData(codeBlockDOS);
        }
        return codeBlockWideTableDTO;
    }

    @Override
    public CodeBlockWideTableDTO getCodeBlockList(CodeBlockWideTableReqDTO vo) {
        List<CodeBlockDO> codeBlockDOS = codeBlockMapper.getCodeBlockList();
        if (CollectionUtil.isNotEmpty(codeBlockDOS)) {
            codeBlockDOS = getCodeBlockLegalEntityDOS(vo, codeBlockDOS);
        }
        CodeBlockWideTableDTO codeBlockWideTableDTO = null;
        if (CollectionUtil.isNotEmpty(codeBlockDOS)) {
            codeBlockWideTableDTO = this.fillData(codeBlockDOS);
        }
        return codeBlockWideTableDTO;
    }

    @Override
    public CodeBlockWideTableDTO financeFilterFirmList(CodeBlockWideTableReqDTO vo) {
        List<CodeBlockDO> codeBlockDOS = codeBlockMapper.financeFilterFirmList();
        if (CollectionUtil.isNotEmpty(codeBlockDOS)) {
            codeBlockDOS = getCodeBlockLegalEntityDOS(vo, codeBlockDOS);
        }
        CodeBlockWideTableDTO codeBlockWideTableDTO = null;
        if (CollectionUtil.isNotEmpty(codeBlockDOS)) {
            codeBlockWideTableDTO = this.fillData(codeBlockDOS);
        }
        return codeBlockWideTableDTO;
    }

    private CodeBlockWideTableDTO fillData(List<CodeBlockDO> codeBlockLegalEntityDOS) {
        CodeBlockWideTableDTO codeBlockWideTableDTO = new CodeBlockWideTableDTO();
        if (CollectionUtil.isNotEmpty(codeBlockLegalEntityDOS)) {
            List<CodeBlockCodeValueDTO> legalEntityCode = codeBlockLegalEntityDOS.stream().filter(c -> StringUtils.isNotBlank(c.getLegalEntityCode()))
                    .map(c -> new CodeBlockCodeValueDTO(c.getLegalEntityCode(), c.getLegalEntityName()))
                    .distinct().sorted(Comparator.comparing(CodeBlockCodeValueDTO::getCode))
                    .collect(Collectors.toList());
            List<CodeBlockCodeValueDTO> countryRegionCode = codeBlockLegalEntityDOS.stream().filter(c -> StringUtils.isNotBlank(c.getCountryRegionCode()))
                    .map(c -> new CodeBlockCodeValueDTO(c.getCountryRegionCode(), c.getCountryRegionName()))
                    .distinct().sorted(Comparator.comparing(CodeBlockCodeValueDTO::getCode))
                    .collect(Collectors.toList());
            List<CodeBlockCodeValueDTO> geoRegionCode = codeBlockLegalEntityDOS.stream().filter(c -> StringUtils.isNotBlank(c.getGeoRegionCode()))
                    .map(c -> new CodeBlockCodeValueDTO(c.getGeoRegionCode(), c.getGeoRegionName()))
                    .distinct().sorted(Comparator.comparing(CodeBlockCodeValueDTO::getCode))
                    .collect(Collectors.toList());
            List<CodeBlockCodeValueDTO> buCode = codeBlockLegalEntityDOS.stream().filter(c -> StringUtils.isNotBlank(c.getBuCode()))
                    .map(c -> new CodeBlockCodeValueDTO(c.getBuCode(), c.getBuName()))
                    .distinct().sorted(Comparator.comparing(CodeBlockCodeValueDTO::getCode))
                    .collect(Collectors.toList());
            List<CodeBlockCodeValueDTO> ouCode = codeBlockLegalEntityDOS.stream().filter(c -> StringUtils.isNotBlank(c.getOuCode()))
                    .map(c -> new CodeBlockCodeValueDTO(c.getOuCode(), c.getOuName()))
                    .distinct().sorted(Comparator.comparing(CodeBlockCodeValueDTO::getCode))
                    .collect(Collectors.toList());

            List<CodeBlockCodeValueDTO> muCode = codeBlockLegalEntityDOS.stream().filter(c -> StringUtils.isNotBlank(c.getMuCode()))
                    .map(c -> new CodeBlockCodeValueDTO(c.getMuCode(), c.getMuName()))
                    .distinct().sorted(Comparator.comparing(CodeBlockCodeValueDTO::getCode))
                    .collect(Collectors.toList());
            List<CodeBlockCodeValueDTO> smuCode = codeBlockLegalEntityDOS.stream().filter(c -> StringUtils.isNotBlank(c.getSmuCode()))
                    .map(c -> new CodeBlockCodeValueDTO(c.getSmuCode(), c.getSmuName()))
                    .distinct().sorted(Comparator.comparing(CodeBlockCodeValueDTO::getCode))
                    .collect(Collectors.toList());
            List<CodeBlockCodeValueDTO> slCode = codeBlockLegalEntityDOS.stream().filter(c -> StringUtils.isNotBlank(c.getSlCode()))
                    .map(c -> new CodeBlockCodeValueDTO(c.getSlCode(), c.getSlName()))
                    .distinct().sorted(Comparator.comparing(CodeBlockCodeValueDTO::getCode))
                    .collect(Collectors.toList());
            List<CodeBlockCodeValueDTO> sslCode = codeBlockLegalEntityDOS.stream().filter(c -> StringUtils.isNotBlank(c.getSslCode()))
                    .map(c -> new CodeBlockCodeValueDTO(c.getSslCode(), c.getSslName()))
                    .distinct().sorted(Comparator.comparing(CodeBlockCodeValueDTO::getCode))
                    .collect(Collectors.toList());
            List<CodeBlockCodeValueDTO> competencyCode = codeBlockLegalEntityDOS.stream().filter(c -> StringUtils.isNotBlank(c.getCompetencyCode()))
                    .map(c -> new CodeBlockCodeValueDTO(c.getCompetencyCode(), c.getCompetencyName()))
                    .distinct().sorted(Comparator.comparing(CodeBlockCodeValueDTO::getCode))
                    .collect(Collectors.toList());
            List<CodeBlockCodeValueDTO> mrCode = codeBlockLegalEntityDOS.stream().filter(c -> StringUtils.isNotBlank(c.getMrCode()))
                    .map(c -> new CodeBlockCodeValueDTO(c.getMrCode(), c.getMrName()))
                    .distinct().sorted(Comparator.comparing(CodeBlockCodeValueDTO::getCode))
                    .collect(Collectors.toList());
            List<CodeBlockCodeValueDTO> areaCode = codeBlockLegalEntityDOS.stream().filter(c -> StringUtils.isNotBlank(c.getAreaCode()))
                    .map(c -> new CodeBlockCodeValueDTO(c.getAreaCode(), c.getAreaName()))
                    .distinct().sorted(Comparator.comparing(CodeBlockCodeValueDTO::getCode))
                    .collect(Collectors.toList());

            codeBlockWideTableDTO.setLegalEntity(legalEntityCode);
            codeBlockWideTableDTO.setCountryRegionCode(countryRegionCode);
            codeBlockWideTableDTO.setGeoRegionCode(geoRegionCode);
            codeBlockWideTableDTO.setBuCode(buCode);
            codeBlockWideTableDTO.setOuCode(ouCode);
            codeBlockWideTableDTO.setMuCode(muCode);
            codeBlockWideTableDTO.setSmuCode(smuCode);
            codeBlockWideTableDTO.setSlCode(slCode);
            codeBlockWideTableDTO.setSslCode(sslCode);
            codeBlockWideTableDTO.setCompetencyCode(competencyCode);
            codeBlockWideTableDTO.setMrCode(mrCode);
            codeBlockWideTableDTO.setAreaCode(areaCode);
        }

        return codeBlockWideTableDTO;
    }

    private List<CodeBlockDO> getCodeBlockDOList() {
        List<CodeBlockDO> codeBlockData = codeBlockRedisDAO.getCodeBlockData();
        if (CollectionUtil.isNotEmpty(codeBlockData)) {
            return codeBlockData;
        }
        QueryWrapper<CodeBlockLegalEntityDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("deleted", false);
        List<CodeBlockLegalEntityDO> blockLegalEntityDOS = codeBlockLegalEntityMapper.selectList(queryWrapper);
        List<CodeBlockDO> codeBlockLegalEntityDOS = null;
        //查原始codeBlock宽表数据（根据法律实体）
        if (CollectionUtil.isNotEmpty(blockLegalEntityDOS)) {
            List<CodeBlockVO> blockVOS = CodeBlockLegalEntityConvert.INSTANCE.doConvertVo(blockLegalEntityDOS);
            List<CodeBlockVO> collect = blockVOS.stream().peek(e -> {
                e.setSmuCode(e.getSmuCode().equals("-") ? null : e.getSmuCode());
                e.setLegalEntityCode(e.getLegalEntityCode().equals("-") ? null : e.getLegalEntityCode());
                e.setSlCode(e.getSlCode().equals("-") ? null : e.getSlCode());
                e.setSslCode(e.getSslCode() == null || e.getSslCode().equals("-") ? null : e.getSslCode());
            }).collect(Collectors.toList());
            codeBlockLegalEntityDOS = codeBlockMapper.filterList(collect);
        }
        //排除受限制的codeBlock
        this.exclusionList(codeBlockLegalEntityDOS);
        codeBlockRedisDAO.set(codeBlockLegalEntityDOS);
        return codeBlockLegalEntityDOS;
    }


    private void exclusionList(List<CodeBlockDO> codeBlockLegalEntityDOS) {
        if (CollectionUtil.isNotEmpty(codeBlockLegalEntityDOS)) {
            QueryWrapper<SystemEngagementCodeRuleDO> ruleDOQueryWrapper = new QueryWrapper<>();
            ruleDOQueryWrapper.eq("deleted", false);
            List<SystemEngagementCodeRuleDO> codeRuleDOS = codeRuleMapper.selectList(ruleDOQueryWrapper);
            if (CollectionUtil.isNotEmpty(codeRuleDOS)) {
                //排除code规则数据
                for (SystemEngagementCodeRuleDO c : codeRuleDOS) {
                    List<CodeBlockDO> removeCodeBlockList = codeBlockLegalEntityDOS.stream()
                            .filter(codeBlock -> (c.getEntityCode().equals("-") || c.getEntityCode().equals(codeBlock.getLegalEntityCode()))
                                    && (c.getBuCode().equals("-") || c.getBuCode().equals(codeBlock.getBuCode()))
                                    && (c.getOuCode().equals("-") || c.getOuCode().equals(codeBlock.getOuCode()))
                                    && (c.getMuCode().equals("-") || c.getMuCode().equals(codeBlock.getMuCode()))
                                    && (c.getSmuCode().equals("-") || c.getSmuCode().equals(codeBlock.getSmuCode()))
                                    && (c.getSlCode() == null || c.getSlCode().equals("-") || c.getSlCode().equals(codeBlock.getSlCode()))
                                    && (c.getSslCode() == null || c.getSslCode().equals("-") || c.getSslCode().equals(codeBlock.getSslCode())))
                            .collect(Collectors.toList());
                    if (CollectionUtil.isNotEmpty(removeCodeBlockList)) {
                        codeBlockLegalEntityDOS.removeAll(removeCodeBlockList);
                    }
                }

            }
        }
    }

    @Override
    public CodeBlockLegalRespDTO getLegalEffective(CodeBlockReqDTO dto) {
        List<CodeBlockDO> codeBlockLegalEntityDOS = getCodeBlockDOList();
        if (CollectionUtil.isEmpty(codeBlockLegalEntityDOS)) {
            throw exception(CODE_BLOCK_LEGAL_ENTITY);
        }
        List<CodeBlockDO> collect = codeBlockLegalEntityDOS.stream().filter(c -> c.getBuCode().equals(dto.getBuCode())
                && c.getOuCode().equals(dto.getOuCode()) && c.getMuCode().equals(dto.getMuCode())
                && c.getSmuCode().equals(dto.getSmuCode())).collect(Collectors.toList());

        CodeBlockLegalRespDTO codeBlockLegalRespDTO = new CodeBlockLegalRespDTO();
        if (CollectionUtil.isNotEmpty(collect)) {
            codeBlockLegalRespDTO.setCodeBlockCode(dto.getBuCode() + dto.getOuCode() + dto.getMuCode() + dto.getSmuCode());
            codeBlockLegalRespDTO.setIsLegal(true);
            return codeBlockLegalRespDTO;
        }
        codeBlockLegalRespDTO.setCodeBlockCode(null);
        codeBlockLegalRespDTO.setIsLegal(false);
        return codeBlockLegalRespDTO;
    }

    @Override
    public CodeBlockInfoRespDTO getCodeBlockInfo(String codeBlock) {
        CodeBlockDO codeBlockDO = codeBlockMapper.selectOne(codeBlock);
        return CodeBlockLegalEntityConvert.INSTANCE.doConvertDTO(codeBlockDO);
    }
}
