package com.njcn.advance.service.responsibility.impl;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.afterturn.easypoi.handler.inter.IReadHandler;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.advance.enums.AdvanceResponseEnum;
import com.njcn.advance.mapper.responsibility.RespUserDataMapper;
import com.njcn.advance.pojo.bo.responsibility.DealDataResult;
import com.njcn.advance.pojo.bo.responsibility.DealUserDataResult;
import com.njcn.advance.pojo.bo.responsibility.UserDataExcel;
import com.njcn.advance.pojo.po.responsibility.RespUserData;
import com.njcn.advance.pojo.po.responsibility.RespUserDataIntegrity;
import com.njcn.advance.service.responsibility.IRespUserDataIntegrityService;
import com.njcn.advance.service.responsibility.IRespUserDataService;
import com.njcn.common.pojo.dto.SelectOption;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.utils.FileUtil;
import com.njcn.common.utils.PubUtils;
import com.njcn.db.constant.DbConstant;
import com.njcn.oss.constant.OssPath;
import com.njcn.oss.utils.FileStorageUtil;
import com.njcn.poi.util.PoiUtil;
import com.njcn.web.factory.PageFactory;
import com.njcn.web.pojo.param.BaseParam;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author hongawen
 * @since 2023-07-13
 */
@Service
@RequiredArgsConstructor
public class RespUserDataServiceImpl extends ServiceImpl<RespUserDataMapper, RespUserData> implements IRespUserDataService {

    private final FileStorageUtil fileStorageUtil;

    private final IRespUserDataIntegrityService respUserDataIntegrityService;

    @Override
    public void uploadUserData(MultipartFile file, HttpServletResponse response) {
        ImportParams params = new ImportParams();
        List<UserDataExcel> userDataExcels = new ArrayList<>();
        try {
            ExcelImportUtil.importExcelBySax(file.getInputStream(), UserDataExcel.class, params, new IReadHandler<UserDataExcel>() {
                @Override
                public void handler(UserDataExcel o) {
                    userDataExcels.add(o);
                }

                @Override
                public void doAfterAll() {

                }
            });
            //处理用户上传的用采数据内容
            analysisUserData(userDataExcels, file.getOriginalFilename());
        } catch (Exception e) {
            throw new BusinessException(AdvanceResponseEnum.ANALYSIS_USER_DATA_ERROR);
        }
    }

    @Override
    public Page<RespUserData> userDataList(BaseParam queryParam) {
        QueryWrapper<RespUserData> respUserDataQueryWrapper = new QueryWrapper<>();
        if (ObjectUtil.isNotNull(queryParam)) {
            //查询参数不为空，进行条件填充
            if (StrUtil.isNotBlank(queryParam.getSearchValue())) {
                //仅提供用采名称
                respUserDataQueryWrapper.and(param -> param.like("pqs_resp_user_data.name", queryParam.getSearchValue()));
            }
            //排序
            if (ObjectUtil.isAllNotEmpty(queryParam.getSortBy(), queryParam.getOrderBy())) {
                respUserDataQueryWrapper.orderBy(true, queryParam.getOrderBy().equals(DbConstant.ASC), StrUtil.toUnderlineCase(queryParam.getSortBy()));
            } else {
                //没有排序参数，默认根据sort字段排序，没有排序字段的，根据updateTime更新时间排序
                respUserDataQueryWrapper.orderBy(true, false, "pqs_resp_user_data.update_time");
            }
        } else {
            respUserDataQueryWrapper.orderBy(true, false, "pqs_resp_user_data.update_time");
        }
        respUserDataQueryWrapper.eq("pqs_resp_user_data.state", DataStateEnum.ENABLE.getCode());
        return this.baseMapper.page(new Page<>(PageFactory.getPageNum(queryParam), PageFactory.getPageSize(queryParam)), respUserDataQueryWrapper);
    }

    @Override
    public List<SelectOption> userDataSelect() {
        List<SelectOption> selectOptions = new ArrayList<>();
        LambdaQueryWrapper<RespUserData> respUserDataLambdaQueryWrapper = new LambdaQueryWrapper<>();
        respUserDataLambdaQueryWrapper.eq(RespUserData::getState, DataStateEnum.ENABLE.getCode())
                .orderByDesc(RespUserData::getUpdateTime);
        List<RespUserData> respUserData = this.baseMapper.selectList(respUserDataLambdaQueryWrapper);
        if (CollectionUtil.isNotEmpty(respUserData)) {
            selectOptions = respUserData.stream().map(temp -> new SelectOption(temp.getName(), temp.getId())).collect(Collectors.toList());
        }
        return selectOptions;
    }

