package com.jiayue.passback.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jiayue.passback.constants.CommonStant;
import com.jiayue.passback.entity.ForecastPowerShortTerm;
import com.jiayue.passback.entity.ForecastPowerUltraShortTerm;
import com.jiayue.passback.entity.SysParameter;
import com.jiayue.passback.entity.UploadFileLog;
import com.jiayue.passback.entity.eunm.PredictionModelEnum;
import com.jiayue.passback.mapper.ForecastPowerShortTermMapper;
import com.jiayue.passback.mapper.ForecastPowerUltraShortTermMapper;
import com.jiayue.passback.mapper.SystemParameterMapper;
import com.jiayue.passback.mapper.UploadFileLogMapper;
import com.jiayue.passback.service.ForecastPowerShortTermService;
import com.jiayue.passback.service.ForecastPowerUltraShortTermService;
import com.jiayue.passback.service.InApiService;
import com.jiayue.passback.service.UploadFileLogService;
import com.jiayue.passback.util.CacheUtil;
import com.jiayue.passback.util.JsonResultUtil;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import net.jodah.expiringmap.ExpirationPolicy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 预测数据业务实现
 *
 * @author yh
 * @version 1.0
 * @since 2022/5/22 11:29
 */
@Slf4j
@Service
public class InApiServiceImpl implements InApiService {
    @Resource
    private SystemParameterMapper systemParameterMapper;
    @Resource
    private ForecastPowerShortTermMapper forecastPowerShortTermMapper;
    @Resource
    private ForecastPowerShortTermService forecastPowerShortTermService;
    @Resource
    private ForecastPowerUltraShortTermMapper forecastPowerUltraShortTermMapper;
    @Resource
    private ForecastPowerUltraShortTermService forecastPowerUltraShortTermService;
    @Resource
    private UploadFileLogService uploadFileLogService;

    /**
     * 获取token
     *
     * @param userInfo
     * @return
     */
    @Override
    public JSONObject getToken(JSONObject userInfo) {
        CacheUtil.cacheMap.entrySet().forEach(entry -> log.info("==========user:" + entry.getKey() + "token" + entry.getValue() + "=========="));
        JSONObject result;
        //获取用户名密码
        String requestUserName = userInfo.getStr(CommonStant.REQUEST_USER_NAME);
        String requestUserPass = userInfo.getStr(CommonStant.REQUEST_USER_PASS);
        // 设置参数
        QueryWrapper<SysParameter> wrapper = new QueryWrapper();
        wrapper.eq("C_SYS_KEY", "TOKEN_USER");
        SysParameter systemParameter = systemParameterMapper.selectOne(wrapper);
        if (systemParameter == null || systemParameter.getSysValue() == null || "".equals(systemParameter.getSysValue()) || systemParameter.getSysValue().split(CommonStant.CHARACTER_COLON).length != 2) {
            log.error("系统参数TOKEN_USER不正常");
            return JsonResultUtil.failureDesc("系统参数TOKEN_USER不正常");
        }
        String tokenUser = systemParameter.getSysValue();
        // 根据用户名获取密码
        String userName = tokenUser.split(CommonStant.CHARACTER_COLON)[0];
        String userPassWord = tokenUser.split(CommonStant.CHARACTER_COLON)[1];
        // 加密后的密码
        String passwordMD5t = DigestUtils.md5DigestAsHex(userPassWord.getBytes()).toUpperCase();
        // 如果用户名或密码不匹配
        if (!requestUserName.equals(userName) || !requestUserPass.equals(passwordMD5t)) {
            log.error("账户密码不匹配");
            return JsonResultUtil.failureDesc("账户密码不匹配");
        }
        // 根据用户名去缓存中获取token，如果token已存在，则返回token，如果token不存在，则创建新token并返回
        // 根据用户名获取token
        String token = CacheUtil.cacheMap.get(userName);

        // 如果token不存在，则生成token
        if (token == null || "".equals(token) || "null".equals(token)) {
            // 生成token
            JwtBuilder builder = Jwts.builder()
                    .setSubject(requestUserName)
                    //用于设置签发时间
                    .setIssuedAt(new Date())
                    //用于设置签名秘钥
                    .signWith(SignatureAlgorithm.HS256, requestUserPass);
            // 获取token
            result = JsonResultUtil.success();
            token = builder.compact();
            result.set(CommonStant.TOKEN, token);
            // 将生成的token放入缓存中
            CacheUtil.cacheMap.put(userName, token, ExpirationPolicy.CREATED, 1440, TimeUnit.MINUTES);
            CacheUtil.cacheMap.entrySet().forEach(entry -> log.info("==========user:" + entry.getKey() + "token" + entry.getValue() + "=========="));
        } else {
            // 将缓存中查到的token返回
            result = JsonResultUtil.success();
            result.set(CommonStant.TOKEN, token);
        }
        // 返回结果
        return result;
    }

