package com.yeejoin.amos.boot.module.statistcs.biz.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yeejoin.amos.boot.biz.common.dto.CommonVideoDto;
import com.yeejoin.amos.boot.biz.common.entity.BaseEntity;
import com.yeejoin.amos.boot.module.common.api.dto.DPFilterParamDto;
import com.yeejoin.amos.boot.module.common.api.entity.BaseEnterpriseVideo;
import com.yeejoin.amos.boot.module.common.biz.service.impl.BaseEnterpriseVideoServiceImpl;
import com.yeejoin.amos.boot.module.statistics.api.dto.UnitVideoDto;
import com.yeejoin.amos.boot.module.statistics.api.vo.TreeNodeVo;
import com.yeejoin.amos.boot.module.ymt.api.entity.TzBaseEnterpriseInfo;
import com.yeejoin.amos.boot.module.ymt.api.enums.EquipmentClassifityEnum;
import com.yeejoin.amos.boot.module.ymt.api.mapper.TzBaseEnterpriseInfoMapper;
import com.yeejoin.amos.feign.systemctl.model.RegionModel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author Administrator
 */
@Service
@Slf4j
public class VideoServiceImpl {

    private static String NODE_TYPE_REGION = "region";

    private static String NODE_TYPE_UNIT = "unit";

    private static String NODE_TYPE_VIDEO = "video";

    private TzBaseEnterpriseInfoMapper tzBaseEnterpriseInfoMapper;

    private StCommonServiceImpl stCommonService;

    private BaseEnterpriseVideoServiceImpl enterpriseVideoService;


    public VideoServiceImpl(BaseEnterpriseVideoServiceImpl enterpriseVideoService, TzBaseEnterpriseInfoMapper tzBaseEnterpriseInfoMapper, StCommonServiceImpl stCommonService) {
        this.enterpriseVideoService = enterpriseVideoService;
        this.tzBaseEnterpriseInfoMapper = tzBaseEnterpriseInfoMapper;
        this.stCommonService = stCommonService;
    }

    /**
     * 按照区域信息，进行显示区域（可能多层级）、单位（可能在多个区域下）、摄像头（在单位节点下）树
     *
     * @param dpFilterParamDto 区域信息
     * @return List<TreeNodeVo>
     */
    public List<TreeNodeVo> getVideoTree(DPFilterParamDto dpFilterParamDto) {
        // 摄像头设备
        List<BaseEnterpriseVideo> videoEquipList = getVideos(dpFilterParamDto);
        // 摄像头对应的单位列表
        List<TzBaseEnterpriseInfo> baseEnterpriseInfos = this.getUnitListForTree(videoEquipList.stream().map(BaseEnterpriseVideo::getUseUnitCode).collect(Collectors.toSet()));
        // 摄像头对应的行政区域列表
        List<RegionModel> regionModels = this.getRegionListOfVideo(videoEquipList);
        // 摄像头通道
        List<CommonVideoDto> channelNoList = this.getVideoChannel(videoEquipList);
        // 树组装
        return castList2Tree(baseEnterpriseInfos, regionModels, channelNoList, videoEquipList);
    }

    private List<TreeNodeVo> castList2Tree(List<TzBaseEnterpriseInfo> baseEnterpriseInfos, List<RegionModel> regionModels, List<CommonVideoDto> channelNoList, List<BaseEnterpriseVideo> videoEquipList) {
        // 数据处理，把行政区域、单位、摄像头 数据格式化为有parent的标准树结构列表
        List<TreeNodeVo> regionNodes = regionModels.stream().map(r -> {
            TreeNodeVo treeNodeVo = new TreeNodeVo();
            treeNodeVo.setNodeType(NODE_TYPE_REGION);
            treeNodeVo.setId(r.getSequenceNbr().toString());
            treeNodeVo.setName(r.getRegionName());
            treeNodeVo.setCode(r.getRegionCode() + "");
            treeNodeVo.setParentId(String.valueOf(r.getParentId()));
            return treeNodeVo;
        }).collect(Collectors.toList());
        // 区域拼接为树结构
        List<TreeNodeVo> treeNodeVos = regionNodes.stream().filter(r -> StringUtils.isEmpty(r.getParentId()) || "0".equals(r.getParentId())).map(r -> {
            TreeNodeVo treeNodeVo = new TreeNodeVo();
            BeanUtil.copyProperties(r, treeNodeVo);
            treeNodeVo.setChildren(this.getRegionTypeNode(treeNodeVo, regionNodes));
            return treeNodeVo;
        }).collect(Collectors.toList());
        // 给区域的最下级增加直接下级->单位，id不是其他的parent_id则为树的叶子节点
        treeNodeVos.forEach(t -> {
            this.setChildrenUnitTypeNode(regionNodes, t, baseEnterpriseInfos, videoEquipList, channelNoList);
        });
        return treeNodeVos;
    }