    @Override
    public void deleteUserDataByIds(List<String> ids) {
        this.baseMapper.deleteUserDataByIds(ids);
    }

    @Override
    public List<UserDataExcel> getUserDataExcelList(String userDataId) {
        LambdaQueryWrapper<RespUserData> userDataLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userDataLambdaQueryWrapper.eq(RespUserData::getId, userDataId).eq(RespUserData::getState, DataStateEnum.ENABLE.getCode());
        RespUserData respUserData = this.getOne(userDataLambdaQueryWrapper);
        if (Objects.isNull(respUserData)) {
            throw new BusinessException(AdvanceResponseEnum.USER_DATA_NOT_FOUND);
        }
        InputStream fileStream = fileStorageUtil.getFileStream(respUserData.getDataPath());
        String excelDataStr = IoUtil.read(fileStream, CharsetUtil.UTF_8);
        //将文件流转为list集合
        List<UserDataExcel> userDataExcels = JSONArray.parseArray(excelDataStr, UserDataExcel.class);
        if (CollectionUtils.isEmpty(userDataExcels)) {
            throw new BusinessException(AdvanceResponseEnum.USER_DATA_NOT_FOUND);
        }
        return userDataExcels;
    }


    /**
     * 根据流获取出用采有功功率数据
     */
    private void analysisUserData(List<UserDataExcel> userDataExcelList, String fileName) {
        List<UserDataExcel> exportExcelList = new ArrayList<>();
        RespUserData respUserData;
        //判断数据提取情况
        if (CollectionUtils.isEmpty(userDataExcelList)) {
            throw new BusinessException(AdvanceResponseEnum.USER_DATA_EMPTY);
        }
        DealDataResult dealDataResult = getStanderData(userDataExcelList, 0);
        Map<String/*户号@监测点号@户名*/, Map<String/*yyyy-MM-dd天日期*/, Map<Date/*yyyy-MM-dd HH:mm:ss日期格式*/, UserDataExcel>>> totalData = dealDataResult.getTotalData();
        //收集所有的日期，以便获取起始日期和截止日期
        List<String> dates = dealDataResult.getDates();
        //将前面获取出来的日期进行排序，提供入库
        List<LocalDate> resultDates = getSortDate(dates);
        LocalDate endTime = resultDates.get(resultDates.size() - 1);
        LocalDate startTime = resultDates.get(0);
        //针对每个用户的数据进行完整度的判断 todo 暂且认为所有用户的时间跨度是一样的，比如都是15天或者都是30天，不存在有的用户5天数据，有的用户10天数据
        Map<String, List<UserDataExcel>> tempResult = new HashMap<>();
        List<RespUserDataIntegrity> respUserDataIntegrities = new ArrayList<>();
        Set<String> userNames = totalData.keySet();
        for (String name : userNames) {
            Map<String, Map<Date, UserDataExcel>> userDataTemp = totalData.get(name);
            //现在数据拿到了，但是因为是hashkey，所以日期顺序是乱的-->怎么变成有序的呢
            Set<String> times = userDataTemp.keySet();
            //循环日期处理数据
            for (String time : times) {
                DealUserDataResult dealtData = dealUserData(name, userDataTemp.get(time), time, 15);
                List<UserDataExcel> UserDataExcelTemp = dealtData.getCompleted();
                List<UserDataExcel> UserDataExcel;
                if (CollectionUtils.isEmpty(UserDataExcelTemp) && Objects.nonNull(dealtData.getRespUserDataIntegrity())) {
                    //为空，说明补齐操作没有进行，选择填充缺失数据即可
                    respUserDataIntegrities.add(dealtData.getRespUserDataIntegrity());
                    UserDataExcel = dealtData.getLack();
                } else {
                    //填充补齐完整性后的数据
                    UserDataExcel = UserDataExcelTemp;
                }
                List<UserDataExcel> userDatas = tempResult.get(name);
                if (CollectionUtil.isNotEmpty(UserDataExcel)) {
                    if (CollectionUtils.isEmpty(userDatas)) {
                        userDatas = new ArrayList<>(UserDataExcel);
                    } else {
                        userDatas.addAll(UserDataExcel);
                    }
                }
                tempResult.put(name, userDatas);
            }
        }
        //完成后，开始将数据按公司排序，然后输出到指定表格中，方便下次使用
        for (String name : userNames) {
            List<UserDataExcel> tempUserData = tempResult.get(name);
            //按时间排序
            Collections.sort(tempUserData);
            exportExcelList.addAll(tempUserData);
        }
        //输出到报表中
        String fileNameWithOutSuffix = fileName.substring(0, fileName.indexOf('.'));
        fileNameWithOutSuffix = fileNameWithOutSuffix.concat(LocalDateTimeUtil.format(startTime, DatePattern.PURE_DATE_PATTERN)).concat(StrPool.DASHED).concat(LocalDateTimeUtil.format(endTime, DatePattern.PURE_DATE_PATTERN));
        //处理完后的用采数据，生成json文件流到oss服务器
        JSONArray finalUserData = JSONArray.parseArray(JSON.toJSONString(exportExcelList));
        InputStream reportStream = IoUtil.toStream(finalUserData.toString(), CharsetUtil.UTF_8);
        String ossPath = fileStorageUtil.uploadStream(reportStream, OssPath.RESPONSIBILITY_USER_DATA, FileUtil.generateFileName("json"));
        //入库前进行查询操作，存在则更新，不存在则插入
        LambdaQueryWrapper<RespUserData> respUserDataLambdaQueryWrapper = new LambdaQueryWrapper<>();
        respUserDataLambdaQueryWrapper.eq(RespUserData::getName, fileNameWithOutSuffix)
                .eq(RespUserData::getStartTime, startTime)
                .eq(RespUserData::getEndTime, endTime)
                .eq(RespUserData::getState, DataStateEnum.ENABLE.getCode());
        respUserData = this.baseMapper.selectOne(respUserDataLambdaQueryWrapper);
        //不存在则插入
        if (Objects.isNull(respUserData)) {
            respUserData = new RespUserData();
            respUserData.setEndTime(endTime);
            respUserData.setStartTime(startTime);
            respUserData.setName(fileNameWithOutSuffix);
            respUserData.setDataPath(ossPath);
            respUserData.setState(DataStateEnum.ENABLE.getCode());
            this.baseMapper.insert(respUserData);
            if (CollectionUtil.isNotEmpty(respUserDataIntegrities)) {
                //关联插入数据 户号，监测点号，户名，时间，完整性
                for (RespUserDataIntegrity respUserDataIntegrity : respUserDataIntegrities) {
                    respUserDataIntegrity.setUserDataId(respUserData.getId());
                }
                //插入操作
                respUserDataIntegrityService.saveBatch(respUserDataIntegrities);
                respUserData.setIntegrity(1);
            } else {
                respUserData.setIntegrity(0);
            }
            this.baseMapper.updateById(respUserData);
        } else {
            //存在则更新,需要删除之前的oss文件
            fileStorageUtil.deleteFile(respUserData.getDataPath());
            if (CollectionUtil.isNotEmpty(respUserDataIntegrities)) {
                LambdaQueryWrapper<RespUserDataIntegrity> respUserDataIntegrityLambdaQueryWrapper = new LambdaQueryWrapper<>();
                respUserDataIntegrityLambdaQueryWrapper.eq(RespUserDataIntegrity::getUserDataId, respUserData.getId());
                respUserDataIntegrityService.remove(respUserDataIntegrityLambdaQueryWrapper);
                for (RespUserDataIntegrity respUserDataIntegrity : respUserDataIntegrities) {
                    respUserDataIntegrity.setUserDataId(respUserData.getId());
                }
                //插入操作
                respUserDataIntegrityService.saveBatch(respUserDataIntegrities);
                respUserData.setIntegrity(1);
            } else {
                respUserData.setIntegrity(0);
            }
            respUserData.setDataPath(ossPath);
            this.baseMapper.updateById(respUserData);
        }

    }