    /**
     * 获取短期数据并返回json格式
     *
     * @param requestJson
     * @return
     */
    @Override
    public JSONObject getShortWpfData(JSONObject requestJson) {
        // 获取场站id
        String stationCode = requestJson.getStr("wfId");
        String startTime = requestJson.getStr("beginDate");
        String endTime = requestJson.getStr("endDate");
        SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd");
        try {
            long startTimeL = sdfDate.parse(startTime).getTime();
            long oneDay = 24 * 60 * 60 * 1000 - 1000;
            long endTimeL = sdfDate.parse(endTime).getTime() + oneDay;
            // 获取明日短期预测数据
            List<ForecastPowerShortTerm> shortList = getShortArr(startTimeL, endTimeL);
            //解析短期文件
            JSONObject result = fileAnalysis(stationCode, shortList, null, "dq", requestJson.getStr("beginDate"), requestJson.getStr("endDate"));
            return result;
        } catch (ParseException e) {
            log.error(String.valueOf(e));
            return JsonResultUtil.failure("获取超短期数据失败");
        }
    }

    /**
     * 接收修正短期数据
     *
     * @param requestJson
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public JSONObject putShortWpfData(JSONObject requestJson) {
        // 功率json
        JSONObject values = JSONUtil.parseObj(requestJson.getJSONArray("values").get(0));
        if (values == null || values.getJSONArray("datas").size() == 0) {
            return JsonResultUtil.failureDesc("修正短期数据为空");
        }
        // 明日修正后数据Arr
        JSONArray datas = values.getJSONArray("datas");
        // 将修正后的明日的一体化数据数据转化为List
        List<ForecastPowerShortTerm> dqList = getDqListMap(datas);
        if (dqList == null || dqList.size() == 0 || dqList.size() > 96) {
            log.error("修正数据大于96条");
            return JsonResultUtil.failureDesc("修正数据大于96条");
        }
        // 更新
        updateShortTerm(dqList);
        return JsonResultUtil.success();
    }

    /**
     * 获取超短期文件并解析返回json格式
     *
     * @param requestJson
     * @return
     */
    @Override
    public JSONObject getSShortWpfData(JSONObject requestJson) {
        String stationCode = requestJson.getStr("wfId");
        List<ForecastPowerUltraShortTerm> ultraShortList = getUltraShortArr(requestJson.getStr("beginDate"), requestJson.getStr("endDate"));
        //解析短期文件
        JSONObject result = fileAnalysis(stationCode, null, ultraShortList, "cdq", requestJson.getStr("beginDate"), requestJson.getStr("endDate"));
        return result;
    }

