package com.njcn.harmonic.service.upload.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.StrBuilder;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.harmonic.mapper.upload.RUploadPointStatisticalDataDMapper;
import com.njcn.harmonic.pojo.dto.upload.MonitorStatisticalDTO;
import com.njcn.harmonic.pojo.param.UploadDataParam;
import com.njcn.harmonic.pojo.param.UploadParam;
import com.njcn.harmonic.pojo.po.upload.RUploadDataLog;
import com.njcn.harmonic.pojo.po.upload.RUploadPointStatisticalDataD;
import com.njcn.harmonic.pojo.vo.upload.UploadPointStatisticalDataVo;
import com.njcn.harmonic.service.upload.IPointStatisticalDataService;
import com.njcn.harmonic.service.upload.IRUploadDataLogService;
import com.njcn.system.api.DicDataFeignClient;
import com.njcn.system.enums.DicDataTypeEnum;
import com.njcn.system.pojo.po.DictData;
import com.njcn.user.api.DeptFeignClient;
import com.njcn.user.pojo.po.Dept;
import com.njcn.web.enums.GWSendEnum;
import com.njcn.web.factory.PageFactory;
import com.njcn.web.pojo.param.SendParam;
import com.njcn.web.utils.GwSendUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author xuyang
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class PointStatisticalDataServiceImpl extends ServiceImpl<RUploadPointStatisticalDataDMapper, RUploadPointStatisticalDataD> implements IPointStatisticalDataService {

    private final DeptFeignClient deptFeignClient;

    private final DicDataFeignClient dicDataFeignClient;

    private final IRUploadDataLogService irUploadDataLogService;

    @Override
    public Page<UploadPointStatisticalDataVo> getPointStatisticalData(UploadDataParam param) {
        LambdaQueryWrapper<RUploadPointStatisticalDataD> lambdaQueryWrapper = getDeptLevel(param);
        //默认按照省名称排序
        lambdaQueryWrapper.orderByDesc(RUploadPointStatisticalDataD::getProvinceName);
        Page<RUploadPointStatisticalDataD> page = this.page(new Page<>(PageFactory.getPageNum(param), PageFactory.getPageSize(param)), lambdaQueryWrapper);
        List<UploadPointStatisticalDataVo> voList = BeanUtil.copyToList(page.getRecords(), UploadPointStatisticalDataVo.class);

        Page<UploadPointStatisticalDataVo> pageVo = new Page<>();
        pageVo.setTotal(page.getTotal());
        pageVo.setPages(page.getPages());
        pageVo.setSize(page.getSize());
        pageVo.setRecords(voList);
        pageVo.setCurrent(page.getCurrent());
        return pageVo;
    }

    @Override
    public String uploadPointStatisticalData(UploadParam param) {
        LocalDate localDate = LocalDate.parse(param.getTime(), DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN));

        List<DictData> dictDataList = dicDataFeignClient.getDicDataByTypeCode(DicDataTypeEnum.Station_Type.getCode()).getData();
        Map<String, DictData> dictDataMap = dictDataList.stream().collect(Collectors.toMap(DictData::getId, Function.identity()));

        List<RUploadPointStatisticalDataD> list;
        //指定数据上送,不管之前有没有上送,都重新上送一次
        //(预防之前上送过,修改数据后需要再次上送)
        if (CollUtil.isNotEmpty(param.getList())){
            list = this.lambdaQuery()
                    .eq(RUploadPointStatisticalDataD::getComputeDate,param.getTime())
                    .in(RUploadPointStatisticalDataD::getId,param.getList())
                    .list();
        }
        //未指定数据上送,则上送省级数据
        else {
            list = this.lambdaQuery()
                    .eq(RUploadPointStatisticalDataD::getComputeDate,param.getTime())
                    .in(RUploadPointStatisticalDataD::getStatisticalLevel, Stream.of("3","4","5").collect(Collectors.toList()))
                    .list();
        }

        list = list.stream().peek(item->item.setStationType(dictDataMap.get(item.getStationType()).getValue())).collect(Collectors.toList());
        List<List<RUploadPointStatisticalDataD>> partition = ListUtils.partition(list, 50);
        StrBuilder resultLog = new StrBuilder();
        AtomicBoolean resultFlag = new AtomicBoolean(true);
        //分片上传
        for (int i = 0; i < partition.size(); i++) {

            List<MonitorStatisticalDTO> dtoList = BeanUtil.copyToList(partition.get(i), MonitorStatisticalDTO.class);
            SendParam sp = new SendParam();
            sp.setStats(dtoList);
            sp.setStatisticalDate(partition.get(i).get(0).getComputeDate());
            if(i == 0){
                sp.setIsAppend("0");
            }else if(i==partition.size()-1){
                sp.setIsAppend("2");
            }else {
                sp.setIsAppend("1");
            }
            //上送数据
            Map<String, String> send = GwSendUtil.send(sp, GWSendEnum.STATISTICAL_CREATE);
            //获取返回结果
            List<String> l = dtoList.stream().map(MonitorStatisticalDTO::getId).collect(Collectors.toList());
            returnInformation(i,l,send,resultLog,resultFlag,localDate);
        }

        if(resultFlag.get()){
            resultLog.append(" 上送" + list.size()+ "条数据成功;");
            assUploadLog(localDate,resultLog,resultFlag);
        }

        return "上送成功:成功数据" + list.size() + "条";
    }

    /**
     * 获取当前单位的层级
     */
    public LambdaQueryWrapper<RUploadPointStatisticalDataD> getDeptLevel(UploadDataParam param) {
        Dept dept = deptFeignClient.getDeptByCode(param.getOrgId()).getData();
        List<String> list = Arrays.stream(dept.getPids().split(",")).map(String::trim).collect(Collectors.toList());
        LambdaQueryWrapper<RUploadPointStatisticalDataD> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        String beginMonth = formatBeginMonth(param.getSearchBeginTime(), param.getDataType());
        switch (list.size()) {
            //国
            case 1:
                String orgId = getOrgIdForProvince(dept);
                addFiltersForProvince(lambdaQueryWrapper, orgId, beginMonth, param.getSearchState());
                break;
            //省
            case 2:
                addFilters(lambdaQueryWrapper, RUploadPointStatisticalDataD::getProvinceId, param.getOrgId(), beginMonth, param.getSearchState());
                break;
            //市
            case 3:
                addFilters(lambdaQueryWrapper, RUploadPointStatisticalDataD::getCityId, param.getOrgId(), beginMonth, param.getSearchState());
                break;
            //县
            case 4:
                addFilters(lambdaQueryWrapper, RUploadPointStatisticalDataD::getCountyId, param.getOrgId(), beginMonth, param.getSearchState());
                break;
            default:
                break;
        }
        return lambdaQueryWrapper;
    }

    private String formatBeginMonth(String searchBeginTime, String dataType) {
        if (Objects.equals(dataType, "02")) {
            return DateUtil.format(DateUtil.beginOfMonth(DateUtil.parse(searchBeginTime, DatePattern.NORM_MONTH_PATTERN)), DatePattern.NORM_MONTH_PATTERN);
        } else if (Objects.equals(dataType, "01")){
            return DateUtil.format(DateUtil.beginOfMonth(DateUtil.parse(searchBeginTime, DatePattern.NORM_YEAR_PATTERN)), DatePattern.NORM_YEAR_PATTERN);
        }
        return searchBeginTime;
    }

    private void addFiltersForProvince(LambdaQueryWrapper<RUploadPointStatisticalDataD> lambdaQueryWrapper, String orgId, String beginMonth, Integer searchState) {
        lambdaQueryWrapper.eq(RUploadPointStatisticalDataD::getProvinceId, orgId)
                .eq(RUploadPointStatisticalDataD::getStatisticalDate, beginMonth)
                .eq(!Objects.isNull(searchState), RUploadPointStatisticalDataD::getUploadStatus, searchState);
    }

    private void addFilters(LambdaQueryWrapper<RUploadPointStatisticalDataD> lambdaQueryWrapper, SFunction<RUploadPointStatisticalDataD, Object> idFunction, String orgId, String beginMonth, Integer searchState) {
        lambdaQueryWrapper.eq(idFunction, orgId)
                .eq(RUploadPointStatisticalDataD::getStatisticalDate, beginMonth)
                .eq(!Objects.isNull(searchState), RUploadPointStatisticalDataD::getUploadStatus, searchState);
    }

    private String getOrgIdForProvince(Dept dept) {
        List<Dept> l1 = deptFeignClient.getDirectSonSelf(dept.getId()).getData();
        return l1.stream()
                .filter(o -> !Objects.equals(o.getId(), dept.getId()))
                .findFirst()
                .map(Dept::getCode)
                .orElse(null);
    }

    /**
     * 国网上送返回信息
     *
     * @param step  分片上传当前阶段
     * @param ids   分片数据
     * @param send  上送结果
     * @return
     */
    private void returnInformation(Integer step, List<String> ids, Map<String, String> send, StrBuilder resultLog, AtomicBoolean resultFlag, LocalDate localDate) {
        int start = step * 100;
        int end = (step+1) * 100;
        if (send.containsKey("succeed")) {
            String succeed = send.get("succeed");
            if (succeed.contains("\\\"")) {
                succeed = succeed.replace("\\\"", "\"");
            }
            Map mapData = JSON.parseObject(succeed, Map.class);
            String status = mapData.get("status").toString();
            if ("000000".equals(status)) {
                //修改数据上送状态
                updateState(ids);
                String result = mapData.get("result").toString();
                Map mapCount = JSON.parseObject(result, Map.class);
                String count = mapCount.get("count").toString();

            } else {
                resultFlag.set(false);
                String errors = mapData.get("errors").toString();
                String errorMsg = " 上送" + start + "条至" + end + "条数据,上送失败:" + status + "_" + errors+" ;";
                resultLog.append(errorMsg);
                assUploadLog(localDate,resultLog,resultFlag);
                throw new BusinessException(errorMsg);
            }
        } else {
            resultFlag.set(false);
            resultLog.append(" 上送" + start + "条至" + end + "条数据网络异常;");
            assUploadLog(localDate,resultLog,resultFlag);
            throw new BusinessException("当前时间段国网上送请求过多，请稍后再试");
        }
    }


    private void assUploadLog(LocalDate statisticDate,StrBuilder msg,AtomicBoolean resultFlag){
        RUploadDataLog rUploadDataLog = new RUploadDataLog();
        rUploadDataLog.setStatisticDate(statisticDate);
        rUploadDataLog.setBigType(0);
        rUploadDataLog.setSmallType(1);
        rUploadDataLog.setCreateTime(LocalDateTime.now());
        rUploadDataLog.setResult(resultFlag.get()?1:0);
        rUploadDataLog.setResultMsg(msg.toString());
        irUploadDataLogService.saveOrUpdateByMultiId(rUploadDataLog);
    }
    /**
     * 修改上送信息状态
     * @param ids id集合
     */
    private void updateState(List<String> ids) {
        LambdaUpdateWrapper<RUploadPointStatisticalDataD> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.in(RUploadPointStatisticalDataD::getId, ids)
                .set(RUploadPointStatisticalDataD::getUploadStatus, 1);
        this.update(lambdaUpdateWrapper);
    }

}
