package pwc.taxtech.atms.service.impl;

import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import pwc.taxtech.atms.common.CommonConstants;
import pwc.taxtech.atms.common.CommonUtils;
import pwc.taxtech.atms.common.SortUtils;
import pwc.taxtech.atms.dao.RegionMapper;
import pwc.taxtech.atms.dto.RegionShortDto;
import pwc.taxtech.atms.dto.VMRegionDevTreeData;
import pwc.taxtech.atms.dto.navtree.DevTreeDto;
import pwc.taxtech.atms.dto.navtree.IvhTreeDto;
import pwc.taxtech.atms.entity.Area;
import pwc.taxtech.atms.entity.AreaExample;
import pwc.taxtech.atms.entity.AreaRegion;
import pwc.taxtech.atms.entity.AreaRegionExample;
import pwc.taxtech.atms.entity.Region;
import pwc.taxtech.atms.entity.RegionExample;
import pwc.taxtech.atms.exception.ApplicationException;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static java.util.Comparator.*;
import static java.util.stream.Collectors.toList;
import static pwc.taxtech.atms.common.CommonUtils.copyProperties;
import static pwc.taxtech.atms.common.CommonUtils.getUUID;

@Service
public class RegionServiceImpl extends AbstractService {

    @Autowired
    private AreaServiceImpl areaService;

    @Autowired
    private AreaRegionServiceImpl areaRegionService;

    @Autowired
    private RegionMapper regionMapper;

    /*
     * (non-Javadoc)
     *
     * @see pwc.taxtech.atms.service.RegionService#getRegionAreaTree()
     */
    public List<DevTreeDto<VMRegionDevTreeData>> getRegionAreaTree() {

        List<DevTreeDto<VMRegionDevTreeData>> regionAreaTree = new ArrayList<>();
        List<Area> topAreas = areaService.findTopAreas();
        if (null == topAreas || topAreas.isEmpty()) {
            return regionAreaTree;
        }

        for (Area topArea : topAreas) {

            DevTreeDto<VMRegionDevTreeData> topNode = new DevTreeDto<>();
            topNode.setItems(new ArrayList<>());

            VMRegionDevTreeData data = new VMRegionDevTreeData();
            data.setLevelType(0);
            data.setMergerName(topArea.getName());
            data.setIsArea(true);
            data.setIsActive(topArea.getIsActive());

            topNode.setId(topArea.getId());
            topNode.setCode("");
            topNode.setExpanded(true);
            topNode.setParentId(null);
            topNode.setRelyOnCodes("");
            topNode.setText(topArea.getName());
            topNode.setData(data);
            topNode.setUniqueId(CommonUtils.getUUID());

            topNode.setItems(getChildrenAreaTree(topNode));
            topNode.getItems().addAll(getRegionTree(topArea));

            regionAreaTree.add(topNode);
        }

        SortUtils.sortDevTreeDtoByText(regionAreaTree);

        return regionAreaTree;
    }

    /**
     * Recursively get area tree
     *
     * @param current
     * @return List<DevTreeDto>
     */
    private List<DevTreeDto<VMRegionDevTreeData>> getChildrenAreaTree(DevTreeDto<VMRegionDevTreeData> current) {

        List<DevTreeDto<VMRegionDevTreeData>> childrenAreaTree = new ArrayList<>();
        List<Area> subAreas = areaService.getSubAreaListIngoreActive(current.getId());
        if (subAreas == null || subAreas.isEmpty()) {
            return childrenAreaTree;

        }

        for (Area subArea : subAreas) {
            DevTreeDto<VMRegionDevTreeData> node = new DevTreeDto<>();
            node.setItems(new ArrayList<>());

            VMRegionDevTreeData data = new VMRegionDevTreeData();
            data.setLevelType(current.getData().getLevelType() + 1);
            data.setMergerName(subArea.getName());
            data.setIsArea(true);
            data.setIsActive(subArea.getIsActive());

            node.setId(subArea.getId());
            node.setCode("");
            node.setExpanded(false);
            node.setParentId(current.getId());
            node.setRelyOnCodes("");
            node.setText(subArea.getName());
            node.setData(data);
            node.setUniqueId(CommonUtils.getUUID());

            node.setItems(getChildrenAreaTree(node));
            node.getItems().addAll(getRegionTree(subArea));
            childrenAreaTree.add(node);
        }

        SortUtils.sortDevTreeDtoByText(childrenAreaTree);

        return childrenAreaTree;
    }