    /**
     * 解析用采数据为一个标准格式
     */
    public static DealDataResult getStanderData(List<UserDataExcel> userDataExcelBodies, int flag) {
        DealDataResult result = new DealDataResult();
        //收集所有的日期，以便获取起始日期和截止日期
        List<String> dates = new ArrayList<>();
        Map<String, Map<String, Map<Date, UserDataExcel>>> totalData = new HashMap<>();
        Map<String, Map<String, List<UserDataExcel>>> totalListData = new HashMap<>();
        for (UserDataExcel UserDataExcel : userDataExcelBodies) {
            //第一个key
            String key = UserDataExcel.getUserId() + "@" + UserDataExcel.getLine() + "@" + UserDataExcel.getUserName();
            String time = UserDataExcel.getTime().substring(0, 10);
            if (!dates.contains(time)) {
                dates.add(time);
            }
            if (!totalData.containsKey(key)) {
                if (flag == 0) {
                    //Map形式，避免后面补齐数据嵌套循环
                    Map<Date, UserDataExcel> userDatas = new HashMap<>();
                    userDatas.put(PubUtils.getSecondsAsZero(DateUtil.parse(UserDataExcel.getTime(), DatePattern.NORM_DATETIME_PATTERN)), UserDataExcel);
                    Map<String, Map<Date, UserDataExcel>> dataToUserDatas = new HashMap<>();
                    dataToUserDatas.put(time, userDatas);
                    totalData.put(key, dataToUserDatas);
                } else if (flag == 1) {
                    //List形式,避免后面责任数据提取嵌套循环
                    List<UserDataExcel> userListDatas = new ArrayList<>();
                    userListDatas.add(UserDataExcel);
                    Map<String, List<UserDataExcel>> dataToUserListDatas = new HashMap<>();
                    dataToUserListDatas.put(time, userListDatas);
                    totalData.put(key, new HashMap<>());
                    totalListData.put(key, dataToUserListDatas);
                }
            } else {
                if (flag == 0) {
                    //Map形式，避免后面补齐数据嵌套循环
                    Map<String, Map<Date, UserDataExcel>> dataToUserDatas = totalData.get(key);
                    Map<Date, UserDataExcel> userDatas = dataToUserDatas.get(time);
                    //某日凌晨，还没存放该日的数据
                    if (CollectionUtils.isEmpty(userDatas)) {
                        userDatas = new HashMap<>();
                        userDatas.put(PubUtils.getSecondsAsZero(DateUtil.parse(UserDataExcel.getTime(), DatePattern.NORM_DATETIME_PATTERN)), UserDataExcel);
                        dataToUserDatas.put(time, userDatas);
                    } else {
                        //累加该日的数据
                        userDatas.put(PubUtils.getSecondsAsZero(DateUtil.parse(UserDataExcel.getTime(), DatePattern.NORM_DATETIME_PATTERN)), UserDataExcel);
                        dataToUserDatas.put(time, userDatas);
                    }
                    totalData.put(key, dataToUserDatas);
                } else if (flag == 1) {
                    //List形式,避免后面责任数据提取嵌套循环
                    Map<String, List<UserDataExcel>> dataToUserListDatas = totalListData.get(key);
                    List<UserDataExcel> userListDatas = dataToUserListDatas.get(time);
                    if (CollectionUtils.isEmpty(userListDatas)) {
                        userListDatas = new ArrayList<>();
                        userListDatas.add(UserDataExcel);
                        dataToUserListDatas.put(time, userListDatas);
                    } else {
                        userListDatas.add(UserDataExcel);
                        dataToUserListDatas.put(time, userListDatas);
                    }
                    totalListData.put(key, dataToUserListDatas);
                }
            }

        }
        result.setDates(dates);
        result.setTotalData(totalData);
        result.setTotalListData(totalListData);
        return result;
    }

