package com.gyee.runeconomy.service.agc;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.TypeReference;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gyee.common.model.PointData;
import com.gyee.runeconomy.config.GyeeConfig;
import com.gyee.runeconomy.controller.agc.AgcDeviateTag;
import com.gyee.runeconomy.controller.agc.AiPoints;
import com.gyee.runeconomy.controller.agc.FileService;
import com.gyee.runeconomy.controller.agc.SpeedPowerAnalysis;
import com.gyee.runeconomy.init.CacheContext;
import com.gyee.runeconomy.model.PowerLosses;
import com.gyee.runeconomy.model.PowerLossesDTO;
import com.gyee.runeconomy.model.StatusTime;
import com.gyee.runeconomy.model.auto.PointInfo;
import com.gyee.runeconomy.model.auto.ProBasicEquipment;
import com.gyee.runeconomy.model.auto.ProBasicEquipmentPoint;
import com.gyee.runeconomy.service.PowerLossesService;
import com.gyee.runeconomy.service.auto.IPointInfoService;
import com.gyee.runeconomy.util.realtimesource.IEdosUtil;
import com.gyee.runeconomy.util.realtimesource.feign.IDataAdapter;
import com.gyee.runeconomy.util.realtimesource.feign.RemoteServiceBuilder;
import com.gyee.runeconomy.util.realtimesource.feign.TsDoubleTsData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.File;
import java.text.DecimalFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 读取数据库配置文件
 */
@Order(0)
@Component
public class AgcDeviateService {
    public static Map<String, ConcurrentHashMap<Double, Double>> fitcoef = new ConcurrentHashMap<>();


    /**
     * 是否是离线版本
     */
    private boolean isOffline;

    /**
     * 离线数据保存路径
     */
    private String filePathPower = "data\\power\\";

    public File jarF = null;

    {
        ApplicationHome h = new ApplicationHome(getClass());
        jarF = h.getSource();
    }

    /**
     * 文件读写
     */
    private FileService fileService;


    @Autowired
    private GyeeConfig config;

    @Resource
    private IDataAdapter iDataAdapter;
    @Autowired
    private RemoteServiceBuilder remoteService;
    @Resource
    private PowerLossesService powerLossService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private IEdosUtil edosUtil;

    @Resource
    private IPointInfoService pointInfoService;


    /**
     * 获取agc曲线偏差分析需要的数据
     *
     * @param id       场站ID
     * @param startTs  开始时间戳
     * @param endTs    结束时间戳
     * @param interval 数据时间间隔
     * @return 分析数据
     */
    public List<AgcDeviateTag> getAgcDeviateTags(String id, long startTs, long endTs, int interval) {
        List<AgcDeviateTag> ladt = new ArrayList<>();
        List<AiPoints> laps = getAiPoints(id);
        if (laps == null) {
            return ladt;
        }
        for (AiPoints ap : laps) {
            AgcDeviateTag adt = new AgcDeviateTag();
            adt.setName(ap.getName());
            List<TsDoubleTsData> lpd = getPointData(ap, startTs, endTs, interval);
            adt.setValues(lpd);
            ladt.add(adt);
        }
        // 上限下限
        List<AgcDeviateTag> upperLowerLimits = getUpperLowerLimits(ladt, id);
        ladt.addAll(upperLowerLimits);

        // 获取第一个 AgcDeviateTag 的 values 大小作为基准
        int baseSize1 = ladt.get(0).getValues().size();
        int baseSize2 = ladt.get(1).getValues().size();
        int baseSize3 = ladt.get(2).getValues().size();
        int baseSize4 = ladt.get(3).getValues().size();
        int baseSize5 = ladt.get(4).getValues().size();

        // 首先，从 "理论功率" 中找到参考数量
        int referenceSize = -1;
        for (AgcDeviateTag tag : ladt) {
            if ("理论功率".equals(tag.getName())) {
                referenceSize = tag.getValues().size();
                break;
            }
        }


        // 检查并调整其他列表
        for (AgcDeviateTag tag : ladt) {
            if (!"理论功率".equals(tag.getName()) && tag.getValues().size() != referenceSize) {
                // 如果尺寸不匹配，则删除倒数第二条元素
                if (tag.getValues().size() > referenceSize) {
                    tag.getValues().remove(tag.getValues().size() - 2); // 删除倒数第二项
                } else {
                    System.out.println("警告：" + tag.getName() + " 的元素数量少于理论功率。无法调整。");
                }
            }
        }
        return ladt;
    }