    private void setChildrenUnitTypeNode(List<TreeNodeVo> regionNodes, TreeNodeVo parentNode, List<TzBaseEnterpriseInfo> baseEnterpriseInfos, List<BaseEnterpriseVideo> videoEquipList, List<CommonVideoDto> channelNoList) {
        if (regionNodes.stream().noneMatch(e -> parentNode.getId().equals(e.getParentId()))) {
            parentNode.setChildren(this.buildUnitTypeNode(baseEnterpriseInfos, videoEquipList, channelNoList, parentNode));
        } else {
            parentNode.getChildren().forEach(p -> {
                setChildrenUnitTypeNode(regionNodes, p, baseEnterpriseInfos, videoEquipList, channelNoList);
            });
        }
    }

    private List<TreeNodeVo> getRegionTypeNode(TreeNodeVo parentNode, List<TreeNodeVo> regionNodes) {
        return regionNodes.stream().filter(r -> StringUtils.isNotEmpty(r.getParentId()) && r.getParentId().equals(parentNode.getId())).map(r -> {
            TreeNodeVo treeNodeVo = new TreeNodeVo();
            BeanUtil.copyProperties(r, treeNodeVo);
            treeNodeVo.setChildren(this.getRegionTypeNode(treeNodeVo, regionNodes));
            return treeNodeVo;
        }).collect(Collectors.toList());
    }


    private List<TreeNodeVo> buildUnitTypeNode(List<TzBaseEnterpriseInfo> baseEnterpriseInfos, List<BaseEnterpriseVideo> videoEquipList, List<CommonVideoDto> channelNoList, TreeNodeVo parentNode) {
        Set<String> useUnitCodes = videoEquipList.stream().filter(v -> v.getRegionCode().contains(parentNode.getCode())).map(BaseEnterpriseVideo::getUseUnitCode).collect(Collectors.toSet());
        return baseEnterpriseInfos.stream().filter(v -> useUnitCodes.contains(v.getUseUnitCode())).map(u -> {
            TreeNodeVo treeNodeVo = new TreeNodeVo();
            treeNodeVo.setNodeType(NODE_TYPE_UNIT);
            // 防止id不唯一，拼接为{父id}_{本级id},目前左侧树与右侧列表没交互不定位，如需定位需要按照code进行定位
            treeNodeVo.setId(parentNode.getId() + "_" + u.getSequenceNbr());
            treeNodeVo.setName(u.getUseUnit());
            treeNodeVo.setCode(u.getUseUnitCode());
            treeNodeVo.setParentId(parentNode.getId());
            treeNodeVo.setChildren(this.buildVideoTypeNode(treeNodeVo, channelNoList));
            return treeNodeVo;
        }).collect(Collectors.toList());
    }

    private List<TreeNodeVo> buildVideoTypeNode(TreeNodeVo parentNode, List<CommonVideoDto> channelNoList) {
        return channelNoList.stream().filter(v -> v.getParent().contains(parentNode.getCode())).map(u -> {
            TreeNodeVo treeNodeVo = new TreeNodeVo();
            treeNodeVo.setNodeType(NODE_TYPE_VIDEO);
            // 这个key需要全局唯一，用来页面左侧点击摄像头类型的节点时与右侧的摄像头列表进行定位
            // @see /video/list->CommonVideoDto.key
            treeNodeVo.setId(u.getKey());
            treeNodeVo.setName(u.getLabel());
            treeNodeVo.setParentId(parentNode.getId());
            return treeNodeVo;
        }).collect(Collectors.toList());
    }