    /**
     * 将日期排序后返回
     */
    private List<LocalDate> getSortDate(List<String> dates) {
        List<LocalDate> result = new ArrayList<>();
        for (String date : dates) {
            LocalDate temp = LocalDateTimeUtil.parseDate(date, DatePattern.NORM_DATE_PATTERN);
            result.add(temp);
        }
        if (!CollectionUtils.isEmpty(result)) {
            Collections.sort(result);
        }
        return result;
    }


    /**
     * 处理用户每日数据
     *
     * @param name       用户名
     * @param beforeDeal 处理前的用户数据
     */
    private DealUserDataResult dealUserData(String name, Map<Date, UserDataExcel> beforeDeal, String time, int step) {
        DealUserDataResult result = new DealUserDataResult();
        String[] userFlag = name.split("@");
        //每天的最开是的数据是从00:00:00开始的，所以起始时间为time + 00:00:00
        List<UserDataExcel> completed = new ArrayList<>();
        List<UserDataExcel> lack = new ArrayList<>();
        if (CollectionUtils.isEmpty(beforeDeal)) {
            return result;
        } else {
            String timeTemp = time + " 00:00:00";
            Date date = DateUtil.parse(timeTemp, DatePattern.NORM_DATETIME_PATTERN);
            int count = 24 * 60 / 15;
            if ((float) beforeDeal.size() / (float) count < 0.9) {
                Set<Date> dates = beforeDeal.keySet();
                for (Date tempDate : dates) {
                    UserDataExcel UserDataExcel = beforeDeal.get(tempDate);
                    if (UserDataExcel.getWork() != null) {
                        lack.add(UserDataExcel);
                    }
                }
                RespUserDataIntegrity respUserDataIntegrity = new RespUserDataIntegrity();
                respUserDataIntegrity.setIntegrity(BigDecimal.valueOf((double) lack.size() / 96.0));
                respUserDataIntegrity.setLackDate(LocalDateTimeUtil.parseDate(time, DatePattern.NORM_DATE_PATTERN));
                respUserDataIntegrity.setUserName(userFlag[2]);
                respUserDataIntegrity.setLineNo(userFlag[1]);
                respUserDataIntegrity.setUserNo(userFlag[0]);
                result.setLack(lack);
                result.setRespUserDataIntegrity(respUserDataIntegrity);
                return result;
            } else {
                for (int i = 0; i < count; i++) {
                    Calendar calendar = Calendar.getInstance();
                    calendar.setTime(date);
                    calendar.add(Calendar.MINUTE, step * i);
                    UserDataExcel UserDataExcel = beforeDeal.get(calendar.getTime());
                    if (UserDataExcel != null && UserDataExcel.getWork() != null) {
                        completed.add(UserDataExcel);
                    } else {
                        //找到前一个时间点值
                        Float perValue = getPreValue(date, calendar.getTime(), beforeDeal);
                        //找到后一个时间点值
                        Float appendValue = getAppendValue(date, count, step, calendar.getTime(), beforeDeal);
                        UserDataExcel temp = new UserDataExcel();
                        SimpleDateFormat sdf = new SimpleDateFormat(DatePattern.NORM_DATETIME_PATTERN);
                        temp.setTime(sdf.format(calendar.getTime()));
                        temp.setUserId(userFlag[0]);
                        temp.setLine(userFlag[1]);
                        temp.setUserName(userFlag[2]);
                        //还需要判断前值和后值为空的情况
                        if (null == perValue && null == appendValue) {
                            temp.setWork(new BigDecimal("0.0"));
                        } else if (null == perValue) {
                            temp.setWork(new BigDecimal(appendValue));
                        } else if (null == appendValue) {
                            temp.setWork(new BigDecimal(perValue));
                        } else {
                            temp.setWork(BigDecimal.valueOf((perValue + appendValue) / 2));
                        }
                        completed.add(temp);
                    }
                }
            }
        }
        result.setCompleted(completed);
        return result;
    }

