package com.aucma.base.service.impl;

import com.aucma.base.domain.BaseDeviceParamVal;
import com.aucma.base.mapper.DeviceParamReadFacadeMapper;
import com.aucma.base.service.IDeviceParamReadFacade;
import com.aucma.base.support.DeviceParamTableRouter;
import com.aucma.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * 统一旁路参数读取Service
 */
@Service
public class DeviceParamReadFacadeImpl implements IDeviceParamReadFacade {

    private static final int DEFAULT_TIME_POINT_WINDOW_SECONDS = 60;
    private static final int LATEST_LOOKBACK_MONTHS = 12;

    @Autowired
    private DeviceParamReadFacadeMapper deviceParamReadFacadeMapper;

    @Autowired
    private DeviceParamTableRouter deviceParamTableRouter;

    @Override
    public BaseDeviceParamVal getLatestParamValue(String deviceCode, String paramCode, String paramName) {
        Map<String, Object> params = buildQueryParams(deviceCode, paramCode, paramName, buildLatestBeginTime(), new Date());
        // 为什么这样做：旁路消费方不应该继续感知 OLD 单表、NEW 月表和跨月范围，
        // latest 口径在这里统一收口后，预警/安灯/报表才能保持一致。
        return deviceParamReadFacadeMapper.selectLatestParamValue(params);
    }

    @Override
    public List<BaseDeviceParamVal> listParamsByTimePoint(String deviceCode, Date snapshotTime, int windowSeconds) {
        if (StringUtils.isEmpty(deviceCode) || snapshotTime == null) {
            return Collections.emptyList();
        }
        int effectiveWindowSeconds = windowSeconds > 0 ? windowSeconds : DEFAULT_TIME_POINT_WINDOW_SECONDS;
        Date beginTime = addSeconds(snapshotTime, -effectiveWindowSeconds);
        Date endTime = addSeconds(snapshotTime, effectiveWindowSeconds);
        Map<String, Object> params = buildQueryParams(deviceCode, null, null, beginTime, endTime);
        params.put("snapshotTime", snapshotTime);
        // 为什么这样做：工艺快照的本质是“某时间点附近最近一次参数状态”，
        // 所以这里统一用时间窗口 + 双源合并，而不是让快照模块直接写死旧表。
        List<BaseDeviceParamVal> paramList = deviceParamReadFacadeMapper.selectParamsByTimePoint(params);
        return paramList != null ? paramList : Collections.<BaseDeviceParamVal>emptyList();
    }

    @Override
    public List<Map<String, Object>> compareParamsByTimePoint(String deviceCode, Date timePoint1, Date timePoint2, int windowSeconds) {
        List<BaseDeviceParamVal> beforeList = listParamsByTimePoint(deviceCode, timePoint1, windowSeconds);
        List<BaseDeviceParamVal> afterList = listParamsByTimePoint(deviceCode, timePoint2, windowSeconds);
        if (beforeList.isEmpty() && afterList.isEmpty()) {
            return Collections.emptyList();
        }
        Map<String, BaseDeviceParamVal> beforeMap = toParamIndex(beforeList);
        Map<String, BaseDeviceParamVal> afterMap = toParamIndex(afterList);
        TreeMap<String, Map<String, Object>> compareMap = new TreeMap<String, Map<String, Object>>();
        fillCompareMap(compareMap, beforeMap, true);
        fillCompareMap(compareMap, afterMap, false);
        return new ArrayList<Map<String, Object>>(compareMap.values());
    }

    @Override
    public List<BaseDeviceParamVal> listHistoryParamValues(String deviceCode, String paramCode, String paramName,
                                                           Date beginTime, Date endTime, Integer limit) {
        Map<String, Object> params = buildQueryParams(deviceCode, paramCode, paramName, beginTime, endTime);
        params.put("limit", limit);
        List<BaseDeviceParamVal> paramList = deviceParamReadFacadeMapper.selectHistoryParamValues(params);
        return paramList != null ? paramList : Collections.<BaseDeviceParamVal>emptyList();
    }

    @Override
    public List<BaseDeviceParamVal> listHistoryParamValuesByNames(String deviceCode, List<String> paramNames,
                                                                  Date beginTime, Date endTime, Integer limit) {
        if (paramNames == null || paramNames.isEmpty()) {
            return Collections.emptyList();
        }
        Map<String, Object> params = buildQueryParams(deviceCode, null, null, beginTime, endTime);
        params.put("paramNames", paramNames);
        params.put("limit", limit);
        List<BaseDeviceParamVal> paramList = deviceParamReadFacadeMapper.selectHistoryParamValues(params);
        return paramList != null ? paramList : Collections.<BaseDeviceParamVal>emptyList();
    }

