package com.aucma.base.service.impl;

import com.aucma.base.domain.BaseDeviceParamVal;
import com.aucma.base.mapper.BaseDeviceParamValMapper;
import com.aucma.base.service.IBaseDeviceParamValService;
import com.aucma.base.service.IRtDailyProdStateService;
import com.aucma.base.support.DeviceParamTableRouter;
import com.aucma.common.utils.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * 设备工艺参数当前值Service业务层处理
 */
@Service
public class BaseDeviceParamValServiceImpl implements IBaseDeviceParamValService {

    private static final Logger log = LoggerFactory.getLogger(BaseDeviceParamValServiceImpl.class);
    private static final String DAILY_OUTPUT_PARAM_NAME = "机台状态-实际产出数量";
    private static final String PDA_DAILY_TOTAL_PARAM_NAME = "生产计数-当前日期生产总数";
    private static final DateTimeFormatter QUERY_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Autowired
    private BaseDeviceParamValMapper baseDeviceParamValMapper;

    @Autowired
    private DeviceParamTableRouter deviceParamTableRouter;

    @Autowired
    private IRtDailyProdStateService rtDailyProdStateService;

    @Override
    public BaseDeviceParamVal selectBaseDeviceParamValByRecordId(String recordId) {
        return baseDeviceParamValMapper.selectBaseDeviceParamValByRecordId(recordId);
    }

    @Override
    public List<BaseDeviceParamVal> selectBaseDeviceParamValList(BaseDeviceParamVal baseDeviceParamVal) {
        return baseDeviceParamValMapper.selectBaseDeviceParamValList(baseDeviceParamVal);
    }