    /**
     * Get the region tree under an area
     *
     * @param area
     * @return List<DevTreeDto>
     */
    private List<DevTreeDto<VMRegionDevTreeData>> getRegionTree(Area area) {

        List<DevTreeDto<VMRegionDevTreeData>> regionTree = new ArrayList<>();

        List<AreaRegion> subRegions = areaRegionService.findAreaRegionsByArea(area.getId());
        if (subRegions == null || subRegions.isEmpty()) {
            return regionTree;
        }

        int currentLevelType = 9;
        for (int i = 0; i < subRegions.size(); i++) {

            DevTreeDto<VMRegionDevTreeData> currentNode = new DevTreeDto<>();
            currentNode.setItems(new ArrayList<>());
            AreaRegion areaRegion = subRegions.get(i);

            VMRegionDevTreeData data = new VMRegionDevTreeData();
            data.setLevelType(areaRegion.getRegion().getLevelType());
            data.setMergerName(areaRegion.getRegion().getMergerName());
            data.setIsArea(false);
            data.setAreaId(areaRegion.getArea().getId());
            data.setIsActive(areaRegion.getRegion().getIsActive());

            currentNode.setId(areaRegion.getRegion().getId());
            currentNode.setCode("");
            currentNode.setExpanded(false);
            currentNode.setParentId(areaRegion.getRegion().getParentId());
            currentNode.setRelyOnCodes("");
            currentNode.setText(areaRegion.getRegion().getName());
            currentNode.setData(data);
            currentNode.setUniqueId(CommonUtils.getUUID());

            if (currentLevelType > currentNode.getData().getLevelType()) {
                regionTree.add(currentNode);
            } else {
                if (null == regionTree.get(regionTree.size() - 1).getItems()) {
                    regionTree.get(regionTree.size() - 1).setItems(new ArrayList<DevTreeDto<VMRegionDevTreeData>>());
                }
                currentNode.setUniqueId(null);
                regionTree.get(regionTree.size() - 1).getItems().add(currentNode);

            }
            currentLevelType = currentNode.getData().getLevelType();
        }
        return regionTree;
    }