    @Override
    public List<Double> listNumericHistoryValues(String deviceCode, String paramCode, Date beginTime, Date endTime) {
        List<BaseDeviceParamVal> historyList = listHistoryParamValues(deviceCode, paramCode, null, beginTime, endTime, 1000);
        if (historyList.isEmpty()) {
            return Collections.emptyList();
        }
        List<Double> values = new ArrayList<Double>(historyList.size());
        for (BaseDeviceParamVal history : historyList) {
            Double numericValue = parseNumeric(history.getParamValue());
            if (numericValue != null) {
                values.add(numericValue);
            }
        }
        return values;
    }

    private Map<String, Object> buildQueryParams(String deviceCode, String paramCode, String paramName, Date beginTime, Date endTime) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("deviceCode", trimToNull(deviceCode));
        params.put("paramCode", trimToNull(paramCode));
        params.put("paramName", trimToNull(paramName));
        params.put("beginTime", beginTime);
        params.put("endTime", endTime);
        params.put("tableSuffixes", resolveReadTableSuffixes(beginTime, endTime));
        return params;
    }

    private List<String> resolveReadTableSuffixes(Date beginTime, Date endTime) {
        if (beginTime == null || endTime == null) {
            return Collections.emptyList();
        }
        return deviceParamTableRouter.resolveReadTableSuffixes(beginTime, endTime);
    }

    private Date buildLatestBeginTime() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MONTH, -LATEST_LOOKBACK_MONTHS);
        return calendar.getTime();
    }

    private Date addSeconds(Date baseTime, int seconds) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(baseTime);
        calendar.add(Calendar.SECOND, seconds);
        return calendar.getTime();
    }

    private Map<String, BaseDeviceParamVal> toParamIndex(List<BaseDeviceParamVal> paramList) {
        Map<String, BaseDeviceParamVal> paramIndex = new LinkedHashMap<String, BaseDeviceParamVal>();
        for (BaseDeviceParamVal paramVal : paramList) {
            paramIndex.put(resolveParamKey(paramVal), paramVal);
        }
        return paramIndex;
    }

    private void fillCompareMap(TreeMap<String, Map<String, Object>> compareMap, Map<String, BaseDeviceParamVal> paramMap, boolean beforeFlag) {
        for (Map.Entry<String, BaseDeviceParamVal> entry : paramMap.entrySet()) {
            BaseDeviceParamVal paramVal = entry.getValue();
            Map<String, Object> row = compareMap.get(entry.getKey());
            if (row == null) {
                row = new LinkedHashMap<String, Object>();
                row.put("paramCode", trimToNull(paramVal.getParamCode()));
                row.put("paramName", trimToNull(paramVal.getParamName()));
                compareMap.put(entry.getKey(), row);
            }
            if (beforeFlag) {
                row.put("beforeValue", paramVal.getParamValue());
            } else {
                row.put("afterValue", paramVal.getParamValue());
            }
            row.put("isChanged", isChanged(row.get("beforeValue"), row.get("afterValue")) ? "Y" : "N");
        }
    }

    private boolean isChanged(Object beforeValue, Object afterValue) {
        String beforeText = beforeValue == null ? null : beforeValue.toString();
        String afterText = afterValue == null ? null : afterValue.toString();
        if (beforeText == null) {
            return afterText != null;
        }
        return !beforeText.equals(afterText);
    }

    private String resolveParamKey(BaseDeviceParamVal paramVal) {
        if (paramVal == null) {
            return "";
        }
        if (StringUtils.isNotEmpty(paramVal.getParamCode())) {
            return "CODE:" + paramVal.getParamCode();
        }
        return "NAME:" + trimToNull(paramVal.getParamName());
    }

    private String trimToNull(String text) {
        if (text == null) {
            return null;
        }
        String trimmed = text.trim();
        return trimmed.isEmpty() ? null : trimmed;
    }

    private Double parseNumeric(String paramValue) {
        if (StringUtils.isEmpty(paramValue)) {
            return null;
        }
        try {
            return new BigDecimal(paramValue.trim()).doubleValue();
        } catch (NumberFormatException ex) {
            return null;
        }
    }
}