    /**
     * 接收修正超短期数据
     *
     * @param requestJson
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public JSONObject putSShortWpfData(JSONObject requestJson) {
        // 功率json
        JSONObject values = JSONUtil.parseObj(requestJson.getJSONArray("values").get(0));
        // 所有日期的数据Arr
        JSONArray datas = values.getJSONArray("datas");
        if (datas.size() == 0) {
            return JsonResultUtil.failureDesc("修正超短期数据为空");
        }
        // 将json数据转化为ListMap
        List<ForecastPowerUltraShortTerm> cdqListMap = getCdqListMap(datas);
        // 更新
        updateUltraShortTerm(cdqListMap);
        return JsonResultUtil.success();
    }

    /**
     * 获取上报文件
     *
     * @param requestJson
     * @return
     */
    @Override
    public JSONObject getUploadState(JSONObject requestJson) {
        String wfId = requestJson.getStr("wfId");
        // 山东短期文件类型
        String dqArr[] = {"E131", "E132"};
        // 山东超短期文件类型
        String cdqArr[] = {"E133"};
        // 获取最新短期上报文件状态
        UploadFileLog uploadFileLogDq = uploadFileLogService.getNewDateByType(dqArr);
        // 获取最新超短期上报文件状态
        UploadFileLog uploadFileLogCdq = uploadFileLogService.getNewDateByType(cdqArr);
        // 拼接json
        JSONArray resultArr = new JSONArray();
        JSONObject uploadStateJson = new JSONObject();
        String dqFileName = "";
        String cdqFileName = "";
        int dqFileStatus = 0;
        int cdqFileStatus = 0;

        if (uploadFileLogDq != null) {
            dqFileName = uploadFileLogDq.getFileName();
            // 如果是E2 则是上报成功，则设置为1，否则为0
            dqFileStatus = ("E2".equals(uploadFileLogDq.getFileStatusEnum()) ? 1 : 0);
        }

        if (uploadFileLogCdq != null) {
            cdqFileName = uploadFileLogCdq.getFileName();
            // 如果是E2 则是上报成功，则设置为1，否则为0
            cdqFileStatus = ("E2".equals(uploadFileLogCdq.getFileStatusEnum()) ? 1 : 0);
        }

        uploadStateJson.set("spfupfilename", dqFileName);
        uploadStateJson.set("spfupstate", dqFileStatus);
        uploadStateJson.set("sspfupfilename", cdqFileName);
        uploadStateJson.set("sspfupstate", cdqFileStatus);
        uploadStateJson.set("wfId", wfId);
        resultArr.add(uploadStateJson);
        return JsonResultUtil.success(resultArr);
    }