    /**
     * 递归找前值
     *
     * @param date       起始时间
     * @param time       当前时间
     * @param beforeDeal 处理前的数据
     */
    private Float getPreValue(Date date, Date time, Map<Date, UserDataExcel> beforeDeal) {
        Float result;
        if (date.equals(time)) {
            return null;
        } else {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(time);
            calendar.add(Calendar.MINUTE, -15);
            UserDataExcel temp = beforeDeal.get(calendar.getTime());
            if (temp == null || temp.getWork() == null) {
                result = getPreValue(date, calendar.getTime(), beforeDeal);
            } else {
                result = temp.getWork().floatValue();
            }
        }
        return result;
    }

    /**
     * 递归找后置
     *
     * @param date  起始时间
     * @param count 一天时间的总计数
     * @param step  间隔分钟
     * @param time  截止时间
     */
    private Float getAppendValue(Date date, int count, int step, Date time, Map<Date, UserDataExcel> beforeDeal) {
        Float result;
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.MINUTE, (count - 1) * step);
        if (time.equals(calendar.getTime())) {
            return null;
        } else {
            Calendar calendar1 = Calendar.getInstance();
            calendar1.setTime(time);
            calendar1.add(Calendar.MINUTE, 15);
            UserDataExcel temp = beforeDeal.get(calendar1.getTime());
            if (temp == null || temp.getWork() == null) {
                result = getAppendValue(date, count, step, calendar1.getTime(), beforeDeal);
            } else {
                result = temp.getWork().floatValue();
            }
        }
        return result;
    }

}
