package com.aucma.base.service.impl;

import com.aucma.base.domain.RtDailyProdState;
import com.aucma.base.mapper.RtDailyProdStateMapper;
import com.aucma.base.service.IRtDailyProdStateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;

/**
 * 设备当日实时产量状态Service实现
 */
@Service
public class RtDailyProdStateServiceImpl implements IRtDailyProdStateService {

    @Autowired
    private RtDailyProdStateMapper rtDailyProdStateMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void incrementProduction(String deviceCode, String paramName, BigDecimal newVal, Date collectTime) {
        // 基础入参保护：RT 是核心统计表，空值直接跳过比写入脏数据更安全。
        if (deviceCode == null || paramName == null || newVal == null || collectTime == null) {
            return;
        }

        // 统一把采集时间截断到自然日，保证同一天的数据只落到一行 RT 记录中。
        Date prodDate = truncateToDay(collectTime);
        // 先加行锁再算差值，避免高频采集并发更新时把当天累计算乱。
        RtDailyProdState state = rtDailyProdStateMapper.selectForUpdate(prodDate, deviceCode, paramName);
        Date now = new Date();
        if (state == null) {
            RtDailyProdState insertState = new RtDailyProdState();
            insertState.setProdDate(prodDate);
            insertState.setDeviceCode(deviceCode);
            insertState.setParamName(paramName);
            // 为什么这样做：当天首条只建立基准值，避免把历史累计值误算成当天产量。
            insertState.setLastParamVal(newVal);
            // 首条只是“当前计数器读数”，不是“今天新增量”，所以当天累计必须从 0 开始。
            insertState.setCurrentTotal(BigDecimal.ZERO);
            insertState.setResetCount(0L);
            insertState.setDirtyFlag(0L);
            insertState.setLastCollectTime(collectTime);
            insertState.setUpdateTime(now);
            rtDailyProdStateMapper.insertRtDailyProdState(insertState);
            return;
        }

        BigDecimal lastParamVal = state.getLastParamVal() == null ? BigDecimal.ZERO : state.getLastParamVal();
        BigDecimal currentTotal = state.getCurrentTotal() == null ? BigDecimal.ZERO : state.getCurrentTotal();
        Long resetCount = state.getResetCount() == null ? 0L : state.getResetCount();

        BigDecimal delta = BigDecimal.ZERO;
        if (newVal.compareTo(lastParamVal) > 0) {
            // 正常递增场景：新旧差值就是这次采样间隔内的真实新增量。
            delta = newVal.subtract(lastParamVal);
        } else if (newVal.compareTo(lastParamVal) < 0) {
            // 为什么这样做：计数器回退时把当前值视为重置后的新增量，避免丢产量。
            delta = newVal;
            resetCount += 1;
        }

        // 无论是否产生新增量，都要刷新最后一次参数值和采集时间，
        // 否则下一次比较会基于旧值，导致后续累计偏大或偏小。
        state.setLastParamVal(newVal);
        state.setCurrentTotal(currentTotal.add(delta));
        state.setResetCount(resetCount);
        state.setLastCollectTime(collectTime);
        state.setUpdateTime(now);
        rtDailyProdStateMapper.updateRtDailyProdState(state);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void overwriteTodayProduction(String deviceCode, String paramName, BigDecimal todayTotal, Date collectTime) {
        // OLD 设备走人工录入，先做空值保护，避免把不完整请求直接覆盖掉 RT。
        if (deviceCode == null || paramName == null || todayTotal == null || collectTime == null) {
            return;
        }

        // OLD 设备虽然是人工录入，但 RT 仍然按“设备 + 自然日”唯一落一条事实记录。
        Date prodDate = truncateToDay(collectTime);
        // 先锁行再更新，避免同一台 OLD 设备被连续提交两次时互相覆盖顺序异常。
        RtDailyProdState state = rtDailyProdStateMapper.selectForUpdate(prodDate, deviceCode, paramName);
        Date now = new Date();
        if (state == null) {
            RtDailyProdState insertState = new RtDailyProdState();
            insertState.setProdDate(prodDate);
            insertState.setDeviceCode(deviceCode);
            insertState.setParamName(paramName);
            // 为什么这样做：OLD 设备首笔上报就是“今天已经产了多少”，
            // 这里必须直接落成当天总量，不能再像累计计数器那样只建基线。
            insertState.setLastParamVal(todayTotal);
            insertState.setCurrentTotal(todayTotal);
            insertState.setResetCount(0L);
            insertState.setDirtyFlag(0L);
            insertState.setLastCollectTime(collectTime);
            insertState.setUpdateTime(now);
            rtDailyProdStateMapper.insertRtDailyProdState(insertState);
            return;
        }

        // 为什么这样做：OLD 设备 PDA 填的是“今日累计值”，
        // 人工修正时应直接覆盖当天事实值，避免差值累加把产量越改越大。
        // 这里 lastParamVal 与 currentTotal 同时覆盖，
        // 是为了让 RT 在“展示值”和“后续再覆盖时的基线值”上始终保持一致。
        state.setLastParamVal(todayTotal);
        state.setCurrentTotal(todayTotal);
        state.setLastCollectTime(collectTime);
        state.setUpdateTime(now);
        rtDailyProdStateMapper.updateRtDailyProdState(state);
    }

    private Date truncateToDay(Date collectTime) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(collectTime);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }
}
