package com.jiayue.insu.incloud.pulldata;

import cn.hutool.core.collection.CollectionUtil;
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.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jiayue.insu.incloud.compermisson.QNHLComPermisson;
import com.jiayue.insu.incloud.constants.CommonStant;
import com.jiayue.insu.incloud.constants.CompanyConstant;
import com.jiayue.insu.incloud.constants.enums.QNHLEnum;
import com.jiayue.insu.incloud.constants.enums.SHKJEnum;
import com.jiayue.insu.incloud.constants.enums.StatusEnum;
import com.jiayue.insu.incloud.constants.vo.RequestVo;
import com.jiayue.insu.incloud.constants.vo.ResponseSHLNVo;
import com.jiayue.insu.incloud.constants.vo.ResponseVo;
import com.jiayue.insu.incloud.entity.ForecastData;
import com.jiayue.insu.incloud.entity.IntegrationCompany;
import com.jiayue.insu.incloud.entity.Record;
import com.jiayue.insu.incloud.entity.Station;
import com.jiayue.insu.incloud.service.ForecastDataService;
import com.jiayue.insu.incloud.service.IntegrationCompanyService;
import com.jiayue.insu.incloud.service.RecordService;
import com.jiayue.insu.incloud.service.StationService;
import com.jiayue.insu.incloud.utils.HuaWeiObsUtil;
import com.jiayue.insu.incloud.utils.JsonResultUtil;
import com.jiayue.insu.incloud.utils.SystermUtils;
import com.jiayue.insu.minio.util.MinioUtilService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

import java.io.*;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 清能互联 拉取修正数据业务
 *
 * @author yh
 * @version 1.0
 * @since 2022/5/22 11:29
 */
@Component
@Slf4j
@RefreshScope
public class PullCorrectDataForSH implements IPullInitCorrectDataForSH {

    @Autowired
    private ForecastDataService forecastDataService;
    @Autowired
    private StationService stationService;
    @Autowired
    private IntegrationCompanyService integrationCompanyService;

    @Autowired
    private RecordService recordService;
    @Autowired
    private VelocityEngine velocityEngine;
    @Autowired
    private MinioUtilService minioUtilService;

    @Value("${minio.fileurl}")
    String fileurl;

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

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