    private List<CommonVideoDto> getVideoChannel(List<BaseEnterpriseVideo> videoEquipList) {
        List<CommonVideoDto> commonVideoDtos = new ArrayList<>();
        videoEquipList.forEach(v -> {
            String channelNos = v.getChannelNo();
            List<CommonVideoDto> channelNoList = JSONObject.parseArray(channelNos, CommonVideoDto.class);
            commonVideoDtos.addAll(channelNoList.stream().peek(e -> e.setParent(v.getUseUnitCode())).collect(Collectors.toList()));
        });
        return commonVideoDtos;
    }

    private List<RegionModel> getRegionListOfVideo(List<BaseEnterpriseVideo> videoEquipList) {
        return stCommonService.getRegionModels().parallelStream().filter(r -> videoEquipList.stream().anyMatch(v -> v.getRegionCode().contains(r.getRegionCode().toString()))).collect(Collectors.toList());
    }

    private List<TzBaseEnterpriseInfo> getUnitListForTree(Set<String> useUnitCodes) {
        LambdaQueryWrapper<TzBaseEnterpriseInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(TzBaseEnterpriseInfo::getUseUnitCode, useUnitCodes);
        queryWrapper.select(TzBaseEnterpriseInfo::getUseUnitCode, TzBaseEnterpriseInfo::getUseUnit, TzBaseEnterpriseInfo::getAddress, BaseEntity::getSequenceNbr);
        return tzBaseEnterpriseInfoMapper.selectList(queryWrapper);
    }

    private List<BaseEnterpriseVideo> getVideos(DPFilterParamDto dpFilterParamDto) {
        LambdaQueryWrapper<BaseEnterpriseVideo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BaseEnterpriseVideo::getEquListCode, EquipmentClassifityEnum.KYSD.getCode());
        queryWrapper.like(BaseEnterpriseVideo::getRegionCode, dpFilterParamDto.getCityCode());
        return enterpriseVideoService.list(queryWrapper);
    }


    private List<BaseEnterpriseVideo> getVideos(String regionCode) {
        LambdaQueryWrapper<BaseEnterpriseVideo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BaseEnterpriseVideo::getEquListCode, EquipmentClassifityEnum.KYSD.getCode());
        queryWrapper.like(BaseEnterpriseVideo::getRegionCode, regionCode);
        return enterpriseVideoService.list(queryWrapper);
    }

    public List<UnitVideoDto> getUnitList(String regionCode) {
        // 摄像头设备
        List<BaseEnterpriseVideo> videoEquipList = getVideos(regionCode);
        // 摄像头对应的单位列表
        List<TzBaseEnterpriseInfo> baseEnterpriseInfos = this.getUnitListForTree(videoEquipList.stream().map(BaseEnterpriseVideo::getUseUnitCode).collect(Collectors.toSet()));
        return baseEnterpriseInfos.parallelStream().map(e -> {
            UnitVideoDto unitVideoDto = new UnitVideoDto();
            unitVideoDto.setId(e.getSequenceNbr() + "");
            unitVideoDto.setName(e.getUseUnit());
            unitVideoDto.setCode(e.getUseUnitCode());
            // 摄像头通道
            List<CommonVideoDto> channelNoList = this.getVideoChannel(videoEquipList);
            unitVideoDto.setCountNum(channelNoList.stream().filter(c -> c.getParent().equals(e.getUseUnitCode())).count());
            return unitVideoDto;
        }).collect(Collectors.toList());
    }

    public List<CommonVideoDto> getVideoList(String useUnitCode, String regionCode) {
        return enterpriseVideoService.getUnitVideoUrl(useUnitCode, regionCode);
    }

}
