package com.jiayue.ipfcst.service;

import com.jiayue.ipfcst.common.data.entity.ForecastPowerShortTermOne;
import com.jiayue.ipfcst.common.data.entity.ForecastPowerShortTermOneHis;
import com.jiayue.ipfcst.common.data.repository.ForecastPowerShortTermHisRepository;
import com.jiayue.ipfcst.common.data.repository.ForecastPowerShortTermOneHisRepository;
import com.jiayue.ipfcst.common.data.service.BaseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 历史预测短期业务层
 *
 * @author yh
 * @version 1.0
 * @since 2019/8/5 16:02
 */
@Service
@Slf4j
public class ForecastPowerShortTermOneHisService extends BaseService {
  private final ForecastPowerShortTermOneHisRepository forecastPowerShortTermOneHisRepository;

  @Autowired
  public ForecastPowerShortTermOneHisService(ForecastPowerShortTermHisRepository forecastPowerShortTermHisRepository, ForecastPowerShortTermOneHisRepository forecastPowerShortTermOneHisRepository) {
    this.forecastPowerShortTermOneHisRepository = forecastPowerShortTermOneHisRepository;
  }

  /**
   * 根据提前多久，开始结束时间查询历史超短期 yh
   *
   * @param startTime 开始时间
   * @param endTime   结束时间
   * @param ago       提前多久预测
   * @return 结果集
   */
  @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
  public Map<String, Object> findByForecastTimeBetweenAndForecastHowLongAgoOrderByForecastTime(Long startTime, Long endTime, Integer ago) {
    Map<String, Object> map = new HashMap<>();
    List<ForecastPowerShortTermOneHis> list = forecastPowerShortTermOneHisRepository.findByForecastTimeBetweenAndForecastHowLongAgo(startTime, endTime, ago);
    list.sort(Comparator.comparing(ForecastPowerShortTermOneHis::getForecastTime));
    long timeStep = 900000L;
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    List<String> times = new ArrayList<>();
    List<Float> datas = new ArrayList<>();
    if (startTime % timeStep != 0) {
      startTime = startTime - (startTime % timeStep) + timeStep;
    }
    if (list != null && list.size() > 0) {
      for (Long i = startTime; i < endTime; i = i + timeStep) {
        long finalI = i;
        List<ForecastPowerShortTermOneHis> filterList = list.stream().filter(t -> t.getForecastTime() == finalI).collect(Collectors.toList());
        if (filterList != null && filterList.size() > 0) {
          datas.add(filterList.get(0).getAbleValue().floatValue());
        } else {
          datas.add(null);
        }

        times.add(sdf.format(new Date(finalI)));
      }
    }
    map.put("times", times);
    map.put("datas", datas);

    return map;
  }