    /*
     * (non-Javadoc)
     *
     * @see pwc.taxtech.atms.service.RegionService#getProvinceAndCityTreeList()
     */
    public List<DevTreeDto<IvhTreeDto<Region>>> getProvinceAndCityTreeList() {

        // Find all regions with leveltype 1
        RegionExample regionExample = new RegionExample();
        regionExample.createCriteria().andLevelTypeEqualTo(CommonConstants.REGION_LEVELTYPE_PROVINCE);
        regionExample.setOrderByClause("id ASC");
        List<Region> regionList = regionMapper.selectByExample(regionExample);

        List<DevTreeDto<IvhTreeDto<Region>>> devTree = new ArrayList<>();
        for (Region region : regionList) {

            DevTreeDto<IvhTreeDto<Region>> regionNode = new DevTreeDto<>();
            regionNode.setItems(new ArrayList<>());

            regionNode.setId(region.getId());
            regionNode.setCode(null);
            regionNode.setExpanded(false);
            regionNode.setParentId(null);
            regionNode.setRelyOnCodes(null);
            regionNode.setText(region.getName());
            regionNode.setUniqueId(null);
            regionNode.setItems(new ArrayList<DevTreeDto<IvhTreeDto<Region>>>());

            IvhTreeDto<Region> regionData = new IvhTreeDto<>();
            regionData.setId(region.getId());
            regionData.setLabel(region.getName());
            regionData.setValue(region);
            regionData.setChildren(new ArrayList<IvhTreeDto<Region>>());

            // Find cities under region
            RegionExample cityExample = new RegionExample();
            cityExample.createCriteria().andLevelTypeEqualTo(CommonConstants.REGION_LEVELTYPE_CITY)
                    .andParentIdEqualTo(region.getId());
            regionExample.setOrderByClause("id ASC");
            List<Region> cityList = regionMapper.selectByExample(cityExample);

            for (Region city : cityList) {

                DevTreeDto<IvhTreeDto<Region>> cityNode = new DevTreeDto<>();
                cityNode.setItems(new ArrayList<>());
                cityNode.setId(city.getId());
                cityNode.setCode(null);
                cityNode.setExpanded(false);
                cityNode.setParentId(null);
                cityNode.setRelyOnCodes(null);
                cityNode.setText(city.getName());
                cityNode.setUniqueId(null);

                IvhTreeDto<Region> cityData = new IvhTreeDto<>();
                cityData.setChildren(new ArrayList<>());
                Region cloneCity = new Region();
                try {
                    cloneCity = (Region) (city.clone());
                } catch (CloneNotSupportedException e) {

                }
                cityData.setId(city.getId());
                cityData.setLabel(city.getName());
                cityData.setValue(cloneCity);
                cityNode.setData(cityData);

                IvhTreeDto<Region> childIvhTreeDto = new IvhTreeDto<>();
                childIvhTreeDto.setChildren(new ArrayList<>());
                childIvhTreeDto.setId(city.getId());
                childIvhTreeDto.setLabel(city.getName());
                childIvhTreeDto.setValue(city);
                regionData.getChildren().add(childIvhTreeDto);

                regionNode.getItems().add(cityNode);
            }
            regionNode.setData(regionData);
            devTree.add(regionNode);
        }
        return devTree;
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    public List<DevTreeDto> getAreaRegionTreeByNodeId(String areaId) {
        Area toNode = areaMapper.selectByPrimaryKey(areaId);
        Region regionInfo = null;
        if (toNode == null) {
            // id 为省市 id
            regionInfo = regionMapper.selectByPrimaryKey(areaId);
            if (regionInfo == null) {
                return new ArrayList<>();
            }
            AreaRegionExample areaRegionExample = new AreaRegionExample();
            areaRegionExample.createCriteria().andRegionIdEqualTo(regionInfo.getId());
            AreaRegion data = areaRegionMapper.selectByExample(areaRegionExample).stream().findFirst().orElse(null);
            if (data == null) {
                return new ArrayList<>();
            }

            toNode = new Area();
            toNode.setId(regionInfo.getId());
            toNode.setName(regionInfo.getName());
            toNode.setParentId(data.getAreaId());
        }
        RegionShortDto topNode = new RegionShortDto();
        topNode.setId(toNode.getId());
        topNode.setIsActive(BooleanUtils.isTrue(toNode.getIsActive()));
        topNode.setLevelType(0);
        topNode.setMergerName(toNode.getName());
        topNode.setName(toNode.getName());
        topNode.setParentId(toNode.getParentId());
        topNode.setSubRegionList(new ArrayList<>());
        topNode.setIsArea(regionInfo == null);

        VMRegionDevTreeData regionDevTreeData = new VMRegionDevTreeData();
        regionDevTreeData.setMergerName(topNode.getMergerName());
        regionDevTreeData.setLevelType(topNode.getLevelType());
        regionDevTreeData.setIsArea(topNode.getIsArea());
        regionDevTreeData.setAreaId(topNode.getAreaId());
        regionDevTreeData.setIsActive(BooleanUtils.isTrue(toNode.getIsActive()));

        DevTreeDto tree = new DevTreeDto();
        tree.setCode("");
        tree.setExpanded(true);
        tree.setItems(new ArrayList<>());
        tree.setParentId("");
        tree.setRelyOnCodes("");
        tree.setText(toNode.getName());
        tree.setId(toNode.getId());
        tree.setData(regionDevTreeData);

        if (regionInfo == null) {
            List<Area> areaList = areaMapper.selectByExample(new AreaExample());
            List<AreaRegion> areaRegionList = areaRegionMapper.selectByExample(new AreaRegionExample());
            RegionExample regionExample = new RegionExample();
            regionExample.createCriteria().andLevelTypeLessThan(3);
            List<Region> regionList = regionMapper.selectByExample(regionExample);
            List<RegionShortDto> relationRegionQuery = new ArrayList<>();
            for (AreaRegion ar : areaRegionList) {
                if (ar == null || ar.getRegionId() == null) {
                    continue;
                }
                for (Region r : regionList) {
                    if (ar.getRegionId().equals(r.getId())) {
                        RegionShortDto regionShortDto = new RegionShortDto();
                        regionShortDto.setAreaId(ar.getAreaId());
                        regionShortDto.setId(r.getId());
                        regionShortDto.setIsActive(BooleanUtils.isTrue(r.getIsActive()));
                        regionShortDto.setIsArea(false);
                        regionShortDto.setLevelType(r.getLevelType());
                        regionShortDto.setMergerName(r.getMergerName());
                        regionShortDto.setName(r.getName());
                        regionShortDto.setParentId(r.getParentId());
                        regionShortDto.setSubRegionList(new ArrayList<>());
                        relationRegionQuery.add(regionShortDto);
                    }
                }
            }
            tree.setItems(getAreaRegionChildrenList(topNode, areaList, relationRegionQuery));
        } else {
            RegionExample regionExample = new RegionExample();
            regionExample.createCriteria().andParentIdEqualTo(topNode.getId());
            List<Region> sublist = regionMapper.selectByExample(regionExample);
            List<RegionShortDto> subRegionShortList = sublist.stream().map(sa -> {
                RegionShortDto regionShortDto = copyProperties(sa, new RegionShortDto());
                regionShortDto.setIsActive(BooleanUtils.isTrue(sa.getIsActive()));
                return regionShortDto;
            }).collect(toList());
            tree.setItems(getNavTreeDtoList(topNode, subRegionShortList));
        }
        List<DevTreeDto> list = new ArrayList<>();
        list.add(tree);
        return list;
    }

    /**
     * 获取当前节点的子节点(必须是自定义的区域或者是城市及以上的区域)
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    private List<DevTreeDto> getAreaRegionChildrenList(RegionShortDto current, List<Area> areaList,
                                                       List<RegionShortDto> relationRegionQuery) {
        List<DevTreeDto> subWarpList = new ArrayList<>();
        // 获取区域
        List<Area> subList = areaList.stream().filter(sa -> Objects.equals(sa.getParentId(), current.getId()))
                .collect(toList());
        if (subList != null && !subList.isEmpty()) {
            for (Area area : subList) {
                RegionShortDto model = new RegionShortDto();
                model.setId(area.getId());
                model.setIsActive(BooleanUtils.isTrue(area.getIsActive()));
                model.setIsArea(true);
                model.setLevelType(current.getLevelType() + 1);
                model.setMergerName(area.getName());
                model.setName(area.getName());
                model.setParentId(area.getParentId());
                model.setSubRegionList(new ArrayList<>());

                VMRegionDevTreeData data = copyProperties(model, new VMRegionDevTreeData());
                data.setIsActive(BooleanUtils.isTrue(area.getIsActive()));

                DevTreeDto tree = new DevTreeDto();
                tree.setCode("");
                tree.setExpanded(false);
                tree.setItems(new ArrayList<>());
                tree.setParentId(area.getParentId());
                tree.setRelyOnCodes("");
                tree.setText(area.getName());
                tree.setId(area.getId());
                tree.setData(data);
                tree.setUniqueId(getUUID());
                tree.setItems(getAreaRegionChildrenList(model, areaList, relationRegionQuery));
                tree.getItems().sort(comparing(DevTreeDto<VMRegionDevTreeData>::getText, nullsLast(naturalOrder())));
                subWarpList.add(tree);
            }
        }
        List<RegionShortDto> regionList = relationRegionQuery.stream()
                .filter(sa -> Objects.equals(sa.getAreaId(), current.getId())).collect(toList());
        if (regionList != null && !regionList.isEmpty()) {
            Integer minLevel = regionList.stream().map(RegionShortDto::getLevelType).filter(Objects::nonNull)
                    .min(naturalOrder()).orElseThrow(ApplicationException::new);
            for (RegionShortDto region : regionList) {
                if (Objects.equals(region.getLevelType(), minLevel)) {
                    VMRegionDevTreeData data = copyProperties(region, new VMRegionDevTreeData());
                    DevTreeDto navTree = new DevTreeDto();
                    navTree.setCode("");
                    navTree.setExpanded(false);
                    navTree.setItems(new ArrayList<>());
                    navTree.setParentId(region.getParentId());
                    navTree.setRelyOnCodes("");
                    navTree.setText(region.getName());
                    navTree.setId(region.getId());
                    navTree.setData(data);
                    navTree.setUniqueId(getUUID());
                    // 省的parentId为区域Id
                    region.setParentId(
                            BooleanUtils.isTrue(current.getIsArea()) ? current.getId() : current.getParentId());
                    navTree.setItems(getNavTreeDtoList(region, regionList));
                    navTree.getItems()
                            .sort(comparing(DevTreeDto<VMRegionDevTreeData>::getText, nullsLast(naturalOrder())));
                    subWarpList.add(navTree);
                }
            }
        }
        // TODO 按照拼音排序(所有通过getText进行排序)
        subWarpList.sort(comparing(DevTreeDto<VMRegionDevTreeData>::getText, nullsLast(naturalOrder())));
        return subWarpList;
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    private List<DevTreeDto> getNavTreeDtoList(RegionShortDto currentNode, List<RegionShortDto> list) {
        List<RegionShortDto> subList = list.stream().filter(sa -> Objects.equals(sa.getParentId(), currentNode.getId()))
                .collect(toList());
        List<DevTreeDto> retlist = new ArrayList<>();
        for (RegionShortDto item : subList) {
            VMRegionDevTreeData data = copyProperties(item, new VMRegionDevTreeData());
            DevTreeDto model = new DevTreeDto();
            model.setCode("");
            model.setExpanded(false);
            model.setItems(new ArrayList<>());
            model.setParentId(currentNode.getId());
            model.setRelyOnCodes("");
            model.setText(item.getName());
            model.setId(item.getId());
            model.setData(data);

            model.setItems(getNavTreeDtoList(item, list));
            retlist.add(model);
        }
        return retlist;
    }
}