package com.ruoyi.ibs.grid.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.ibs.customerselect.service.IMyCustomerService;
import com.ruoyi.ibs.draw.domain.dto.grid.CustCountDTO;
import com.ruoyi.ibs.grid.domain.entity.*;
import com.ruoyi.ibs.grid.domain.vo.AddressTreeVO;
import com.ruoyi.ibs.grid.domain.vo.GridUserVO;
import com.ruoyi.ibs.grid.domain.vo.TopGridSelectVO;
import com.ruoyi.ibs.grid.mapper.*;
import com.ruoyi.ibs.grid.service.RegionGridUtilsService;
import com.ruoyi.system.mapper.SysDeptMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author 吴凯程
 * @Date 2024/11/13
 **/
@Service
@Slf4j
public class RegionGridUtilsServiceImpl implements RegionGridUtilsService {

    @Resource
    private RegionGridListServiceImpl regionGridListService;

    @Resource
    private RegionGridMapper regionGridMapper;

    @Resource
    private RegionUserRelateMapper regionUserRelateMapper;


    @Resource
    private RegionAddressTreeMapper regionAddressTreeMapper;

    @Resource
    private RegionAdminDivisionRelateMapper regionAdminDivisionRelateMapper;

    @Resource
    private GridUserMapper gridUserMapper;

    @Resource
    private SysDeptMapper sysDeptMapper;

    @Resource
    private DeptRegionRelateMapper deptRegionRelateMapper;

    @Resource
    private AdminDivisionMapper adminDivisionMapper;

    private final Map<Integer, Integer> codeSizeToLevel = new HashMap<Integer, Integer>() {{
        put(2, 1);
        put(4, 2);
        put(6, 3);
        put(9, 4);
        put(12, 5);
    }};

    @Resource
    private IMyCustomerService myCustomerService;

    @Resource
    private RedisCache redisCache;

    private final String regionRetailCountRedisKey = "REGION_RETAIL_COUNT_";

    private final String regionBusinessCountRedisKey = "REGION_BUSINESS_COUNT_";

    private final String regionMerchantCountRedisKey = "REGION_MERCHANT_COUNT_";

    private final String topGridNameRedisKey = "TOP_GRID_NAME_";

    /**
     * @param collect
     * @return
     */
    @Override
    public Long getExpectedCustCount(List<String> collect) {
        Long total = 0L;
        for (String code : collect) {
            if (redisCache.hasKey(regionRetailCountRedisKey + code))
                total = total + (Long) redisCache.getCacheObject(regionRetailCountRedisKey + code);
            else {
                Long count = myCustomerService.countCustRetailByRegion(code);
                redisCache.setCacheObjectToEndDay(regionRetailCountRedisKey + code, count);
                total = total + count;
            }
            if (redisCache.hasKey(regionBusinessCountRedisKey + code)) {
                total = total + (Long) redisCache.getCacheObject(regionBusinessCountRedisKey + code);
            } else {
                Long count = myCustomerService.countCustBusinessByRegion(code);
                redisCache.setCacheObjectToEndDay(regionBusinessCountRedisKey + code, count);
                total = total + count;
            }
            if (redisCache.hasKey(regionMerchantCountRedisKey + code)) {
                total = total + (Long) redisCache.getCacheObject(regionMerchantCountRedisKey + code);
            } else {
                Long count = myCustomerService.countCustMerchantByRegion(code);
                redisCache.setCacheObjectToEndDay(regionMerchantCountRedisKey + code, count);
                total = total + count;
            }
        }
        ;
        return total;
    }

    /**
     * @param gridLevel
     * @return
     */
    @Override
    public List<String> getSelectedRegionCodes(String gridLevel, String opsDept) {
        Long deptId = SecurityUtils.getDeptId();
        // TODO
        if (Objects.isNull(opsDept) && !SecurityUtils.isHead()) {
            throw new ServiceException("当前用户不存在业务部室归属信息");
        }
        if (Objects.isNull(opsDept)) {
            opsDept = SecurityUtils.getOpsDept();
        }
        return regionGridMapper.getSelectedRegionCodes(deptId, opsDept, gridLevel, null);
    }