  /**
   * 导出短期上报日数据
   * @param time 数据生成日期
   */
  public void exportForForecastPowerShortTerm(Long time,HttpServletResponse response) throws Exception {

    BufferedOutputStream bos = null;

    Map<String, Object> map = findByGenDateBetween(time, time + 86400000l - 1l, 900000l);
    try {
      StringBuilder templateContent = new StringBuilder();
      response.setCharacterEncoding("UTF-8");
      String header =  "\"预测时间\""  +  "," + "\"预测功率\"" +"\r\n";
      List<String> times = (List<String>)map.get("times");
      List<Float> datas = (List<Float>)map.get("datas");
      templateContent.append(header);
      for (int i = 0; i <times.size() ; i++) {
        StringBuilder content = new StringBuilder();

        content.append(times.get(i) + ",");

        if(datas.get(i) == null){
          content.append(",");
        }else{
          content.append(datas.get(i)  + ",");
        }
        content.append("\r\n");

        templateContent.append(content.toString());
      }
      SimpleDateFormat format =  new SimpleDateFormat( "yyyy-MM-dd" );
      String s = format.format(new Date(time));
      response.setContentType("application/x-msdownload;charset=UTF-8");// 文件下载必须配置为application/x-msdownload
      response.setHeader("Content-disposition", "attachment; filename=" + URLEncoder.encode("短期上报日数据_"+s+".csv", "UTF-8"));// 中文文件名必须使用URLEncoder.encode进行转码
      byte[] templateContentBytes = templateContent.toString().getBytes("UTF-8");
      bos = new BufferedOutputStream(response.getOutputStream());// 向response中写入文件流
      bos.write(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF});// 指定csv文件用UTF-8字符集打开
      bos.write(templateContentBytes);
      response.flushBuffer();
      templateContent = null;

    } catch (Exception e) {
      log.error("系统错误：" + e.getMessage(), e);
      throw new RuntimeException(e);
    } finally {
      if (bos != null){
        try {
          bos.close();
        } catch (IOException e) {
          log.error("系统错误：" + e.getMessage(), e);
        }
      }
    }

  }


  /**
   * 根据提前多久，开始结束时间查询历史超短期 yh
   *
   * @param startTime 开始时间
   * @param endTime   结束时间
   * @param ago       提前多久预测
   * @return 结果集
   */
  @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
  public Map<String, Object> findByForecastTimeBetweenAndForecastHowLongAgoOrderByForecastTimeForDB(Long startTime, Long endTime, Integer ago, Long timeStep) {
    Map<String, Object> map = new HashMap<>();
    List<ForecastPowerShortTermOneHis> list = forecastPowerShortTermOneHisRepository.findByForecastTimeBetweenAndForecastHowLongAgo(startTime, endTime, ago);
    list.sort(Comparator.comparing(ForecastPowerShortTermOneHis::getForecastTime));
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    List<String> times = new ArrayList<>();
    List<Float> datas = new ArrayList<>();
    if (startTime % timeStep != 0) {
      startTime = startTime - (startTime % timeStep) + timeStep;
    }
    for (Long i = startTime; i < endTime; i = i + timeStep) {
      if (list != null && list.size() > 0) {
        long finalI = i;
        List<ForecastPowerShortTermOneHis> filterList = list.stream().filter(t -> t.getForecastTime() == finalI).collect(Collectors.toList());
        if (filterList != null && filterList.size() > 0) {
          datas.add(filterList.get(0).getAbleValue().floatValue());
        } else {
          datas.add(null);
        }
      } else {
        datas.add(null);
      }
      times.add(sdf.format(new Date(i)));

    }

    map.put("times", times);
    map.put("datas", datas);

    return map;
  }


  /**
   * 根据提前多久，开始结束时间查询历史超短期 yh
   *
   * @param startTime 开始时间
   * @param endTime   结束时间
   * @return 结果集
   */
  @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
  public Map<String, Object> findByGenDateBetween(Long startTime, Long endTime, Long timeStep) {
    Map<String, Object> map = new HashMap<>();
    List<ForecastPowerShortTermOneHis> list = forecastPowerShortTermOneHisRepository.findByGenDateBetween(new Date(startTime), new Date(endTime));
    list.sort(Comparator.comparing(ForecastPowerShortTermOneHis::getForecastTime));
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    List<String> times = new ArrayList<>();
    List<Float> datas = new ArrayList<>();

    if(list.size()>0){
      for (Long i = list.get(0).getForecastTime(); i <= list.get(list.size()-1).getForecastTime(); i = i + timeStep) {
        if (list != null && list.size() > 0) {
          long finalI = i;
          List<ForecastPowerShortTermOneHis> filterList = list.stream().filter(t -> t.getForecastTime() == finalI).collect(Collectors.toList());
          if (filterList != null && filterList.size() > 0) {
            datas.add(filterList.get(0).getAbleValue().floatValue());
          } else {
            datas.add(null);
          }
        } else {
          datas.add(null);
        }
        times.add(sdf.format(new Date(i)));

      }
    }
    map.put("times", times);
    map.put("datas", datas);

    return map;
  }



  /**
   * 根据时间 分页查询 查询短期预测数据
   *
   * @param startTime 开始时间
   * @param endTime   结束时间
   * @param page      页码
   * @param size      条数
   * @param sortOrder 排序
   * @return
   */
  @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
  public Map<String, Object> findByTimeBetweenAndNoForPaging(Long startTime, Long endTime,
                                                             Integer page, Integer size,
                                                             String sortOrder) {
    Map<String, Object> map = new HashMap<>();
    Sort sort = Sort.by(Sort.Direction.DESC, "forecastTime");
    if (sortOrder.contains("asc")) {
      sort = Sort.by(Sort.Direction.ASC, "forecastTime");
    }
    Specification<ForecastPowerShortTermOneHis> specification = this.specificationFindByTimeAndNo(new Date(startTime), new Date(endTime));
    Pageable pageable = PageRequest.of(page - 1, size, sort);
    Page forecastPowerShortTerms = forecastPowerShortTermOneHisRepository.findAll(specification, pageable);
    List<ForecastPowerShortTermOne> data = forecastPowerShortTerms.getContent();
    map.put("content", data);
    map.put("count", forecastPowerShortTerms.getTotalElements());
    return map;
  }

  /**
   * 根据时间和编号 分页查询条件
   *
   * @param startTime 开始时间
   * @param endTime   结束时间
   * @return 过滤条件
   */
  Specification<ForecastPowerShortTermOneHis> specificationFindByTimeAndNo(final Date startTime, final Date endTime) {
    return (Specification<ForecastPowerShortTermOneHis>) (root, criteriaQuery, cb) -> {
      List<Predicate> predicates = new ArrayList<>();
      if (startTime != null) {
        //大于或等于传入时间
        predicates.add(cb.greaterThanOrEqualTo(root.get("genDate").as(Date.class), startTime));
      }
      if (endTime != null) {
        //小于传入时间
        predicates.add(cb.lessThan(root.get("genDate").as(Date.class), endTime));
      }

      //添加排序的功能
      return cb.and(predicates.toArray(new Predicate[predicates.size()]));

    };
  }
}