    private List<TsDoubleTsData> getPointData(AiPoints ap, long startTs, long endTs, int interval) {
        List<TsDoubleTsData> lpds = null;
        if (ap.getTag().contains(",")) {
            lpds = getMultiple(ap, startTs, endTs, interval);
        } else {


            if (ap.getTag().startsWith("NEM")) {
                lpds = remoteService.taos().getHistorytsSnap(ap.getTag(), startTs, endTs, interval);
            } else {
                lpds = remoteService.adapterfd().getHistorytsSnap(ap.getTag(), startTs, endTs, interval);
            }

        }
        if (ap.getMultiplier() != 1) {
            for (TsDoubleTsData pd : lpds) {
                pd.setDoubleValue(pd.getDoubleValue() * ap.getMultiplier());
            }
        }
        return lpds;
    }

    /**
     * 获取多个标签点数值
     */
    private List<TsDoubleTsData> getMultiple(AiPoints ap, long startTs, long endTs, int interval) {
        String[] tags = ap.getTag().split(",");
        boolean isFirst = true;
        List<TsDoubleTsData> lpd = new ArrayList<>();
        for (String tag : tags) {
            if (tag.equals("")) {
                continue;
            }

            List<TsDoubleTsData> vals = iDataAdapter.getHistorySnap(tag, startTs, endTs, interval);
            for (int i = 0; i < vals.size(); ++i) {
                if (isFirst) {
                    lpd.addAll(vals);
                    isFirst = false;
                    break;
                } else {
                    TsDoubleTsData pd = lpd.get(i);
                    pd.setDoubleValue(vals.get(i).getDoubleValue() + pd.getDoubleValue());
                }
            }
        }
        return lpd;
    }

    /**
     * 获取上限下限
     */
    private List<AgcDeviateTag> getUpperLowerLimits(List<AgcDeviateTag> ladt, String id) {
        // 装机容量
        double capacity = getCapacity(id);
        // 偏差
        double deviation = capacity * 0.03;
        // 有功设定
        Optional<AgcDeviateTag> agcLimit = ladt.stream().filter(ad -> ad.getName().equals("有功设定限值")).findFirst();

        List<AgcDeviateTag> la = new ArrayList<>();
        if (!agcLimit.isPresent()) {
            return la;
        }
        AgcDeviateTag adtUper = new AgcDeviateTag();
        adtUper.setName("偏差上限");
        adtUper.setValues(new ArrayList<>());
        AgcDeviateTag adtLimt = new AgcDeviateTag();
        adtLimt.setName("偏差下限");
        adtLimt.setValues(new ArrayList<>());

        for (TsDoubleTsData pd : agcLimit.get().getValues()) {
            long ts = pd.getTs();
            TsDoubleTsData pdUper = new TsDoubleTsData();
            pdUper.setTs(ts);
            pdUper.setDoubleValue(pd.getDoubleValue() * 1.03);
            TsDoubleTsData pdLimt = new TsDoubleTsData();
            pdLimt.setTs(ts);
            pdLimt.setDoubleValue(pd.getDoubleValue() * 0.97);
            adtUper.getValues().add(pdUper);
            adtLimt.getValues().add(pdLimt);
        }
        la.add(adtUper);
        la.add(adtLimt);
        return la;
    }


    /**
     * 根据ID获取agc配置信息
     *
     * @param id 场站ID
     * @return 配置信息
     */
    private List<AiPoints> getAiPoints(String id) {
        if (!CacheContext.agcDeviateConfigMap.containsKey(id)) {
            return null;
        }
        AiPoints[] aiPoints = CacheContext.agcDeviateConfigMap.get(id).getAiPoints();
        return Arrays.stream(aiPoints).filter(ap -> ap.getName().contains("功") || ap.getName().contains("风") && !ap.getName().contains("预测")).collect(Collectors.toList());
    }