    /**
     * 拉取修正预测数据并生成文件上送minio服务器
     *
     * @param station 场站信息
     * @return 是否成功
     */
    @Override
    public Boolean pullDQData(Station station) {

        Record record = new Record();
        record.setStationCode(station.getStationCode());
        record.setInCode(station.getInCode());
        record.setType(CommonStant.RECORD_TYPE_PULL_CORRECT);
        LocalDateTime localDateTime = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
        record.setTime(localDateTime);
        record.setCreateTime(LocalDateTime.now());

//        Station st = stationService.findByStationCode(station.getStationCode());

        IntegrationCompany integrationCompany = integrationCompanyService.findByCode(CompanyConstant.COMPANY_SHKJ_LN);
        String dataPullUrl = integrationCompany.getPullUrl();
        String stationId = station.getSignCode();
//        String dataPullUrl = "https://127.0.0.1:9001/consoleOpenInterface/testSh";

        boolean result = false;
        boolean isUpload = false;
        String response;
        List<ForecastData> list = new ArrayList<>();
        try {
            DateTime dateTime = DateUtil.parse(DateUtil.today());
            DateTime tomorrow = DateUtil.offsetDay(dateTime, 1);
            //https://dataservice.tradingthink.cn/api/thirdDatasourceSync/syjy/power_submit
            String day = tomorrow.toString("yyyy-MM-dd");
            Map<String, Object> map = new HashMap<>();
            map.put("station_id",stationId);
            map.put("day", day);
            // 发送请求
            HttpRequest httpRequest = HttpRequest.post(dataPullUrl)
                    .header("Content-Type", "application/json")
                    .header("DATA_SERVICE_TOKEN", "eed451be-fe36-4d2a-a3b0-85cc440b406f")
                    .body(JSONUtil.toJsonStr(map));
            httpRequest.setGlobalTimeout(20000);
            response = httpRequest.execute().body();

            if (StrUtil.isNotEmpty(response)) {
                boolean isJson = JSONUtil.isJsonObj(response);
                if (isJson) {
                    ResponseSHLNVo responseSHLNVo = JSONUtil.toBean(response, ResponseSHLNVo.class);
                    String code = responseSHLNVo.getCode();
                    if (code.equals(SHKJEnum.REQUEST_SUCCESS.getCode())) {
                        List<ForecastData> aList = new ArrayList<>();
                        long startTime = DateUtil.beginOfDay(DateUtil.tomorrow()).getTime() + 15 * 60 * 1000L;
                        long endTime = startTime + 24 * 3600 * 1000L-1;
                        aList = forecastDataService.findTimeByStation(station.getStationCode(), startTime, endTime);
                        log.info(station.getStationCode() + " 请求短期修正数据返回内容:{}", response);

                        try {
                            list = correctWindData(responseSHLNVo.getData());
                            /**************检测解析数据完整性(飔合科技只修正96数据)*******************/
                            BigDecimal checkCount = new BigDecimal(96);
                            if (list.size() < checkCount.intValue()) {
                                record.setState(StatusEnum.DATA_DEFICIENCY.getCode());
                                record.setStateContent(StatusEnum.DATA_DEFICIENCY.getMsg());
                                log.warn("========== {}:{} ==> 请求短期修正数据缺数: {}", integrationCompany.getName(), station.getStationCode(), list.size());
                            } else {
                                result = true;
                                for (ForecastData a : aList) {
                                    List<ForecastData> collect = list.stream().filter(n -> n.getForecastTime().longValue() == a.getForecastTime().longValue()).collect(Collectors.toList());
                                    if (CollectionUtil.isNotEmpty(collect)) {
                                        ForecastData correct = collect.get(0);
                                        a.setCorrectTime(LocalDateTime.now());
                                        a.setFpValueCorrect(correct.getFpValueCorrect());
                                    } else {
                                        record.setState(StatusEnum.DATA_DEFICIENCY.getCode());
                                        record.setStateContent(StatusEnum.DATA_DEFICIENCY.getMsg());
                                        log.warn("========== {}:{} ==> 请求短期修正数据缺数: {}", integrationCompany.getName(), station.getStationCode(), list.size());
                                        break;
                                    }
                                }

                            }
                        } catch (Exception e) {
                            record.setState(StatusEnum.CONNECT_RESPONSE_FORMAT_ERROR.getSign());
                            record.setStateContent("解析短期修正数据失败");
                            log.error("=========={}:{} ==> 解析短期修正数据失败:{}", integrationCompany.getName(), station.getStationCode(),e);
                        }

                        if (result) {
                            log.info("=========={}:{} ==> 拉取短期修正数据成功！ ==========", integrationCompany.getName(), station.getStationCode());
                            record.setState(StatusEnum.SUCCESS.getSign());
                            record.setStateContent(String.valueOf(list.size()));
                            boolean b = forecastDataService.saveOrUpdateBatch(aList);

                            if (b) {
                                File file = genFile(station);
                                if (file != null) {
                                    JSONObject pushMinIoResult = pushFileToMinIo(file, station.getStationCode());
                                    if (JsonResultUtil.Type.failure.value().equals(pushMinIoResult.get(JsonResultUtil.CODE_TAG))) {
                                       log.error(pushMinIoResult.get(JsonResultUtil.DETAIL_TAG).toString());
                                    }
                                    // 获取推送到minIo的文件名
                                    String minIoFileUrl = pushMinIoResult.getStr(JsonResultUtil.DATA_TAG);
                                    String[] minIoFileUrlArr = minIoFileUrl.split(";");
                                    String pushDate = DateUtil.format(new Date(), "yyyyMMdd");
                                    if (minIoFileUrlArr.length > 1) {
                                        for (String url : minIoFileUrlArr) {
                                            if (url.contains(station.getStationCode())) {
                                                pushCorr(station, file.getName(), pushDate, url);
                                            } else {
                                                station.setStationCode(station.getStationCode().replace("-A", "-B"));
                                                pushCorr(station, file.getName(), pushDate, url);
                                            }
                                        }
                                    } else {
                                        // 如果文件放成功，回传文件生成信息到云端
                                        pushCorr(station, file.getName(), pushDate, minIoFileUrl);
                                    }

                                }
                            }

                        }

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

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

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

    }


    /**
     * 风电数据解析(飔合科技只修正短期预测数据)
     *
     * @param data 待解析数据
     * @return 解析数据
     * @throws Exception
     */
    public List<ForecastData> correctWindData(String data) throws Exception {

        data = data.substring(1, data.length() - 1);
        String[] content = data.split("(?<=})\\s*,\\s*");

        List<ForecastData> list = new ArrayList<>();

        for (int i = 0; i < content.length; i++) {

            ForecastData inForecastPowerShortTerm = new ForecastData();
            String column = content[i];

            //JSON 字符串转换为一个树状结构
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode rootNode = objectMapper.readTree(column);
            // 通过键名获取相应的值
            String forecastTime = rootNode.get("time").asText();
            double ageValue = rootNode.get("power").asDouble();

            // 将24:00:00转换为第二天的00:00:00的情况
            if (forecastTime.contains("24:00:00")) {
                String changeTime = forecastTime.replace("24:00:00", "23:59:59");
                Date date = DateUtil.parse(changeTime, "yyyy-MM-dd HH:mm:ss");
                date = DateUtil.offsetSecond(date, 1);
                forecastTime = DateUtil.format(date, "yyyy-MM-dd HH:mm:ss");
            }
            // 将预测时间转换为时间戳格式
            Long forecast = DateUtil.parse(forecastTime, "yyyy-MM-dd HH:mm:ss").getTime();

            inForecastPowerShortTerm.setForecastTime(forecast);

            inForecastPowerShortTerm.setFpValueCorrect(isNumberOrNull(String.valueOf(ageValue)));

            list.add(inForecastPowerShortTerm);
        }


        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 fileName
     * @param url
     */
    public void pushCorr(Station station, String fileName, String pushDate, String url) {
        // 如果场站信息为空或者 不推送修正数据
        if (station == null || "0".equals(station.getIsPushCorr())) {
            return;
        }
        Map params = new HashMap(5);
        params.put("stationCode", station.getStationCode());
        params.put("forecastDay", pushDate);
        params.put("forecastFileName", fileName);
        String fileDownloadUrl = url.split("\\?")[0];
        params.put("fileDownloadPath", fileDownloadUrl);
        params.put("remark", "数源:一体化修正");
        log.info("发送下发修正文件请求");
        String repose = HttpUtil.post(pushCorrFileUrl, params, 10000);
        log.info("发送下发修正文件请求，请求响应 {}", repose);
    }

    /**
     * 生成文件
     *
     * @return
     */
    public File genFile(Station station) {

        String vmsPath = SystermUtils.getResourceBasePath() + "/vms/DQ.vm";
        File file = null;
        Template template = this.velocityEngine.getTemplate(vmsPath);
        if (template != null) {
            VelocityContext velocityContext;
            StringWriter writer;

            long startTime = DateUtil.beginOfDay(new Date()).getTime();
            long endTime = DateUtil.offsetDay(DateUtil.beginOfDay(DateUtil.tomorrow()), station.getDays()).getTime();
            List<ForecastData> aList = new ArrayList<>();
            aList = forecastDataService.findTimeByStation(station.getStationCode(), startTime, endTime);
            aList.sort(Comparator.comparing(ForecastData::getForecastTime));
            List<Map<String, Object>> vList = new ArrayList<>();

            for (ForecastData a : aList) {
                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.getFpValueCorrect() == null ? a.getFpValue() : a.getFpValueCorrect());
                vList.add(map);

            }

            velocityContext = new VelocityContext();
            velocityContext.put("stationName", station.getName());

            velocityContext.put("date", DateUtil.format(new Date(), "yyyy-MM-dd"));
            velocityContext.put("vList", vList);

            writer = new StringWriter();
            template.merge(velocityContext, writer);

            String fUrl = fileurl + File.separatorChar + "correct" + File.separatorChar + station.getStationCode();
            log.info(fUrl);
            File fileUrl = new File(fUrl);
            if (!fileUrl.exists()) {// 判断目录是否存在
                fileUrl.mkdirs();
            }
            String fileName = "";

            //根据时间区分上送minio文件名
            LocalTime noon = LocalTime.NOON;
            LocalDateTime noonToday = LocalDateTime.of(LocalDate.now(), noon);
            // 文件名区分（上午、下午）
            if (LocalDateTime.now().isBefore(noonToday)) {
                fileName = "DQ_" + DateUtil.format(DateUtil.beginOfDay(new Date()), "yyyyMMddHHmmss") + "0.RB";
            } else {
                fileName = "DQ_" + new SimpleDateFormat("yyyyMMdd").format(new Date())+"1200000.RB";
            }

            file = new File(fUrl + File.separatorChar + fileName);


            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 {
                    log.warn(station.getStationCode() + " 生成DQ文件失败");
                }
            } catch (IOException e) {
                e.printStackTrace();
                log.warn(station.getStationCode() + " 创建DQ文件失败");
            } finally {
                if (os != null) {
                    try {
                        os.close();
                    } catch (IOException e) {
                        log.error("文件生成关闭流失败", e);
                    }
                }
            }

        }

        return file;
    }


    public JSONObject pushFileToMinIo(File file, String stationCode) {
        // 存放完文件后返回的文件路径
        String minIoFileUrl;
        InputStream in = null;
        InputStream inB = null;
        try {
            // 查看桶名称是否存在
//            boolean flag = minioUtilService.bucketExists(bucketName);
//            if (!flag) {
//                return JsonResultUtil.failure("MinIo桶不存在");
//            }
            // 向minIo中放入文件
            in = new FileInputStream(file);
            byte[] bytes = IOUtils.toByteArray(in);
            String fileName =  stationCode + "/" + file.getName();

            // 如果文件放成功，回传文件生成信息到云端
//                                        postFileCreateInfoToCloud(station.getStationCode(), file.getName(), minIoFileUrl);
//            pushCorr(station, file.getName(), pushDate, minIoFileUrl);
//                                    }
            minIoFileUrl = HuaWeiObsUtil.uploadFile(bucketName, fileName, bytes);
            // 如果是AB机，向B中也放入文件
            if (stationCode.indexOf("-A") > 0) {
                // InputStream 在被读取后，指针已经移动到了流的末尾，第二次上传B时就已经没有数据了，故新建一个输入流
                inB = new FileInputStream(file);
                byte[] byteB = IOUtils.toByteArray(inB);
                String fileNameB = stationCode.replace("-A", "-B") + "/" + file.getName();
                minIoFileUrl = minIoFileUrl + ";" + HuaWeiObsUtil.uploadFile(bucketName, fileNameB, byteB);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return JsonResultUtil.failure("推送MinIo失败");
        }finally {
            close(in);
            close(inB);
        }
        return JsonResultUtil.success(minIoFileUrl);
    }

    // 封装一个安全的关闭方法
    private static void close(InputStream inputStream) {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace(); // 可选：日志记录
            }
        }
    }

}