    @Override
    public Boolean checkGridNameExist(String gridName, String gridLevel) {
        Long deptId = SecurityUtils.getDeptId();
        LambdaQueryWrapper<RegionGrid> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RegionGrid::getGridName, gridName)
                .eq(RegionGrid::getGridLevel, gridLevel)
                .eq(RegionGrid::getDeptId, deptId);
        return regionGridMapper.selectCount(queryWrapper) > 0;
    }

    /**
     * @return
     */
    @Override
    public List<GridUserVO> getUserListByGridId(Long gridId) {
        // 获取网格关联机构号
        LambdaQueryWrapper<RegionUserRelate> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RegionUserRelate::getGridId, gridId);
        List<RegionUserRelate> regionUserRelates = regionUserRelateMapper.selectList(queryWrapper);
        if (Objects.isNull(regionUserRelates) || regionUserRelates.isEmpty()) {
            throw new ServiceException("当前一级网格未关联机构！");
        }
//        List<Long> deptIds = regionUserRelates.stream().map(RegionUserRelate::getRelateDeptId).collect(Collectors.toList());
//        if (!deptIds.contains(SecurityUtils.getDeptId())) {
//            throw new ServiceException("当前用户没有权限选择客户经理！");
//        }
        //  获取当前用户所属机构下子机构号
        List<Long> collect = sysDeptMapper.selectChildrenDeptById(SecurityUtils.getDeptId()).stream().map(SysDept::getDeptId).collect(Collectors.toList());
        collect.add(SecurityUtils.getDeptId());
        return gridUserMapper.selectGridUser(collect);
    }

    @Override
    public List<AddressTreeVO> getTopGridAddressTree(Long gridId, List<String> selectedRegionCodes) {
        LambdaQueryWrapper<RegionAddressTree> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RegionAddressTree::getGridId, gridId)
                .orderByAsc(RegionAddressTree::getLevel);
        List<RegionAddressTree> regionAddressTrees = regionAddressTreeMapper.selectList(queryWrapper);
        if (Objects.isNull(regionAddressTrees) || regionAddressTrees.isEmpty()) {
            throw new ServiceException("该上级网格不存在！");
        }
        Integer baseLevel = regionAddressTrees.get(0).getLevel();
        List<AddressTreeVO> collect = new ArrayList<>();

        for (RegionAddressTree regionAddressTree : regionAddressTrees) {
            if (regionAddressTree.getLevel().equals(baseLevel)) {
                AddressTreeVO addressTreeVO = new AddressTreeVO();
                addressTreeVO.setValue(regionAddressTree.getCode());
                addressTreeVO.setLabel(regionAddressTree.getLabel());
                addressTreeVO.setLevel(codeSizeToLevel.get(regionAddressTree.getCode().length()));
                if (addressTreeVO.getLevel() < 3 || selectedRegionCodes.contains(regionAddressTree.getCode())) {
                    addressTreeVO.setDisabled(Boolean.TRUE);
                } else {
                    addressTreeVO.setDisabled(Boolean.FALSE);
                }
                addressTreeVO.setChildren(dfs(regionAddressTree.getCode(), regionAddressTrees, selectedRegionCodes));
                collect.add(addressTreeVO);
            }
        }
        collect.forEach(addressTreeVO -> disabledCheck(addressTreeVO));
        return collect;
    }

    private void disabledCheck(AddressTreeVO addressTreeVO) {
        if (Objects.isNull(addressTreeVO.getChildren())) {
            return;
        }
        addressTreeVO.getChildren().forEach(this::disabledCheck);
        if (isAllDisabled(addressTreeVO.getChildren())) {
            addressTreeVO.setDisabled(Boolean.TRUE);
        }
    }

    private boolean isAllDisabled(List<AddressTreeVO> addressTreeVOList) {
        for (AddressTreeVO addressTreeVO : addressTreeVOList) {
            if (!addressTreeVO.getDisabled()) {
                return Boolean.FALSE;
            }
        }
        return Boolean.TRUE;
    }

    private List<AddressTreeVO> dfs(String code, List<RegionAddressTree> regionAddressTrees, List<String> selectedRegionCodes) {
        if (codeSizeToLevel.get(code.length()) > 4) {
            return null;
        }
        List<AddressTreeVO> collect = new ArrayList<>();
        for (RegionAddressTree regionAddressTree : regionAddressTrees) {
            if (regionAddressTree.getParentCode().equals(code)) {
                AddressTreeVO addressTreeVO = new AddressTreeVO();
                addressTreeVO.setValue(regionAddressTree.getCode());
                addressTreeVO.setLabel(regionAddressTree.getLabel());
                addressTreeVO.setLevel(codeSizeToLevel.get(regionAddressTree.getCode().length()));
                if (addressTreeVO.getLevel() < 3 || selectedRegionCodes.contains(regionAddressTree.getCode())) {
                    addressTreeVO.setDisabled(Boolean.TRUE);
                } else {
                    addressTreeVO.setDisabled(Boolean.FALSE);
                }
                addressTreeVO.setChildren(dfs(regionAddressTree.getCode(), regionAddressTrees, selectedRegionCodes));
                collect.add(addressTreeVO);
            }
        }
        if (collect.isEmpty()) {
            return null;
        } else {
            return collect;
        }
    }

    @Override
    public List<TopGridSelectVO> getTopGridList() {
        Long deptId = SecurityUtils.getDeptId();
        LambdaQueryWrapper<RegionUserRelate> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RegionUserRelate::getRelateType, "2").eq(RegionUserRelate::getRelateDeptId, deptId);
        List<RegionUserRelate> regionUserRelates = regionUserRelateMapper.selectList(queryWrapper);
        List<Long> gridIds = regionUserRelates.stream().map(RegionUserRelate::getGridId).collect(Collectors.toList());
        if (gridIds.isEmpty()) {
            return null;
        }
        List<RegionGrid> regionGrids = regionGridMapper.selectBatchIds(gridIds);
        return regionGrids.stream().map(regionGrid -> {
            TopGridSelectVO topGridSelectVO = new TopGridSelectVO();
            BeanUtils.copyProperties(regionGrid, topGridSelectVO);
            return topGridSelectVO;
        }).collect(Collectors.toList());


    }

    /**
     *
     */
    @Override
    public void updateTopGridNameLabelToRedis() {
        Long deptId = SecurityUtils.getDeptId();
        LambdaQueryWrapper<RegionUserRelate> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RegionUserRelate::getRelateType, "2").eq(RegionUserRelate::getRelateDeptId, deptId);
        List<RegionUserRelate> regionUserRelates = regionUserRelateMapper.selectList(queryWrapper);
        List<Long> gridIds = regionUserRelates.stream().map(RegionUserRelate::getGridId).collect(Collectors.toList());
        if (gridIds.isEmpty()) {
            throw new ServiceException("该机构没有可创建的一级网格！");
        }
        List<RegionGrid> regionGrids = regionGridMapper.selectBatchIds(gridIds);
        String label = regionGrids.stream().map(regionGrid -> regionGrid.getGridName()).collect(Collectors.joining(","));
        log.info("更新机构一级网格名称缓存：" + label);
        redisCache.setCacheObject(topGridNameRedisKey + deptId, label);
    }

    /**
     *
     */
    @Override
    public void loadRegionCustCount(Long... deptIds) {
        for (Long deptId : deptIds) {
            LambdaQueryWrapper<DeptRegionRelate> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(DeptRegionRelate::getDeptId, deptId);
            List<DeptRegionRelate> deptRegionRelates = deptRegionRelateMapper.selectList(queryWrapper);

            String deptCode = String.valueOf(deptId).substring(0, 3);
            //TODO 优化行政区划加载
            List<String> codeByDept = regionAdminDivisionRelateMapper.selectCodeByDept(deptCode);
            List<String> codeRange = deptRegionRelates.stream().map(DeptRegionRelate::getRegionCode).collect(Collectors.toList());
            codeRange.addAll(codeByDept);
            if (codeRange.isEmpty()) {
                continue;
            }
            LambdaQueryWrapper<AdminDivision> queryWrapper1 = new LambdaQueryWrapper<>();
            queryWrapper1.and(i -> {
                codeRange.stream().distinct().forEach(region -> {
                    i.or().likeRight(AdminDivision::getCode, region);
                });
            });
            queryWrapper1.gt(AdminDivision::getLevel, 3);
            List<String> codeList = adminDivisionMapper.selectList(queryWrapper1).stream().map(AdminDivision::getCode).collect(Collectors.toList());
            codeList.forEach(code -> {
                List<Long> counts = myCustomerService.countAllByCode(code, deptCode);
                redisCache.setCacheObjectToEndDay(regionRetailCountRedisKey + code, counts.get(0));
                redisCache.setCacheObjectToEndDay(regionBusinessCountRedisKey + code, counts.get(2));
                redisCache.setCacheObjectToEndDay(regionMerchantCountRedisKey + code, counts.get(1));
            });
        }

    }

    /**
     * @return
     */
    @Override
    public List<Long> getGridCustCount(Long gridId) {
        LambdaQueryWrapper<RegionAdminDivisionRelate> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RegionAdminDivisionRelate::getGridId, gridId);
        List<String> collect = regionAdminDivisionRelateMapper.selectList(queryWrapper).stream().map(RegionAdminDivisionRelate::getCode).distinct().collect(Collectors.toList());
        Long retailCount = 0L;
        Long businessCount = 0L;
        Long merchantCount = 0L;
        for (String code : collect) {
            if (redisCache.hasKey(regionRetailCountRedisKey + code))
                retailCount = retailCount + (Long) redisCache.getCacheObject(regionRetailCountRedisKey + code);
            else {
                Long count = myCustomerService.countCustRetailByRegion(code);
                redisCache.setCacheObjectToEndDay(regionRetailCountRedisKey + code, count);
                retailCount = retailCount + count;
            }
            if (redisCache.hasKey(regionBusinessCountRedisKey + code)) {
                businessCount = businessCount + (Long) redisCache.getCacheObject(regionBusinessCountRedisKey + code);
            } else {
                Long count = myCustomerService.countCustBusinessByRegion(code);
                redisCache.setCacheObjectToEndDay(regionBusinessCountRedisKey + code, count);
                businessCount = businessCount + count;
            }
            if (redisCache.hasKey(regionMerchantCountRedisKey + code)) {
                merchantCount = merchantCount + (Long) redisCache.getCacheObject(regionMerchantCountRedisKey + code);
            } else {
                Long count = myCustomerService.countCustMerchantByRegion(code);
                redisCache.setCacheObjectToEndDay(regionMerchantCountRedisKey + code, count);
                merchantCount = merchantCount + count;
            }
        }

        return Arrays.asList(retailCount, merchantCount, businessCount);
    }

    /**
     * @param deptId
     * @return
     */
    @Override
    public CustCountDTO getCustByDeptId(Long deptId, String opsDept) {
        CustCountDTO custCountDTO = new CustCountDTO();
        List<String> codeByDeptId;
        if (SecurityUtils.hasRole("outletAdmin")) {
            codeByDeptId = regionGridMapper.getCodeByOutletId(deptId);
        } else {
            codeByDeptId = regionGridMapper.getCodeByDeptId(deptId, opsDept);
        }
        Long retailCount = 0L;
        Long businessCount = 0L;
        Long merchantCount = 0L;
        for (String code : codeByDeptId) {
            if (redisCache.hasKey(regionRetailCountRedisKey + code))
                retailCount = retailCount + (Long) redisCache.getCacheObject(regionRetailCountRedisKey + code);
            else {
                Long count = myCustomerService.countCustRetailByRegion(code);
                redisCache.setCacheObjectToEndDay(regionRetailCountRedisKey + code, count);
                retailCount = retailCount + count;
            }
            if (redisCache.hasKey(regionBusinessCountRedisKey + code)) {
                businessCount = businessCount + (Long) redisCache.getCacheObject(regionBusinessCountRedisKey + code);
            } else {
                Long count = myCustomerService.countCustBusinessByRegion(code);
                redisCache.setCacheObjectToEndDay(regionBusinessCountRedisKey + code, count);
                businessCount = businessCount + count;
            }
            if (redisCache.hasKey(regionMerchantCountRedisKey + code)) {
                merchantCount = merchantCount + (Long) redisCache.getCacheObject(regionMerchantCountRedisKey + code);
            } else {
                Long count = myCustomerService.countCustMerchantByRegion(code);
                redisCache.setCacheObjectToEndDay(regionMerchantCountRedisKey + code, count);
                merchantCount = merchantCount + count;
            }
        }
        custCountDTO.setRetailCount(retailCount);
        custCountDTO.setMerchantCount(merchantCount);
        custCountDTO.setBusinessCount(businessCount);

        return custCountDTO;
    }

    /**
     *
     * @param gridId
     * @return
     */
    @Override
    public String getGridOpsDept(Long gridId) {
        RegionGrid regionGrid = regionGridMapper.selectById(gridId);
        return regionGrid.getOpsDept();
    }

    /**
     * @param code
     * @param opsDept
     * @return
     */
    @Override
    public Boolean checkadminRegionValid(String code, String opsDept) {
        LambdaQueryWrapper<RegionAdminDivisionRelate> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RegionAdminDivisionRelate::getCode, code)
                .eq(RegionAdminDivisionRelate::getOpsDept, opsDept)
                .isNull(RegionAdminDivisionRelate::getParentGridId);
        return regionAdminDivisionRelateMapper.selectCount(queryWrapper) > 0;
    }

    /**
     * @param code
     * @param opsDept
     * @return
     */
    @Override
    public Boolean checkadminRegionAvailable(String code, String opsDept) {
        LambdaQueryWrapper<RegionAdminDivisionRelate> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RegionAdminDivisionRelate::getCode, code)
                .eq(RegionAdminDivisionRelate::getOpsDept, opsDept)
                .isNotNull(RegionAdminDivisionRelate::getParentGridId);
        return regionAdminDivisionRelateMapper.selectCount(queryWrapper) < 1;
    }
}