    @Override
    public List<BaseDeviceParamVal> selectLatestBaseDeviceParamValList(BaseDeviceParamVal baseDeviceParamVal) {
        try {
            if (baseDeviceParamVal == null) {
                baseDeviceParamVal = new BaseDeviceParamVal();
            }
            if (baseDeviceParamVal.getBeginTime() == null && baseDeviceParamVal.getEndTime() == null) {
                Date end = new Date();
                Date begin = DateUtils.addHours(end, -24);
                baseDeviceParamVal.setBeginTime(begin);
                baseDeviceParamVal.setEndTime(end);
            }
            baseDeviceParamVal.setTableSuffixes(deviceParamTableRouter.resolveReadTableSuffixes(
                    baseDeviceParamVal.getBeginTime(), baseDeviceParamVal.getEndTime()));
            List<BaseDeviceParamVal> list = baseDeviceParamValMapper.selectLatestBaseDeviceParamValList(baseDeviceParamVal);
            return list != null ? list : Collections.emptyList();
        } catch (Exception e) {
            log.error("查询设备参数最新值失败: {}", e.getMessage(), e);
            return Collections.emptyList();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertBaseDeviceParamVal(BaseDeviceParamVal baseDeviceParamVal) {
        if (baseDeviceParamVal == null) {
            return 0;
        }
        // 统一补齐时间，保证后续分表路由、RT 更新、追溯查询都以服务端确定的时间为准。
        fillTimeIfAbsent(baseDeviceParamVal);
        // 根据设备类型与采集时间决定落旧表还是月分表，避免调用方各自拼表名导致口径分叉。
        String tableName = deviceParamTableRouter.resolveWriteTable(
                baseDeviceParamVal.getDeviceCode(), baseDeviceParamVal.getCollectTime());

        if ("BASE_DEVICE_PARAM_VAL".equals(tableName)) {
            // OLD 设备与人工补录统一进单表，便于人工追溯和 PDA 问题排查。
            int rows = baseDeviceParamValMapper.insertBaseDeviceParamVal(baseDeviceParamVal);
            // 单表写入成功后，按业务语义把当天 RT 同步到统计事实层。
            syncRtStateIfNeeded(baseDeviceParamVal, rows);
            return rows;
        }

        // 月分表 record_id 依赖字符串业务唯一标识，不能再沿用旧表序列语义。
        fillPartitionRecordIdIfAbsent(baseDeviceParamVal);
        return baseDeviceParamValMapper.insertPartitionBaseDeviceParamVal(tableName, baseDeviceParamVal);
    }

    @Override
    public int updateBaseDeviceParamVal(BaseDeviceParamVal baseDeviceParamVal) {
        return baseDeviceParamValMapper.updateBaseDeviceParamVal(baseDeviceParamVal);
    }

    @Override
    public int deleteBaseDeviceParamValByRecordIds(String[] recordIds) {
        return baseDeviceParamValMapper.deleteBaseDeviceParamValByRecordIds(recordIds);
    }

    @Override
    public int deleteBaseDeviceParamValByRecordId(String recordId) {
        return baseDeviceParamValMapper.deleteBaseDeviceParamValByRecordId(recordId);
    }

    @Override
    public Map<String, Object> selectDeviceStatusStatistics() {
        try {
            Map<String, Object> params = buildRangeParams(DateUtils.addHours(new Date(), -2), new Date());
            Map<String, Object> result = baseDeviceParamValMapper.selectDeviceStatusStatistics(params);
            if (result == null) {
                result = new HashMap<>();
                result.put("TOTALCOUNT", 0);
                result.put("RUNNINGCOUNT", 0);
                result.put("STOPPEDCOUNT", 0);
                result.put("STANDBYCOUNT", 0);
                result.put("ALARMCOUNT", 0);
                result.put("NOTSTARTEDCOUNT", 0);
            }
            int total = getIntValue(result.get("TOTALCOUNT"));
            int running = getIntValue(result.get("RUNNINGCOUNT"));
            String runningRate = total > 0 ? String.format("%.2f%%", (running * 100.0 / total)) : "0%";
            result.put("runningRate", runningRate);
            return result;
        } catch (Exception e) {
            log.error("查询设备状态统计失败: {}", e.getMessage(), e);
            Map<String, Object> result = new HashMap<>();
            result.put("TOTALCOUNT", 0);
            result.put("RUNNINGCOUNT", 0);
            result.put("STOPPEDCOUNT", 0);
            result.put("STANDBYCOUNT", 0);
            result.put("ALARMCOUNT", 0);
            result.put("NOTSTARTEDCOUNT", 0);
            result.put("runningRate", "0%");
            return result;
        }
    }

    @Override
    public List<Map<String, Object>> selectDeviceStatusList() {
        try {
            Map<String, Object> params = buildRangeParams(DateUtils.addHours(new Date(), -2), new Date());
            List<Map<String, Object>> list = baseDeviceParamValMapper.selectDeviceStatusList(params);
            return list != null ? list : Collections.emptyList();
        } catch (Exception e) {
            log.error("查询设备状态列表失败: {}", e.getMessage(), e);
            return Collections.emptyList();
        }
    }

    @Override
    public List<Map<String, Object>> selectDeviceStartTimeList() {
        try {
            Date now = new Date();
            Map<String, Object> params = buildRangeParams(now, now);
            List<Map<String, Object>> list = baseDeviceParamValMapper.selectDeviceStartTimeList(params);
            return list != null ? list : Collections.emptyList();
        } catch (Exception e) {
            log.error("查询设备开机时间列表失败: {}", e.getMessage(), e);
            return Collections.emptyList();
        }
    }

    @Override
    public List<BaseDeviceParamVal> selectTraceList(String deviceCode, String paramCode, String startTime, String endTime) {
        try {
            Map<String, Object> params = buildStringRangeParams(deviceCode, paramCode, startTime, endTime);
            List<BaseDeviceParamVal> list = baseDeviceParamValMapper.selectTraceList(params);
            return list != null ? list : Collections.emptyList();
        } catch (Exception e) {
            log.error("参数追溯查询失败: {}", e.getMessage(), e);
            return Collections.emptyList();
        }
    }

    @Override
    public Map<String, Object> getSPCData(String deviceCode, String paramCode, String startTime, String endTime) {
        Map<String, Object> result = new HashMap<>();
        result.put("deviceCode", deviceCode);
        result.put("paramCode", paramCode);

        try {
            Map<String, Object> params = buildStringRangeParams(deviceCode, paramCode, startTime, endTime);
            String paramName = baseDeviceParamValMapper.selectParamNameByCodeFromSources(params);
            result.put("paramName", paramName != null ? paramName : paramCode);

            List<Double> values = baseDeviceParamValMapper.selectParamHistoryValues(params);
            if (values == null || values.isEmpty()) {
                result.put("values", Collections.emptyList());
                result.put("sampleSize", 0);
                result.put("mean", 0.0);
                result.put("stdDev", 0.0);
                result.put("cpk", 0.0);
                result.put("ucl", 0.0);
                result.put("lcl", 0.0);
                result.put("cl", 0.0);
                return result;
            }

            result.put("values", values);
            result.put("sampleSize", values.size());

            double mean = calculateMean(values);
            double stdDev = calculateStdDev(values, mean);
            double ucl = mean + 3 * stdDev;
            double lcl = mean - 3 * stdDev;

            result.put("mean", Math.round(mean * 1000.0) / 1000.0);
            result.put("stdDev", Math.round(stdDev * 1000.0) / 1000.0);
            result.put("ucl", Math.round(ucl * 1000.0) / 1000.0);
            result.put("lcl", Math.round(lcl * 1000.0) / 1000.0);
            result.put("cl", Math.round(mean * 1000.0) / 1000.0);

            double usl = mean * 1.1;
            double lsl = mean * 0.9;
            double cpk = calculateCPK(mean, stdDev, usl, lsl);
            result.put("cpk", Math.round(cpk * 100.0) / 100.0);
            result.put("usl", Math.round(usl * 1000.0) / 1000.0);
            result.put("lsl", Math.round(lsl * 1000.0) / 1000.0);
            return result;
        } catch (Exception e) {
            log.error("获取SPC分析数据失败: {}", e.getMessage(), e);
            result.put("error", e.getMessage());
            return result;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int upsertTodayParamValue(BaseDeviceParamVal baseDeviceParamVal) {
        if (baseDeviceParamVal == null) {
            return 0;
        }
        fillTimeIfAbsent(baseDeviceParamVal);
        return insertBaseDeviceParamVal(baseDeviceParamVal);
    }

    private void fillTimeIfAbsent(BaseDeviceParamVal entity) {
        Date now = new Date();
        if (entity.getCollectTime() == null) {
            entity.setCollectTime(now);
        }
        if (entity.getRecordTime() == null) {
            entity.setRecordTime(now);
        }
    }

    private void fillPartitionRecordIdIfAbsent(BaseDeviceParamVal entity) {
        if (entity.getRecordId() == null || entity.getRecordId().trim().isEmpty()) {
            // 为什么这样做：自动采集月表要求字符串型业务唯一标识，手工补录时也要保持同一语义。
            entity.setRecordId(UUID.randomUUID().toString());
        }
    }

    private void syncRtStateIfNeeded(BaseDeviceParamVal entity, int rows) {
        // 插入失败时绝不能更新 RT，否则会出现“源表没写成、看板先涨了”的脏数据。
        if (rows <= 0) {
            return;
        }
        // RT 的 Java 同步逻辑只处理 OLD 设备；NEW 设备由月表触发器维护。
        if (!deviceParamTableRouter.isOldDevice(entity.getDeviceCode())) {
            return;
        }
        // 这里只接产量相关参数，避免运行时间、三色灯等无关参数误触发产量累计。
        if (!DAILY_OUTPUT_PARAM_NAME.equals(entity.getParamName())
                && !PDA_DAILY_TOTAL_PARAM_NAME.equals(entity.getParamName())) {
            return;
        }
        try {
            BigDecimal newVal = new BigDecimal(entity.getParamValue());
            if (PDA_DAILY_TOTAL_PARAM_NAME.equals(entity.getParamName())) {
                // 为什么这样做：OLD 设备源表里保存的是“今天已经产了多少”，
                // RT 里要保留的是当天权威总量，因此这里必须用覆盖模式同步。
                rtDailyProdStateService.overwriteTodayProduction(
                        entity.getDeviceCode(), DAILY_OUTPUT_PARAM_NAME, newVal, entity.getCollectTime());
                return;
            }
            // 为什么这样做：OLD 设备只走 Java 写入链路，不在这里同步 RT 就会让 Board4 永远少算 OLD 设备产量。
            if (DAILY_OUTPUT_PARAM_NAME.equals(entity.getParamName())) {
                // 为什么这样做：保留旧口径兼容能力，避免历史链路仍写累计计数器参数时直接失效。
                rtDailyProdStateService.incrementProduction(
                        entity.getDeviceCode(), entity.getParamName(), newVal, entity.getCollectTime());
            }
        } catch (NumberFormatException ex) {
            // 源值非法时宁可跳过 RT，也不能把非数字转成 0 覆盖掉当天产量。
            log.warn("OLD设备产量值不是合法数字，跳过RT同步 | deviceCode={}, paramValue={}",
                    entity.getDeviceCode(), entity.getParamValue());
        }
    }

    private Map<String, Object> buildRangeParams(Date startTime, Date endTime) {
        Map<String, Object> params = new HashMap<>();
        params.put("tableSuffixes", deviceParamTableRouter.resolveReadTableSuffixes(startTime, endTime));
        return params;
    }

    private Map<String, Object> buildStringRangeParams(String deviceCode, String paramCode, String startTime, String endTime) {
        Map<String, Object> params = new HashMap<>();
        params.put("deviceCode", deviceCode);
        params.put("paramCode", paramCode);
        params.put("startTime", startTime);
        params.put("endTime", endTime);
        params.put("tableSuffixes", deviceParamTableRouter.resolveReadTableSuffixes(
                parseQueryDate(startTime), parseQueryDate(endTime)));
        return params;
    }

    private Date parseQueryDate(String dateTime) {
        if (dateTime == null || dateTime.trim().isEmpty()) {
            return null;
        }
        LocalDateTime localDateTime = LocalDateTime.parse(dateTime.trim(), QUERY_TIME_FORMATTER);
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    private int getIntValue(Object value) {
        if (value instanceof Number) {
            return ((Number) value).intValue();
        }
        if (value == null) {
            return 0;
        }
        try {
            return Integer.parseInt(value.toString());
        } catch (NumberFormatException ex) {
            return 0;
        }
    }

    private double calculateMean(List<Double> values) {
        if (values == null || values.isEmpty()) {
            return 0.0;
        }
        double sum = 0.0;
        for (Double value : values) {
            if (value != null) {
                sum += value;
            }
        }
        return sum / values.size();
    }

    private double calculateStdDev(List<Double> values, double mean) {
        if (values == null || values.size() < 2) {
            return 0.0;
        }
        double sumSquares = 0.0;
        for (Double value : values) {
            if (value != null) {
                sumSquares += Math.pow(value - mean, 2);
            }
        }
        return Math.sqrt(sumSquares / (values.size() - 1));
    }

    private double calculateCPK(double mean, double stdDev, double usl, double lsl) {
        if (stdDev == 0) {
            return 0.0;
        }
        double cpUpper = (usl - mean) / (3 * stdDev);
        double cpLower = (mean - lsl) / (3 * stdDev);
        return Math.min(cpUpper, cpLower);
    }
}
