package com.njcn.device.terminal.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.njcn.common.pojo.dto.SimpleDTO;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.device.common.mapper.SuperDataMapper;
import com.njcn.device.common.service.GeneralDeviceService;
import com.njcn.device.device.mapper.DevFuctionMapper;
import com.njcn.device.device.service.DeviceProcessService;
import com.njcn.device.device.service.IDevMealService;
import com.njcn.device.device.service.IDevStrategyService;
import com.njcn.device.device.service.IDeviceService;
import com.njcn.device.line.service.LineService;
import com.njcn.device.pq.constant.Param;
import com.njcn.device.pq.enums.LineBaseEnum;
import com.njcn.device.pq.pojo.dto.GeneralDeviceDTO;
import com.njcn.device.pq.pojo.param.*;
import com.njcn.device.pq.pojo.po.*;
import com.njcn.device.pq.pojo.vo.LineFlowMealDetailVO;
import com.njcn.device.pq.pojo.vo.TerminalDaliyFlowVO;
import com.njcn.device.pq.pojo.vo.TerminalMaintainVO;
import com.njcn.device.terminal.mapper.TerminalMaintainMapper;
import com.njcn.device.terminal.service.IPqsTerminalLogsService;
import com.njcn.device.terminal.service.TerminalMaintainService;
import com.njcn.influx.constant.InfluxDbSqlConstant;
import com.njcn.influx.imapper.CldStatisticsFlowMapper;
import com.njcn.influx.pojo.dto.DeviceDayFlowDTO;
import com.njcn.influx.pojo.po.CldStatisFlow;
import com.njcn.influx.query.InfluxQueryWrapper;
import com.njcn.system.api.DicDataFeignClient;
import com.njcn.system.enums.DicDataEnum;
import com.njcn.system.pojo.enums.StatisticsEnum;
import com.njcn.system.pojo.po.DictData;
import com.njcn.user.api.DeptFeignClient;
import com.njcn.web.utils.RequestUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * pqs
 * 终端运维
 *
 * @author cdf
 * @date 2022/5/11
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TerminalMaintainServiceImpl implements TerminalMaintainService {

    private final TerminalMaintainMapper terminalMaintainMapper;

    private final IDeviceService iDeviceService;

    private final IDevMealService iDevMealService;

    private final IDevStrategyService iDevStrategyService;

    private final GeneralDeviceService generalDeviceService;

    private final DeptFeignClient deptFeignClient;

    private final SuperDataMapper superDataMapper;

    private final DicDataFeignClient dicDataFeignClient;

    private final IPqsTerminalLogsService iPqsTerminalLogsService;

    private final DevFuctionMapper devFuctionMapper;

    private final CldStatisticsFlowMapper cldStatisticsFlowMapper;
    private final LineService lineService;
    private final DeviceProcessService deviceProcessService;

    @Override
    public List<TerminalMaintainVO> getTerminalMainList(TerminalMainQueryParam terminalMainQueryParam) {
        List<TerminalMaintainVO> resList = new ArrayList<>();
        DeviceInfoParam deviceInfoParam = new DeviceInfoParam();
        deviceInfoParam.setLineGrade(terminalMainQueryParam.getLineGrade());
        deviceInfoParam.setDeptIndex(deptFeignClient.getRootDept().getData().getId());
        SimpleDTO simpleDTO = new SimpleDTO();
        simpleDTO.setCode(StatisticsEnum.POWER_NETWORK.getCode());
        deviceInfoParam.setStatisticalType(simpleDTO);
        List<GeneralDeviceDTO> generalDeviceDTOList = generalDeviceService.getDeviceInfo(deviceInfoParam, Objects.isNull(terminalMainQueryParam.getRunFlag()) ? null : CollUtil.newArrayList(terminalMainQueryParam.getRunFlag()), CollUtil.newArrayList(1));
        if (CollectionUtil.isNotEmpty(generalDeviceDTOList)) {
            for (GeneralDeviceDTO generalDeviceDTO : generalDeviceDTOList) {
                List<String> devIds = generalDeviceDTO.getDeviceIndexes();
                if (CollectionUtils.isEmpty(devIds)) {
                    continue;
                }
                TerminalMaintainVO terminalMaintainVO = new TerminalMaintainVO();
                terminalMaintainVO.setName(generalDeviceDTO.getName());
                terminalMaintainVO.setId(generalDeviceDTO.getIndex());
                terminalMaintainVO.setLevel(1);
                List<TerminalMaintainVO> devList = terminalMaintainMapper.terminalConfig(devIds,terminalMainQueryParam.getComFlag(),terminalMainQueryParam.getDevType());
                if (CollectionUtil.isEmpty(devList)) {
                    continue;
                }



                List<String> subIndexes = devList.stream().map(TerminalMaintainVO::getPid).collect(Collectors.toList());
                List<TerminalMaintainVO> subList1 = terminalMaintainMapper.getPqLineGdAndSubList(subIndexes);
                List<String> gdIndexes = subList1.stream().map(TerminalMaintainVO::getPid).collect(Collectors.toList());
                List<TerminalMaintainVO> gdList1 = terminalMaintainMapper.getPqLineGdAndSubList(gdIndexes);
                dealTerminalData(subList1, devList);
                dealTerminalData(gdList1, subList1);
                terminalMaintainVO.setChildren(gdList1);
                resList.add(terminalMaintainVO);
            }
        }
        return resList;
    }

    List<TerminalMaintainVO> dealTerminalData(List<TerminalMaintainVO> parent, List<TerminalMaintainVO> children) {
        return parent.stream().peek(item -> item.setChildren(getChildren(item.getId(), children))).collect(Collectors.toList());
    }

    @Override
    public List<TerminalMaintainVO> getManageList(List<String> ids, Integer type) {
        if (type == 0) {
            //终端状态管理
            List<TerminalMaintainVO> devList = terminalMaintainMapper.getRunFlagList(ids);
            return assemble(devList);
        } else if (type == 1) {
            //流量套餐
            List<TerminalMaintainVO> devList = terminalMaintainMapper.getFlowMealList(ids);
            return assemble(devList);
        } else if (type == 2) {
            //流量策略
            List<TerminalMaintainVO> devList = terminalMaintainMapper.getFlowStrategyList(ids);
            return assemble(devList);
        }
        throw new BusinessException(CommonResponseEnum.FAIL);
    }

    @Override
    public boolean updateRunFlagManage(TerminalParam terminalParam) {
        List<String> devIds = terminalParam.getIds();
        if(CollectionUtils.isEmpty(devIds)){
            return true;
        }
        QueryWrapper<Device> wrapper = new QueryWrapper<>();
        wrapper.in("id", devIds);
        List<Device> newList = iDeviceService.listByIds(terminalParam.getIds());
        boolean b = iDeviceService.update(new LambdaUpdateWrapper<Device>()
                        .set(Device::getRunFlag,terminalParam.getRunFlag())
                        .in(Device::getId,terminalParam.getIds()));
        devIds.forEach(id -> {lineService.updateLineRunFlag(id,terminalParam.getRunFlag());});
        //添加日志信息
        if (b) {
            List<Device> oldList = iDeviceService.list(wrapper);
            for (Device device : newList) {
                for (Device device1 : oldList) {
                    //如果设备id相同,终端运行状态不同
                    if (Objects.equals(device1.getId(), device.getId()) && !Objects.equals(device1.getRunFlag(), device.getRunFlag())) {
                        //获取用户信息
                        String userName = RequestUtil.getUsername();
                        String index = RequestUtil.getUserIndex();
                        //String userName = "zbj";
                        //String index = "1234567";
                        //获取上级信息
                        SuperDataParam superDataParam = superDataMapper.getDeviceSuper(device.getId());
                        StringBuilder sb = new StringBuilder();
                        sb.append(userName).append("修改了").append(superDataParam.getUrbanName()).append(" - ")
                                .append(superDataParam.getSubName()).append(" - ").append(superDataParam.getDevName()).append(" - 设备的参数;");

                        StringBuilder sbNew = new StringBuilder();
                        StringBuilder sbOld = new StringBuilder();
                        sbNew.append("操作历史,");
                        sbOld.append("修改后参数,");
                        String newFlag = "";
                        String oldFlag = "";
                        if (device.getRunFlag() == 0) {
                            newFlag = "投运";
                        } else if (device.getRunFlag() == 1) {
                            newFlag = "检修";
                        } else if (device.getRunFlag() == 2) {
                            newFlag = "停运";
                        }
                        if (device1.getRunFlag() == 0) {
                            oldFlag = "投运";
                        } else if (device1.getRunFlag() == 1) {
                            oldFlag = "检修";
                        } else if (device1.getRunFlag() == 2) {
                            oldFlag = "停运";
                        }
                        sbNew.append("终端运行状态: ").append(newFlag).append(";");
                        sbOld.append("终端运行状态: ").append(oldFlag).append(";");
                        sb.append(sbNew).append(sbOld);
                        HttpResult<DictData> dicDataByCode = dicDataFeignClient.getDicDataByCode(DicDataEnum.DEV_PARAMETER.getCode());
                        DictData data = dicDataByCode.getData();
                        //创建对象
                        PqsTerminalLogs terminalLogsNew = new PqsTerminalLogs();
                        terminalLogsNew.setObjIndex(device.getId());
                        terminalLogsNew.setTerminalDescribe(sb.toString());
                        terminalLogsNew.setIsPush(0);
                        if(device.getRunFlag() == 0&&device1.getRunFlag()!=0){
                            terminalLogsNew.setOperateType(Param.DEL);

                        }
                        if(device1.getRunFlag() == 0&&device.getRunFlag()!=0){
                            terminalLogsNew.setOperateType(Param.ADD);
                            //判断device是否绑定进程号；
                            DeviceProcess deviceProcess = new DeviceProcess();
                            deviceProcess.setId(device1.getId());
                            deviceProcess.setProcessNo(1);

                            deviceProcessService.saveOrUpdate(deviceProcess);
                        }
                        terminalLogsNew.setLogsType(data.getId());
                        terminalLogsNew.setTerminalType(LineBaseEnum.DEVICE_LEVEL.getCode());
                        terminalLogsNew.setState(DataStateEnum.ENABLE.getCode());
                        iPqsTerminalLogsService.save(terminalLogsNew);
                    }
                }
            }
        }
        return b;
    }

    @Override
    public boolean updateFlowMeal(TerminalParam terminalParam) {
        List<DevMeal> tem = new ArrayList<>();
        //修改前
        List<DevMeal> newDevmeal = iDevMealService.listByIds(terminalParam.getIds());
        iDevMealService.removeByIds(terminalParam.getIds());
        terminalParam.getIds().forEach(item -> {
            DevMeal devMeal = new DevMeal();
            devMeal.setLineId(item);
            devMeal.setBaseMealId(terminalParam.getBaseMealId());
            devMeal.setReamMealId(terminalParam.getExtendMealId());
            tem.add(devMeal);
        });
        boolean b = iDevMealService.saveBatch(tem);
        if (b) {
            //修改后
            List<DevMeal> oldDevmeal = iDevMealService.listByIds(terminalParam.getIds());
            for (DevMeal newMeal : newDevmeal) {
                for (DevMeal oldMeal : oldDevmeal) {
                    if (Objects.equals(newMeal.getLineId(),oldMeal.getLineId())){
                        if (!Objects.equals(newMeal.getBaseMealId(),oldMeal.getBaseMealId())||!Objects.equals(newMeal.getReamMealId(),oldMeal.getReamMealId())){
                            //获取用户信息
                            String userName = RequestUtil.getUsername();
                            String index = RequestUtil.getUserIndex();
                            //String userName = "zbj";
                            //String index = "1234567";
                            BaseReamParam baseReam = superDataMapper.getBaseReam(newMeal.getLineId());
                            SuperDataParam superDataParam = superDataMapper.getDeviceSuper(newMeal.getLineId());
                            if (Objects.nonNull(superDataParam.getUrbanName())&&Objects.nonNull(superDataParam.getSubName())&&Objects.nonNull(superDataParam.getDevName())) {
                                StringBuilder sb = new StringBuilder();
                                sb.append(userName).append("将").append(superDataParam.getUrbanName()).append(" 下的 ")
                                        .append(superDataParam.getSubName()).append(" 下的 ").append(superDataParam.getDevName())
                                        .append(" 基础流量套餐设置为: ").append(baseReam.getBaseMealFlow()).append(";").append(" 扩展流量套餐设置为: ")
                                        .append(baseReam.getExtendMealFlow());

                                HttpResult<DictData> dicDataByCode = dicDataFeignClient.getDicDataByCode(DicDataEnum.DATA_PLAN.getCode());
                                DictData data = dicDataByCode.getData();
                                //创建对象
                                PqsTerminalLogs terminalLogsNew = new PqsTerminalLogs();
                                terminalLogsNew.setObjIndex(newMeal.getLineId());
                                terminalLogsNew.setTerminalDescribe(sb.toString());
                                terminalLogsNew.setCreateBy(index);
                                terminalLogsNew.setCreateTime(LocalDateTime.now());
                                terminalLogsNew.setTerminalType(LineBaseEnum.LINE_LEVEL.getCode());
                                terminalLogsNew.setLogsType(data.getId());
                                terminalLogsNew.setState(1);
                                iPqsTerminalLogsService.save(terminalLogsNew);
                            }
                        }
                    }
                }
            }
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean updateFlowStrategy(TerminalParam terminalParam) {
        List<DevStrategy> tem = new ArrayList<>();
        iDevStrategyService.removeByIds(terminalParam.getIds());
        terminalParam.getIds().forEach(item -> {
            DevStrategy devStrategy = new DevStrategy();
            devStrategy.setLineId(item);
            devStrategy.setStrategyId(terminalParam.getFlowStrategyId());
            tem.add(devStrategy);
        });
        return iDevStrategyService.saveBatch(tem);
    }

    @Override
    public List<TerminalDaliyFlowVO> getFlowManageDetail(TerminalMainQueryParam param) {
        List<TerminalDaliyFlowVO> list = new ArrayList<>();
        TerminalDaliyFlowVO flowManageDetail = terminalMaintainMapper.getFlowManageDetail(param.getDevId());
        QueryWrapper<DevFuction> funQueryWrapper = new QueryWrapper<>();
        funQueryWrapper.lambda().eq(DevFuction::getLineId,param.getDevId());
        List<DevFuction> functions = devFuctionMapper.selectList(funQueryWrapper);
        flowManageDetail.setFunctions(functions);
        List<DeviceDayFlowDTO> deviceDayFlowList = getFlowManageDetailFromInfluxDB(param);
        float sum = (float) deviceDayFlowList.stream().mapToDouble(DeviceDayFlowDTO::getDailyActualValue).sum();
        flowManageDetail.setDeviceDayFlowsList(deviceDayFlowList);
        flowManageDetail.setActualValue(sum);
        list.add(flowManageDetail);
        list.forEach(terminalDaliyFlowVO -> terminalDaliyFlowVO.setMonth(param.getSearchBeginTime().substring(6, 7) + "月"));
        return list;
    }

    @Override
    public List<LineFlowMealDetailVO> getMonthFlow(PqsParam deviceInfoParam) {
        List<String> deviceIndexes = new ArrayList<>();
        List<GeneralDeviceDTO> deviceInfoList = generalDeviceService.getDeviceInfo(deviceInfoParam, null, Stream.of(1).collect(Collectors.toList()));
        deviceInfoList.stream().map(GeneralDeviceDTO::getDeviceIndexes).forEach(deviceIndexes::addAll);
        List<LineFlowMealDetailVO> list = terminalMaintainMapper.getMonthFlow(deviceIndexes, DateUtil.beginOfDay(DateUtil.parse(deviceInfoParam.getSearchBeginTime())), DateUtil.endOfDay(DateUtil.parse(deviceInfoParam.getSearchEndTime())));
        return list;
    }

    private List<DeviceDayFlowDTO> getFlowManageDetailFromInfluxDB(TerminalMainQueryParam param) {
        InfluxQueryWrapper influxQueryWrapper = new InfluxQueryWrapper(CldStatisFlow.class,DeviceDayFlowDTO.class);
        influxQueryWrapper.between(InfluxDbSqlConstant.TIME,DateUtil.beginOfDay(DateUtil.parse(param.getSearchBeginTime())).toString(),DateUtil.beginOfDay(DateUtil.parse(param.getSearchEndTime())).toString())
                        .eq(CldStatisFlow::getDevId,param.getDevId());
        List<DeviceDayFlowDTO> deviceDayFlow = cldStatisticsFlowMapper.selectDeviceDayFlowByQueryWrapper(influxQueryWrapper);
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        deviceDayFlow.forEach(t -> t.setDate(formatter.format(Date.from(t.getTime()))));
        Map<String, List<DeviceDayFlowDTO>> dayFlowMap = deviceDayFlow.stream().collect(Collectors.groupingBy(DeviceDayFlowDTO::getDate));
        List<DeviceDayFlowDTO> list = new ArrayList<>();
        for (Map.Entry<String, List<DeviceDayFlowDTO>> entry : dayFlowMap.entrySet()) {
            DeviceDayFlowDTO dayFlow = new DeviceDayFlowDTO();
            dayFlow.setDevId(entry.getValue().get(0).getDevId());
            BigDecimal bigDecimal = new BigDecimal((float) entry.getValue().stream().mapToInt(DeviceDayFlowDTO::getActualValue).sum() / (float) 1024 / (float) 1024);
            dayFlow.setDailyActualValue(bigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP).floatValue());
            dayFlow.setDate(entry.getKey());
            list.add(dayFlow);
        }
        list = list.stream().sorted(Comparator.comparing(DeviceDayFlowDTO::getDate)).collect(Collectors.toList());
        return list;
    }

    private List<TerminalMaintainVO> getChildren(String id, List<TerminalMaintainVO> all) {
        return all.stream().filter(item -> item.getPid().equals(id)).collect(Collectors.toList());
    }

    private List<TerminalMaintainVO> assemble(List<TerminalMaintainVO> devList) {
        List<String> subIds = devList.stream().map(TerminalMaintainVO::getPid).collect(Collectors.toList());
        List<TerminalMaintainVO> subList = terminalMaintainMapper.getPqLineGdAndSubList(subIds);
        List<String> gdIds = subList.stream().map(TerminalMaintainVO::getPid).collect(Collectors.toList());
        List<TerminalMaintainVO> gdList = terminalMaintainMapper.getPqLineGdAndSubList(gdIds);
        List<String> proIds = gdList.stream().map(TerminalMaintainVO::getPid).collect(Collectors.toList());
        List<TerminalMaintainVO> provList = terminalMaintainMapper.getPqLineProvList(proIds);

        List<TerminalMaintainVO> subTem = subList.stream().peek(item -> item.setChildren(getChildren(item.getId(), devList))).collect(Collectors.toList());
        List<TerminalMaintainVO> gdTem = gdList.stream().peek(item -> item.setChildren(getChildren(item.getId(), subTem))).collect(Collectors.toList());
        return provList.stream().peek(item -> item.setChildren(getChildren(item.getId(), gdTem))).collect(Collectors.toList());
    }


}
