package com.jiayue.insu.inclientqn.service.client;


import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jiayue.insu.inclientqn.constant.CommonStant;
import com.jiayue.insu.inclientqn.entity.*;
import com.jiayue.insu.inclientqn.inenum.BJZYYJYSDEnum;
import com.jiayue.insu.inclientqn.inenum.StatusEnum;
import com.jiayue.insu.inclientqn.mapper.BJZYYJYSDCorrforeMapper;
import com.jiayue.insu.inclientqn.model.BJZYYJYSDResponseVo;
import com.jiayue.insu.inclientqn.permisson.com.BJZYYJYSDComPermisson;
import com.jiayue.insu.inclientqn.pulldata.IPullInitForecastData;
import com.jiayue.insu.inclientqn.service.*;
import com.jiayue.insu.inclientqn.util.SystermUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @description:
 * @author: yuanhao
 * @createDate: 2021/9/27
 * @version: 1.0
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class BJZYYJYCorrforeService extends ServiceImpl<BJZYYJYSDCorrforeMapper, BJZYYJYSDCorrfore> {

    private final RecordService recordService;
    private final StationService stationService;
    private final BJZYYJYSDCorrforeService bjzyyjysdCorrforeService;
    private final VelocityEngine velocityEngine;
    private final BJZYYJYSDComPermisson bjzyyjysdComPermisson;
    private final IPullInitForecastData iPullInitForecastData;


    @Value("${minio.bucketname}")
    private String bucketName;


    /**
     * 山东国家研究院下载修正短期预测数据，从一体化上
     *
     * @param station
     * @return
     */
    public boolean downLoadCorrforeData(Station station) {
        // 初始化操作记录
        Record record = new Record();
        record.setType(CommonStant.RECORD_TYPE_PULL_CORRECT);
        record.setStationCode(station.getStationCode());
        LocalDateTime localDateTime = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
        record.setTime(localDateTime);
        record.setCreateTime(LocalDateTime.now());
        // 获取token
        String token = station.getComKey();
        // 获取下载路径
        String dataPullUrl = station.getDownloadurl();
        // 获取场站标识
        String signCode = station.getSignCode();

        boolean result = false;
        boolean isUpload = false;
        String response;
        List<BJZYYJYSDCorrfore> list = new ArrayList<>();
        List<BJZYYJYSDCorrfore> listMinio = new ArrayList<>();

        // 如果token为空或者token失效，从新获取token
        LocalDateTime tokenTime = station.getKeyTime();
        if (StrUtil.isEmpty(token) || LocalDateTime.now().isAfter(tokenTime)) {
            if (bjzyyjysdComPermisson.generateKey(station)) {
                station = stationService.findThis();
                token = station.getComKey();
                tokenTime = station.getKeyTime();
            }
        }

        // 如果token为不为空并且者token未失效
        if (StrUtil.isNotEmpty(token) && LocalDateTime.now().isBefore(tokenTime)) {

            try {
                // 实装请求参数
                DateTime dateTime = DateUtil.parse(DateUtil.today());
                DateTime tomorrow = DateUtil.offsetDay(dateTime, 1);
                // 拼接请求地址
                // dataPullUrl = dataPullUrl + "?pdate=" + dateTime.toString("yyyy-MM-dd") + "&farm_id=SD0015";
                dataPullUrl = dataPullUrl + "?pdate="+tomorrow.toString("yyyy-MM-dd")+"&farm_id="+ signCode;

                //https://www.dljyfzjc.spic.com.cn:20280/sdapi/powerforecast/txnypowerforecast/getPowerForecastByFarmAndPdate?pdate=2023-12-15&farm_id=SD0015
                // 发送请求
                HttpRequest httpRequest = HttpRequest.get(dataPullUrl)
                        .header("TENANT-ID", "4")
                        .header("Authorization", "Bearer " + token);
                httpRequest.setGlobalTimeout(20000);

                response = httpRequest.execute().body();

                // 如果响应不为空
                if (StrUtil.isNotEmpty(response)) {
                    boolean isJson = JSONUtil.isJsonObj(response);
                    // 如果返回结果是json类型
                    if (isJson) {
                        BJZYYJYSDResponseVo bjzyyjysdResponseVo = JSONUtil.toBean(response, BJZYYJYSDResponseVo.class);
                        String code = bjzyyjysdResponseVo.getCode();
                        // 如果调用结果成功
                        if (code.equals(BJZYYJYSDEnum.REQUEST_SUCCESS.getCode())) {
                            log.info(station.getStationCode() + " 请求短期修正数据返回内容:{}", response);
                            try {
                                // 如果data = null 则用原始数据进行上报
                                if (!"[]".equals(bjzyyjysdResponseVo.getData())) {
                                    list = correctData(bjzyyjysdResponseVo.getData());
                                    /**************检测解析数据完整性*******************/
                                    BigDecimal one = new BigDecimal("1");
                                    BigDecimal checkCount = new BigDecimal(station.getDays().toString()).add(one).multiply(new BigDecimal("96")).add(one);
                                    // 如果修正后短期数据不为0
                                    if (list.size() > 0) {
                                        if (list.size() < checkCount.intValue()) {
                                            log.warn("========== 请求短期修正数据缺数: {}", list.size());
                                        }
                                        // 将修正数据存库
                                        bjzyyjysdCorrforeService.updateBetweenForecastTime(list.get(0).getTimeFormat(), list.get(list.size() - 1).getTimeFormat(), list);

                                        // 山东六六巨光需要从minio上请求最新短期文件进行拼接
                                        listMinio = iPullInitForecastData.pullDQData(station);

                                        // 将一体化修正数据存入map
                                        Map<Long, BJZYYJYSDCorrfore> map = new HashMap<>();
                                        for (BJZYYJYSDCorrfore bjzyyjysdCorrfore : list) {
                                            map.put(bjzyyjysdCorrfore.getForecastTime(), bjzyyjysdCorrfore);
                                        }
                                        // 根据关联字段进行替换
                                        for (int i = 0; i < listMinio.size(); i++) {
                                            BJZYYJYSDCorrfore bjzyyjysdCorrfore = listMinio.get(i);
                                            BJZYYJYSDCorrfore bjzyyjysdCorrforeReplace = map.get(bjzyyjysdCorrfore.getForecastTime());
                                            if (bjzyyjysdCorrforeReplace != null) {
                                                // 根据关联字段进行替换
                                                listMinio.set(i, bjzyyjysdCorrforeReplace);
                                            }
                                        }
                                        // 生成短期修正后文件
                                        if (genFile(station, listMinio)) {
                                            result = true;
                                            // 存入记录文件已经生成过
                                            record.setState(CommonStant.RECORD_TYPE_PULL_CORRECT);
                                            record.setStateContent(StatusEnum.SUCCESS.getCode());
                                        }
                                    }
                                    // 如果短期数据为空
                                } else {
                                    // 存入记录
                                    record.setState(StatusEnum.CONNECT_RESPONSE_CONTENT_NULL.getCode());
                                    record.setStateContent(StatusEnum.CONNECT_RESPONSE_CONTENT_NULL.getMsg());
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                                record.setState(StatusEnum.CONNECT_RESPONSE_FORMAT_ERROR.getSign());
                                record.setStateContent("解析短期或生成文件失败");
                                log.error("========== 解析短期或生成文件失败 ");
                            }

                            if (result) {
                                log.info("========== 拉取短期修正数据成功！ ==========");
                                record.setState(StatusEnum.SUCCESS.getSign());
                                record.setStateContent(String.valueOf(list.size()));
                            }

                        } else {
                            record.setState(StatusEnum.RESPONSE_FAIL.getSign());
                            record.setStateContent(StatusEnum.RESPONSE_FAIL.getMsg());
                            log.error("========== 拉取短期修正数据失败 响应码异常 " + station.getStationCode() + " :请求短期修正数据返回内容:" + response);
                        }
                    } else {
                        record.setState(StatusEnum.CONNECT_RESPONSE_FORMAT_ERROR.getSign());
                        record.setStateContent(StatusEnum.CONNECT_RESPONSE_FORMAT_ERROR.getMsg());
                        log.error("========== 拉取短期修正数据失败 接收响应字符串非json格式 ");
                    }
                } else {
                    record.setState(StatusEnum.CONNECT_RESPONSE_CONTENT_NULL.getSign());
                    record.setStateContent(StatusEnum.CONNECT_RESPONSE_CONTENT_NULL.getMsg());
                    log.error("========== 拉取短期修正数据失败  返回响应内容为空 ==========");
                }

            } catch (Exception e) {
                record.setState(StatusEnum.CONNECT_ERROR.getSign());
                record.setStateContent(StatusEnum.CONNECT_ERROR.getMsg());
                log.error("========== 请求短期修正数据失败 连接断开或请求超时 ==========");
                e.printStackTrace();

            }
        }
        recordService.save(record);
        return isUpload;
    }


    /**
     * 数据解析
     *
     * @param data 待解析数据
     * @return
     * @throws Exception
     */
    public List<BJZYYJYSDCorrfore> correctData(String data) throws Exception {

        List<BJZYYJYSDCorrfore> list = new ArrayList<>();
        data = data.substring(1, data.length() - 1);
        String[] content = data.split("(?<=})\\s*,\\s*");

        for (int i = 0; i < content.length; i++) {
            BJZYYJYSDCorrfore bjzyyjysdCorrfore = new BJZYYJYSDCorrfore();
            String column = content[i];
            //JSON 字符串转换为一个树状结构
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode rootNode = objectMapper.readTree(column);
            // 通过键名获取相应的值
            String updateTime = rootNode.get("updateTime").asText();
            String forecastTime = rootNode.get("pdate").asText();
            double ageValue = rootNode.get("forecast").asDouble();
            // 将预测时间转换为时间戳格式
            Long forecast = DateUtil.parse(forecastTime, "yyyy-MM-dd HH:mm:ss").getTime() + 15*60*1000L +15*60*1000L*i;
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String forecastDateFormat = dateFormat.format(new Date(forecast));
            // 存入实体
            bjzyyjysdCorrfore.setForecastTime(forecast);
            bjzyyjysdCorrfore.setTimeFormat(DateUtil.parse(forecastDateFormat, "yyyy-MM-dd HH:mm:ss").toTimestamp().toLocalDateTime());
            bjzyyjysdCorrfore.setUpdateTime(DateUtil.parse(updateTime, "yyyy-MM-dd HH:mm:ss").toTimestamp().toLocalDateTime());
            bjzyyjysdCorrfore.setFpValue(isNumberOrNull(String.valueOf(ageValue)));
            // 排序
            Collections.sort(list, Comparator.comparing(BJZYYJYSDCorrfore::getForecastTime));
            list.add(bjzyyjysdCorrfore);
        }
        return list;
    }

    /**
     * 判断是否是NULL 和是否是数值
     *
     * @param data
     * @return
     */
    public BigDecimal isNumberOrNull(String data) {
        BigDecimal bigDecimal = new BigDecimal("-99");
        if (!data.contains("null") && !data.contains("NULL")) {
            Pattern pattern = Pattern.compile("-[0-9]+(.[0-9]+)?|[0-9]+(.[0-9]+)?");
            Matcher isNum = pattern.matcher(data);
            try {
                if (isNum.matches()) {
                    bigDecimal = new BigDecimal(data).setScale(2, BigDecimal.ROUND_DOWN);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return bigDecimal;
    }

    /**
     * 生成修正后的短期文件(山东中央研究院)
     *
     * @param station
     * @param list
     * @return
     */
    public boolean genFile(Station station, List<BJZYYJYSDCorrfore> list) {
        boolean result = true;
        // 初始化模板
        String vmsPath = SystermUtils.getResourceBasePath() + "/vms/DQ.vm";
        File file;
        Template template = this.velocityEngine.getTemplate(vmsPath);
        // 如果模板不为空
        if (template != null) {
            VelocityContext velocityContext;
            StringWriter writer;
            // 将修正后的短期数据按照预测时间排序
            list.sort(Comparator.comparing(BJZYYJYSDCorrfore::getForecastTime));
            // 根据修正后的数据，拼接需要的数据
            List<Map<String, Object>> vList = new ArrayList<>();
            for (BJZYYJYSDCorrfore a : list) {
                Map<String, Object> map = new HashMap<>();
                map.put("id", a.getId());
                map.put("stationName", station.getName());
                map.put("forecastTime", DateUtil.format(new Date(a.getForecastTime()), "yyyy-MM-dd HH:mm:ss"));
                map.put("fpValue", a.getFpValue() == null ? "-99" : a.getFpValue());
                vList.add(map);
            }
            // 格式化模板数据
            writer = new StringWriter();
            velocityContext = new VelocityContext();
            velocityContext.put("stationName", station.getName());
            velocityContext.put("date", DateUtil.format(new Date(), "yyyy-MM-dd"));
            velocityContext.put("vList", vList);
            template.merge(velocityContext, writer);
            // 山东场站分为AB机,需拆分文件生成路径
            String[] filePath = station.getLocalFilePath().split(";");
            for (int i = 0; i < filePath.length; i++) {
                // 获取文件路径
                File fileUrl = new File(filePath[i]);
                // 判断目录是否存在
                if (!fileUrl.exists()) {
                    fileUrl.mkdirs();
                }
                // 获取文件名
                String fileName = "DQ_" + DateUtil.format(DateUtil.beginOfDay(new Date()), "yyyyMMddHHmmss") + "0.RB";
                file = new File(filePath[i] + File.separatorChar + fileName);
                // 写入文件
                result = writeFile(file, station, writer, fileName, filePath[i]);
            }
        }
        return result;
    }


    /**
     *  写入文件
     * @param file
     * @param station
     * @param writer
     * @param fileName
     * @param filePath
     * @return
     */
    private boolean writeFile(File file, Station station, StringWriter writer, String fileName,String filePath) {
        boolean result = true;
        // 获取文件路径
        File fileUrl = new File(filePath);
        // 判断目录是否存在
        if (!fileUrl.exists()) {
            fileUrl.mkdirs();
        }
        // 检查文件是否存在
        File existingFile = new File(filePath + File.separator + fileName);
        if (existingFile.exists()) {
            // 如果存在同名文件，尝试删除
            boolean deleted = existingFile.delete();
            if (!deleted) {
                // 如果无法删除，记录警告并返回false
                log.warn("无法删除文件：" + fileName);
                return false;
            }
        }

        // 创建流
        FileOutputStream os = null;
        try {
            boolean res = file.createNewFile();
            // 如果创建成功
            if (res) {
                os = new FileOutputStream(file);
                // 采用UTF-8字符集
                os.write(writer.toString().getBytes("UTF-8"));
                os.flush();
                // 创建文件失败
            } else {
                result = false;
                log.warn(station.getStationCode() + "文件名：" + fileName + " 生成失败");
            }
        } catch (IOException e) {
            e.printStackTrace();
            result = false;
            log.warn(station.getStationCode() + " 创建" + "文件名：" + fileName + "失败");
        } finally {
            // 如果流不为空，关闭流
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    log.error("文件生成关闭流失败", e);
                }
            }
            log.info("执行生成文件完毕：" + filePath + File.separatorChar + fileName);
            return result;
        }
    }
}
