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

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.ibs.grid.domain.entity.AdminDivision;
import com.ruoyi.ibs.grid.domain.entity.AdminDivisionFence;
import com.ruoyi.ibs.grid.domain.entity.DeptRegionRelate;
import com.ruoyi.ibs.grid.domain.vo.AddressTreeVO;
import com.ruoyi.ibs.grid.mapper.AdminDivisionFenceMapper;
import com.ruoyi.ibs.grid.mapper.AdminDivisionMapper;
import com.ruoyi.ibs.grid.mapper.DeptRegionRelateMapper;
import com.ruoyi.ibs.grid.service.SysAddressService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import static com.ruoyi.ibs.grid.util.GeoJsonFileToWktConverter.convertFile;

/**
 * @Author 吴凯程
 * @Date 2024/9/13
 **/

@Service
@Slf4j
public class SysAddressServiceImpl implements SysAddressService {
    @Resource
    private AdminDivisionMapper adminDivisionMapper;

    @Resource
    private AdminDivisionFenceMapper adminDivisionFenceMapper;

    @Resource
    private DeptRegionRelateMapper deptRegionRelateMapper;

    // 根据行政编码长度判断行政区划等级
    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);
    }};

    // 根据行政区划等级获取上级行政区划编码
    private final Map<Integer, Integer> levelToParentCode = new HashMap<Integer, Integer>() {{
        put(2, 2);
        put(3, 4);
        put(4, 6);
        put(5, 9);
    }};

    // 根据行政编码获取行政区划信息
    public static final Map<String, AdminDivision> adminDivisionMap = new HashMap<>();

    // 行政区划编码结构树
    private final Map<String, List<String>> addressTreeMap = new HashMap<>();


    public void initGeoData() {
        System.out.println("<-------------------Loading geo data from files------------------->");
        String rootPath = ".\\浙江省";
        List<AdminDivision> adminDivisions = adminDivisionMapper.selectList(null);
        for (AdminDivision adminDivision : adminDivisions) {
            System.out.println(adminDivision.toString());
            AdminDivisionFence adminDivisionFence = new AdminDivisionFence();
            adminDivisionFence.setId(adminDivision.getId());
            String filePath = rootPath;
            if (!adminDivision.getCity().isEmpty()) {
                filePath = filePath + "\\" + adminDivision.getCity();
            }
            if (!adminDivision.getCounty().isEmpty()) {
                filePath = filePath + "\\" + adminDivision.getCounty();
            }
            if (!adminDivision.getTown().isEmpty()) {
                filePath = filePath + "\\" + adminDivision.getTown();
            }
            if (!adminDivision.getVillage().isEmpty()) {
                filePath = filePath + "\\" + adminDivision.getVillage();
            }
            filePath = filePath + ".json";
            String wkt = null;
            try {
                wkt = convertFile(filePath);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            adminDivisionFence.setGrid(wkt);
            adminDivisionFenceMapper.insert(adminDivisionFence);
        }

    }

    /**
     *
     */
    @Override
    public void initSHGeoData() {

        LambdaQueryWrapper<AdminDivision> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.likeRight(AdminDivision::getCode, "31");
        List<AdminDivision> adminDivisions = adminDivisionMapper.selectList(queryWrapper);
        for (AdminDivision adminDivision : adminDivisions) {
            System.out.println(adminDivision.toString());
            AdminDivisionFence adminDivisionFence = new AdminDivisionFence();
            adminDivisionFence.setId(adminDivision.getId());
            ClassPathResource classPathResource = new ClassPathResource("31/" + adminDivision.getCode() + ".json");
            String filePath = classPathResource.getPath();
            log.info("filePath: {}", filePath);
            String wkt = null;
            try {
                wkt = convertFile(filePath);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            adminDivisionFence.setGrid(wkt);
            adminDivisionFenceMapper.insert(adminDivisionFence);
        }
    }



    @Override
    public List<AddressTreeVO> getFullAddressTreeByCache(String code, List<String> selectedRegionCodes) {
        if (!adminDivisionMap.containsKey(code)) {
            throw new ServiceException("行政编码不合法！");
        }
        List<AddressTreeVO> addressTreeVOS = dfsWithCheck(code, selectedRegionCodes);
        addressTreeVOS.forEach(this::disabledCheck);
        return addressTreeVOS;
    }

    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;
    }

    /**
     * @return
     */
    @Override
    public AddressTreeVO getFullAddressTree() {
        AddressTreeVO addressTreeVO = new AddressTreeVO();
        addressTreeVO.setValue("33");
        addressTreeVO.setLabel("浙江省");
        addressTreeVO.setLevel(1);
        addressTreeVO.setChildren(dfs("33"));
        return addressTreeVO;
    }

    /**
     * @param code
     * @return
     */
    @Override
    public List<AddressTreeVO> getAddressTreeByCode(String code) {
        if (codeSizeToLevel.get(code.length()) == 5) {
            return null;
        }
        List<String> children = addressTreeMap.get(code);
        return children.stream().map(child -> {
            AdminDivision adminDivision = adminDivisionMap.get(child);
            AddressTreeVO addressTreeVO = new AddressTreeVO();
            addressTreeVO.setLabel(adminDivision.getLabel());
            addressTreeVO.setValue(adminDivision.getCode());
            addressTreeVO.setLevel(codeSizeToLevel.get(adminDivision.getCode().length()));
            return addressTreeVO;
        }).collect(Collectors.toList());
    }

    @Override
    public List<String> getNextLevelRegionCode(String code) {
        LambdaQueryWrapper<AdminDivision> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.likeRight(AdminDivision::getCode, code).eq(AdminDivision::getLevel, 5);
        List<AdminDivision> adminDivisions = adminDivisionMapper.selectList(queryWrapper);
        return adminDivisions.stream().map(AdminDivision::getCode).collect(Collectors.toList());


    }

    /**
     * @param code
     * @return
     */
    @Override
    public AdminDivision getAdminDivisionByCode(String code) {
        if(adminDivisionMap.containsKey(code)){
            return adminDivisionMap.get(code);
        }
        LambdaQueryWrapper<AdminDivision> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AdminDivision::getCode, code);
        AdminDivision adminDivision = adminDivisionMapper.selectOne(queryWrapper);
        if (Objects.isNull(adminDivision)) {
            throw new ServiceException("行政编码不在浙江省或非法编码！");
        }
        return adminDivision;
    }

    /**
     * @return
     */
    @Override
    public String getRegionCodeByDept() {
        Long deptId = SecurityUtils.getDeptId();
        LambdaQueryWrapper<DeptRegionRelate> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DeptRegionRelate::getDeptId, deptId);
        DeptRegionRelate deptRegionRelate = deptRegionRelateMapper.selectOne(queryWrapper);
        if (Objects.isNull(deptRegionRelate) || deptRegionRelate.getRegionCode().isEmpty()) {
            throw new ServiceException("非总行管理员或总行未绑定行政区划！");
        }
        return deptRegionRelate.getRegionCode();
    }


    private List<AddressTreeVO> dfsWithCheck(String code, List<String> selectedRegionCodes) {
        if (codeSizeToLevel.get(code.length()) == 5) {
            return null;
        }
        List<String> children = addressTreeMap.get(code);
        return children.stream().map(child -> {
            AdminDivision adminDivision = adminDivisionMap.get(child);
            AddressTreeVO addressTreeVO = new AddressTreeVO();
            addressTreeVO.setLabel(adminDivision.getLabel());
            addressTreeVO.setValue(adminDivision.getCode());
            addressTreeVO.setLevel(codeSizeToLevel.get(adminDivision.getCode().length()));
            if (addressTreeVO.getLevel() < 3 || selectedRegionCodes.contains(adminDivision.getCode())) {
                addressTreeVO.setDisabled(Boolean.TRUE);
            } else {
                addressTreeVO.setDisabled(Boolean.FALSE);
            }
            addressTreeVO.setChildren(dfsWithCheck(adminDivision.getCode(), selectedRegionCodes));
            return addressTreeVO;
        }).collect(Collectors.toList());
    }

    @PostConstruct
    public void initAddressTress() {
        log.info("<------------Loading address trees------------->");
        LambdaQueryWrapper<AdminDivision> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByAsc(AdminDivision::getLevel);
        List<AdminDivision> adminDivisions = adminDivisionMapper.selectList(queryWrapper);
        adminDivisions.forEach(adminDivision -> {
            adminDivisionMap.put(adminDivision.getCode(), adminDivision);
            if (adminDivision.getLevel() > 1) {
                String parentCode = adminDivision.getCode().subSequence(0, levelToParentCode.get(adminDivision.getLevel())).toString();
                addressTreeMap.get(parentCode).add(adminDivision.getCode());
            }
            if (adminDivision.getLevel() < 5) {
                addressTreeMap.put(adminDivision.getCode(), new ArrayList<String>());
            }
        });
        log.info("<------------Loading address trees completed------------->");
    }

    private List<AddressTreeVO> dfs(String code) {
        if (codeSizeToLevel.get(code.length()) == 5) {
            return null;
        }

        List<String> children = addressTreeMap.get(code);
        return children.stream().map(child -> {
            AdminDivision adminDivision = adminDivisionMap.get(child);
            AddressTreeVO addressTreeVO = new AddressTreeVO();
            addressTreeVO.setLabel(adminDivision.getLabel());
            addressTreeVO.setValue(adminDivision.getCode());
            addressTreeVO.setLevel(codeSizeToLevel.get(adminDivision.getCode().length()));
            addressTreeVO.setChildren(dfs(adminDivision.getCode()));
            return addressTreeVO;
        }).collect(Collectors.toList());
    }


}