    /**
     * 解析预测数据并返回规定格式的json
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public JSONObject fileAnalysis(String stationCode, List<ForecastPowerShortTerm> shortList, List<ForecastPowerUltraShortTerm> ultraShortList, String type, String startDate, String endDate) {
        JSONArray resultArr = new JSONArray();
        boolean jx = true;
        String compareDate = "";
        // 获取短期文件生成日期
        String recDate = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDateTime.now());
        // 获取当前时间
        // 如果当前时间>=13点，则为pm(下午)，否则为上午
        LocalDateTime now = LocalDateTime.now();
        int nowHour = now.getHour();
        String version = "AM";
        if (nowHour >= 13) {
            version = "PM";
        }
        // 超短期起始时间
        String cdqFileStartTime;
        try {
            // 日数据
            JSONObject dayInfoJson = new JSONObject();
            //一天中的96条数据信息
            JSONArray valuesArr = new JSONArray();
            JSONObject oneJson;
            JSONArray datas = new JSONArray();
            SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd");
            SimpleDateFormat sdfDateTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            // 如果是短期
            if ("dq".equals(type)) {
                if (shortList == null || shortList.size() == 0) {
                    log.error("短期实时数据为空");
                    return JsonResultUtil.failureDesc("短期实时数据为空");
                }
                for (int i = 0; i < shortList.size(); i++) {
                    ForecastPowerShortTerm shortTerm = shortList.get(i);
                    //当条的日期
                    String thisDate = sdfDate.format(new Date(shortTerm.getForecastTime()));
                    // 如果当条的日期和上调日期不一致，新建dayInfoJson(日数据json)
                    if (!thisDate.equals(compareDate)) {
                        // 如果不是第一条，且和上一条日期不是同一天，则将上条日期的96条数据放入日json数据
                        if (!"".equals(compareDate)) {
                            dayInfoJson.set("datas", datas);
                            valuesArr.add(dayInfoJson);
                            datas = new JSONArray();
                        }
                        // 将当条日期放入compareDate
                        compareDate = thisDate;
                        // 新建日json数据
                        dayInfoJson = new JSONObject();
                        dayInfoJson.set("version", version);
                        dayInfoJson.set("date", thisDate);
                        dayInfoJson.set("recDate", recDate);
                    }
                    // 每一条数据的json，放入预测数据和预测时间
                    oneJson = new JSONObject();
                    oneJson.set("data", shortTerm.getFpValue().multiply(new BigDecimal("1000")));
                    oneJson.set("time", sdfDateTime.format(shortTerm.getForecastTime()));
                    // 将一每条数据放入datas(一天的json)中
                    datas.add(oneJson);
                }
                // 将最后一日数据放入valuesArr
                dayInfoJson.set("datas", datas);
                valuesArr.add(dayInfoJson);
                // 将短期预测数据放入powerJson中
                JSONObject powerJson = new JSONObject();
                powerJson.set("values", valuesArr);
                powerJson.set("wfId", stationCode);
                powerJson.set("type", "power");
                //返回短期数据JSONArray
                resultArr.add(powerJson);
            } else if ("cdq".equals(type)) {
                if (ultraShortList == null || ultraShortList.size() == 0) {
                    log.error("超短期实时数据为空");
                    return JsonResultUtil.failureDesc("超短期实时数据为空");
                }
                cdqFileStartTime = sdfDateTime.format(ultraShortList.get(0).getForecastTime());
                datas = new JSONArray();
                dayInfoJson = new JSONObject();
                dayInfoJson.set("date", cdqFileStartTime);
                for (int i = 0; i < ultraShortList.size(); i++) {
                    ForecastPowerUltraShortTerm ultraShortTerm = ultraShortList.get(i);
                    // 每一条数据的json，放入预测数据和预测时间
                    oneJson = new JSONObject();
                    oneJson.set("time", sdfDateTime.format(ultraShortTerm.getForecastTime()));
                    oneJson.set("data", ultraShortTerm.getFpValue().multiply(new BigDecimal("1000")));
                    // 将一每条数据放入datas(一天的json)中
                    datas.add(oneJson);
                }
                // 将最后一日数据放入vaulesArr
                dayInfoJson.set("datas", datas);
                valuesArr.add(dayInfoJson);
                // 将短期预测数据放入powerJson中
                JSONObject powerJson = new JSONObject();
                powerJson.set("wfId", stationCode);
                powerJson.set("values", valuesArr);
                powerJson.set("type", "power");
                //返回短期数据JSONArray
                resultArr.add(powerJson);
            }
        } catch (Exception e) {
            log.error(stationCode + " 解析当天DQ数据错误:", e);
            return JsonResultUtil.failureDesc("解析当天DQ数据错误");
        }
        //如果解析成功
        return JsonResultUtil.success(resultArr);

    }

    /**
     * 将修正后的短期数据转为list
     *
     * @param dqDate
     * @return
     */
    public List<ForecastPowerShortTerm> getDqListMap(JSONArray dqDate) {
        // 短期预测数据
        List<ForecastPowerShortTerm> shortTermList = new ArrayList<>();

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        Date now = new Date();
        try {
            // 循环短期数据
            for (int i = 0; i < dqDate.size(); i++) {
                JSONObject oneJson = dqDate.getJSONObject(i);
                ForecastPowerShortTerm forecastPowerShortTerm = new ForecastPowerShortTerm();
                // 初始化短期数据实体
                forecastPowerShortTerm.setPredictionModelEnum(PredictionModelEnum.E11);
                forecastPowerShortTerm.setForecastTime(sdf.parse(oneJson.getStr("time")).getTime());
                forecastPowerShortTerm.setGenDate(now);
                forecastPowerShortTerm.setFpValue(new BigDecimal(oneJson.getStr("data")).divide(new BigDecimal("1000"), 2, BigDecimal.ROUND_HALF_UP));
                shortTermList.add(forecastPowerShortTerm);
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return shortTermList;
    }

    /**
     * 将修正后的超短期数据转为list
     *
     * @param dqDate
     * @return
     */
    public List<ForecastPowerUltraShortTerm> getCdqListMap(JSONArray dqDate) {
        Date now = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        List<ForecastPowerUltraShortTerm> ultraShortTermList = new ArrayList<>();
        try {
            for (int i = 0; i < dqDate.size(); i++) {
                JSONObject oneJson = dqDate.getJSONObject(i);
                ForecastPowerUltraShortTerm forecastPowerUltraShortTerm = new ForecastPowerUltraShortTerm();
                // 初始化短期数据实体
                forecastPowerUltraShortTerm.setGenDate(now);
                forecastPowerUltraShortTerm.setForecastTime(sdf.parse(oneJson.getStr("time")).getTime());
                forecastPowerUltraShortTerm.setFpValue(new BigDecimal(oneJson.getStr("data")).divide(new BigDecimal("1000"), 2, BigDecimal.ROUND_HALF_UP));
                forecastPowerUltraShortTerm.setPredictionModelEnum(PredictionModelEnum.E11);
                ultraShortTermList.add(forecastPowerUltraShortTerm);
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return ultraShortTermList;
    }

    /**
     * 获取短期预测数据
     *
     * @return
     */
    public List<ForecastPowerShortTerm> getShortArr(long startTime, long endTime) {
        // 获取短期预测数据
        LambdaQueryWrapper<ForecastPowerShortTerm> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.between(ForecastPowerShortTerm::getForecastTime, startTime, endTime);
        List<ForecastPowerShortTerm> shortList = forecastPowerShortTermMapper.selectList(lambdaQueryWrapper);
        shortList.sort(Comparator.comparing(ForecastPowerShortTerm::getForecastTime));
        return shortList;
    }

    /**
     * 根据开始时间和结束时间获取短期数据
     *
     * @param beginDate
     * @param endDate
     * @return
     */
    public List<ForecastPowerUltraShortTerm> getUltraShortArr(String beginDate, String endDate) {
        // 获取短期预测数据
        LambdaQueryWrapper<ForecastPowerUltraShortTerm> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        long beginDateL = 0;
        long endDateL = 0;
        try {
            beginDateL = sdf.parse(beginDate).getTime();
            endDateL = sdf.parse(endDate).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        // 获取明天日期
        lambdaQueryWrapper.between(ForecastPowerUltraShortTerm::getForecastTime, beginDateL, endDateL);
        List<ForecastPowerUltraShortTerm> ultraShortList = forecastPowerUltraShortTermMapper.selectList(lambdaQueryWrapper);
        ultraShortList.sort(Comparator.comparing(ForecastPowerUltraShortTerm::getForecastTime));
        return ultraShortList;
    }

    /**
     * 将修正后明日数据更新到短期实时表中
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public JSONObject updateShortTerm(List<ForecastPowerShortTerm> shortTermList) {
        //升序排列
        shortTermList.sort(Comparator.comparing(ForecastPowerShortTerm::getForecastTime));
        // 获取传入数据的起始时间
        long startTime = shortTermList.get(0).getForecastTime();
        long endTime = shortTermList.get(shortTermList.size() - 1).getForecastTime();

//        long startTime = DateUtil.beginOfDay(DateUtil.tomorrow()).getTime();
//        long endTime = DateUtil.endOfDay(DateUtil.tomorrow()).getTime();
        // 根据开始时间和结束时间作为删除的查询条件
        LambdaQueryWrapper<ForecastPowerShortTerm> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.between(ForecastPowerShortTerm::getForecastTime, startTime, endTime);
        shortTermList.sort(Comparator.comparing(ForecastPowerShortTerm::getForecastTime));
        // 删除明天的预测数据
        forecastPowerShortTermMapper.delete(lambdaQueryWrapper);
        log.info("删除：{}~{}时间的短期数据成功",new Date(startTime),new Date(endTime));
        // 保存明天的预测数据
        forecastPowerShortTermService.saveBatch(shortTermList);
        return JsonResultUtil.success();
    }

    /**
     * 将修正后的超短期数据更新到超短期实时表中
     *
     * @return
     */
    @Transactional
    public JSONObject updateUltraShortTerm(List<ForecastPowerUltraShortTerm> ultraShortTermList) {
        LambdaQueryWrapper<ForecastPowerUltraShortTerm> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        long startTimeL = ultraShortTermList.stream().mapToLong(ForecastPowerUltraShortTerm::getForecastTime).min().getAsLong();
        long endTimeL = ultraShortTermList.stream().mapToLong(ForecastPowerUltraShortTerm::getForecastTime).max().getAsLong();
        if (ultraShortTermList.size() > 16) {
            log.info("====================超短期个数大于16====================");
            return JsonResultUtil.failureDesc("超短期个数大于16");
        }
        ultraShortTermList.sort(Comparator.comparing(ForecastPowerUltraShortTerm::getForecastTime));
        lambdaQueryWrapper.between(ForecastPowerUltraShortTerm::getForecastTime, startTimeL, endTimeL);
        forecastPowerUltraShortTermMapper.delete(lambdaQueryWrapper);
        forecastPowerUltraShortTermService.saveBatch(ultraShortTermList);
        return JsonResultUtil.success();
    }
}