    private double getCapacity(String id) {
        if (!CacheContext.agcDeviateConfigMap.containsKey(id)) {
            return 0;
        }
        return CacheContext.agcDeviateConfigMap.get(id).getInstalledCapacity();
    }

    /**
     * 获取配置
     *
     * @return
     */
    public Object getConfig() {
        return CacheContext.agcDeviateConfigMap;
    }

    public List<AgcDeviateTag> getAgcDeviateTagsOffline(String id, long startTs, long endTs, int interval) {
        if (!CacheContext.files.containsKey(id)) {
            return new ArrayList<>();
        }
        List<String> ls = CacheContext.files.get(id);

        if (ls == null) {
            return new ArrayList<>();
        }

        int ran = (int) (Math.random() * ls.size());
        return fileService.getFromFile(ls.get(ran), new TypeReference<List<AgcDeviateTag>>() {
        });
    }

    /**
     * 限电时间列表
     *
     * @param startTs
     * @param endTs
     * @param pageNum
     * @param pageSize
     * @return
     */
    public Page<PowerLossesDTO> getAgcxd(long startTs, long endTs,int pageNum, int pageSize) {

        LocalDateTime startDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(startTs), ZoneId.systemDefault());
        LocalDateTime endDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(endTs), ZoneId.systemDefault());
        Page<PowerLosses> page = new Page<>(pageNum, pageSize);
        IPage<PowerLosses> list = powerLossService.pageList(page, startDateTime, endDateTime);
        List<PowerLosses> powerLosses = powerLossService.pageTotalList(startDateTime, endDateTime);


        // 使用dto列表替换原始记录列表
        List<PowerLossesDTO> dtos = new ArrayList<>();
        DecimalFormat df = new DecimalFormat("0.0000000");
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

        for (PowerLosses pl : list.getRecords()) {
            PowerLossesDTO dto = new PowerLossesDTO();
            dto.setId(pl.getId());
            dto.setStartTime(pl.getStartTime().format(formatter));
            dto.setEndTime(pl.getEndTime().format(formatter));

            try {
                double xddl = Double.parseDouble(pl.getXdss());
                double xddlDivided = xddl / 10000;
                String xd = df.format(xddlDivided);
                dto.setXdss(xd);
            } catch (NumberFormatException e) {
                dto.setXdss("0.0000000");
            }

            dto.setRecordDate(pl.getRecordDate());
            dtos.add(dto);
        }

        // 创建一个新的IPage对象并设置dtos为记录
        Page<PowerLossesDTO> dtoPage = new Page<>(pageNum, pageSize);
        dtoPage.setRecords(dtos);
        dtoPage.setTotal(powerLosses.size());

        return dtoPage;
    }


    public Map<String, List<SpeedPowerAnalysis>> getWindturbineData(long startTs, long endTs, String windturbineId, int interval) throws Exception {

        //切换数据库
        LettuceConnectionFactory factory = (LettuceConnectionFactory) stringRedisTemplate.getConnectionFactory();
        int database = factory.getDatabase();
        factory.setDatabase(5);
        stringRedisTemplate.setConnectionFactory(factory);
        factory.afterPropertiesSet();
        factory.resetConnection();

        DateTime date = DateUtil.date(endTs * 1000);
        Map<String, List<SpeedPowerAnalysis>> map = new HashMap<>();
        String[] wtIds = windturbineId.split(",");
        List<String> wtIdsLs = Arrays.asList(wtIds);
        for (String wtId : wtIdsLs) {
            List<SpeedPowerAnalysis> ls = new ArrayList<>();
            SpeedPowerAnalysis spa = new SpeedPowerAnalysis();
            List<ProBasicEquipmentPoint> proBasicEquipmentPoints = CacheContext.pointNewMap.get(wtId);
            List<ProBasicEquipmentPoint> speedPoint = proBasicEquipmentPoints.stream().filter(p -> p.getUniformCode().equals("AI066")).collect(Collectors.toList());
            List<PointData> historyspeedDatas = edosUtil.getHistoryDatasSnap(speedPoint.get(0).getNemCode(), startTs, endTs, interval);
            List<Double> speedDatas = historyspeedDatas.stream().map(PointData::getPointValueInDouble).collect(Collectors.toList());
            spa.setWindSpeed(speedDatas);
            List<Long> time = historyspeedDatas.stream().map(PointData::getPointTime).collect(Collectors.toList());
            spa.setTime(time);
            List<ProBasicEquipmentPoint> powerPoint = proBasicEquipmentPoints.stream().filter(p -> p.getUniformCode().equals("AI114")).collect(Collectors.toList());
            List<PointData> historyPowerDatas = edosUtil.getHistoryDatasSnap(powerPoint.get(0).getNemCode(), startTs, endTs, interval);
            List<Double> powerData = historyPowerDatas.stream().map(PointData::getPointValueInDouble).collect(Collectors.toList());
            spa.setActuatedPower(powerData);
            String powerCurveMonth = "glqxnh:" + date.month() + ":";
            Set<String> keys = stringRedisTemplate.keys(powerCurveMonth + wtId);
            Iterator<String> iterator = keys.iterator(); // 获取迭代器
            String firstKey = iterator.hasNext() ? iterator.next() : null;
            String yc = stringRedisTemplate.opsForValue().get(firstKey);
            ConcurrentHashMap<Double, Double> wtyc = JSON.parseObject(yc, new com.alibaba.fastjson.TypeReference<ConcurrentHashMap<Double, Double>>() {
            }.getType());
            fitcoef.put(firstKey.replaceFirst(powerCurveMonth, ""), wtyc);
            List<Double> llgl = speedDatas.stream().map(a -> fitcoef.get(wtId).get(a)).collect(Collectors.toList());
            spa.setTheoreticalPower(llgl);
            ls.add(spa);
            map.put(wtId, ls);
        }

        //关闭数据库切换
        factory.setDatabase(database);
        factory.afterPropertiesSet();
        factory.resetConnection();

        return map;
    }

    public Map<String, List<StatusTime>> getStatusTime(long startTs, long endTs, String uniformcode) throws Exception {
        Map<String, List<StatusTime>> map = new HashMap<>();
        QueryWrapper<PointInfo> qw = new QueryWrapper<>();
        qw.lambda().eq(PointInfo::getUniformCode, uniformcode);
        List<PointInfo> list = pointInfoService.list(qw);
        for (PointInfo ls : list) {
            List<StatusTime> ll = new ArrayList<>();
            List<PointData> historyDatasRaw = edosUtil.getHistoryDatasRaw(ls.getPointKey(), startTs, endTs);
            for (PointData pointData : historyDatasRaw) {
                StatusTime st = new StatusTime();
                st.setName(ls.getName());
                Date da = new Date(pointData.getPointTime());
                st.setTime(DateUtil.formatDateTime(da));
                String getstatus = getstatus((int) pointData.getPointValueInDouble());
                st.setStatus(getstatus);
                ll.add(st);
            }
            List<String> collect = CacheContext.wtls.stream().filter(w -> w.getId().equals(ls.getTurbineId())).map(ProBasicEquipment::getNemCode).collect(Collectors.toList());
            map.put(collect.get(0), ll);
        }
        List<String> sortedKeys = new ArrayList<>(map.keySet());
        Collections.sort(sortedKeys);
        Map<String, List<StatusTime>> sortedMap = new LinkedHashMap<>();
        for (String key : sortedKeys) {
            sortedMap.put(key, map.get(key));
        }
        return sortedMap;

    }


    private String getstatus(int number) {
        String statusText = "";
        switch (number) {
            case 0:
                statusText = "待机";
                break;
            case 1:
                statusText = "停机";
                break;
            case 2:
                statusText = "并网";
                break;
            case 4:
                statusText = "故障";
                break;
            case 6:
                statusText = "检修";
                break;
            case 8:
                statusText = "限电";
                break;
            case 12:
                statusText = "离线";
                break;
            default:
                statusText = "未知";
                break;
        }
        return statusText;
    }

}