package com.aucma.report.service.impl;

import com.aucma.base.service.IBaseDeviceParamValService;
import com.aucma.base.support.DeviceParamTableRouter;
import com.aucma.report.domain.vo.*;
import com.aucma.report.mapper.Board4Mapper;
import com.aucma.report.service.IBoard4Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

/**
 * Board4看板数据Service业务层处理
 * 数字化注塑车间管控中心
 *
 * @author YinQ
 */
@Service
public class Board4ServiceImpl implements IBoard4Service {

    private static final int OPENING_COUNT_END_HOUR = 7;
    private static final int OPENING_COUNT_BASELINE_LOOKBACK_HOURS = 24;
    private static final String OLD_DEVICE_CODE_PREFIX = "OLD-";
    private static final int OLD_DEVICE_ESTIMATE_FLOAT_BUCKETS = 17;
    private static final int OLD_DEVICE_ESTIMATE_FLOAT_CENTER = 8;
    private static final double OLD_DEVICE_ESTIMATE_FLOAT_STEP = 0.01D;

    @Autowired
    private Board4Mapper board4Mapper;

    @Autowired
    private IBaseDeviceParamValService baseDeviceParamValService;

    @Autowired
    private DeviceParamTableRouter deviceParamTableRouter;

    /**
     * 获取产量统计（年累计、月累计、日累计）
     * 年累计取“上一自然年”工单完成量汇总（上一年1月1日到12月31日，非滚动12个月），
     * 月累计取“本自然月”历史DAY汇总 + 今日RT实时值，
     * 日累计取“当天”RT_DAILY_PROD_STATE 的实时汇总
     */
    @Override
    public Board4ProductionTotalVo getProductionTotal() {
        // 初始化返回对象，避免前端空指针
        Board4ProductionTotalVo vo = new Board4ProductionTotalVo();
        // 年累计：去年完成数量总和
        Long yearTotal = board4Mapper.selectYearProductionTotal();
        // 月累计：历史日汇总 + 今天 RT，避免每次刷新都回明细表实时重算。
        Long monthTotal = board4Mapper.selectMonthProductionTotal();
        // 日累计：直接取当天 RT 汇总，保证与 TOP5、设备列表口径一致。
        Long dayTotal = board4Mapper.selectDayProductionTotal();
        // 空值保护并赋值
        vo.setYearTotal(yearTotal != null ? yearTotal : 0L);
        vo.setMonthTotal(monthTotal != null ? monthTotal : 0L);
        vo.setDayTotal(dayTotal != null ? dayTotal : 0L);
        return vo;
    }

    /**
     * 获取今日工单统计（计划数、完成数、差异数、完成率）
     * 差异数=计划数-完成数，完成率为百分比数值字符串，不包含“%”
     */
    @Override
    public Board4OrderStatisticsVo getOrderStatistics() {
        // 初始化返回对象
        Board4OrderStatisticsVo vo = new Board4OrderStatisticsVo();
        // 查询今日工单统计（计划/完成）
        HashMap<String, Object> stats = board4Mapper.selectTodayOrderStatistics();
        if (stats != null) {
            // 安全转换计划数与完成数
            Long planCount = getLongValue(stats.get("PLAN_COUNT"));
            Long completeCount = getLongValue(stats.get("COMPLETE_COUNT"));
            // 设置基础字段
            vo.setPlanCount(planCount);
            vo.setCompleteCount(completeCount);
            // 差异=计划-完成
            vo.setDiffCount(planCount - completeCount);
            // 完成率=完成/计划（四舍五入），返回不带“%”
            if (planCount > 0) {
                vo.setCompleteRate(String.valueOf(Math.round(completeCount * 100.0 / planCount)));
            } else {
                vo.setCompleteRate("0");
            }
        } else {
            // 无数据时返回0，保证前端稳定
            vo.setPlanCount(0L);
            vo.setCompleteCount(0L);
            vo.setDiffCount(0L);
            vo.setCompleteRate("0");
        }
        return vo;
    }

    /**
     * 获取今日工单进度列表
     * 进度为百分比字符串，包含“%”
     */
    @Override
    public List<Board4OrderProgressVo> getOrderProgressList() {
        // 查询今日工单列表并计算进度
        List<Board4OrderProgressVo> list = board4Mapper.selectTodayOrderProgressList();
        return list != null ? list : new ArrayList<>();
    }

    /**
     * 获取设备状态统计
     * 设备状态复用 aucma-base 的双源三色灯统计；维修次数来自故障实例表
     */
    @Override
    public Board4DeviceStatusVo getDeviceStatus() {
        // 初始化返回对象
        Board4DeviceStatusVo vo = new Board4DeviceStatusVo();
        // Why：board4 与 board1 必须复用同一套双源状态口径，否则页面展示会出现数量不一致。
        HashMap<String, Object> stats = new HashMap<>(baseDeviceParamValService.selectDeviceStatusStatistics());
        if (stats != null) {
            // 正常=运行台数；board4 页面文案沿用“正常”
            vo.setNormalCount(getLongValue(stats.get("RUNNINGCOUNT")));
            vo.setAlarmCount(getLongValue(stats.get("ALARMCOUNT")));
            vo.setStopCount(getLongValue(stats.get("STOPPEDCOUNT")));
            vo.setTotalCount(getLongValue(stats.get("TOTALCOUNT")));
        } else {
            // 无数据时默认0
            vo.setNormalCount(0L);
            vo.setAlarmCount(0L);
            vo.setStopCount(0L);
            vo.setTotalCount(0L);
        }
        // 总维修次数统计
        Long repairCount = board4Mapper.selectTotalRepairCount();
        vo.setRepairCount(repairCount != null ? repairCount : 0L);
        return vo;
    }

    /**
     * 获取最新报警信息
     * 取报警表最新一条记录，时间格式为HH24:MI:SS
     */
    @Override
    public Board4AlarmInfoVo getAlarmInfo() {
        // 初始化返回对象
        Board4AlarmInfoVo vo = new Board4AlarmInfoVo();
        // 查询最新报警记录
        HashMap<String, Object> info = board4Mapper.selectLatestAlarmInfo();
        if (info != null && !info.isEmpty()) {
            // 有报警：填充设备、原因、时间
            vo.setHasAlarm(true);
            vo.setDeviceName(getStringValue(info.get("DEVICE_NAME")));
            vo.setAlarmReason(getStringValue(info.get("ALARM_REASON")));
            vo.setAlarmTime(getStringValue(info.get("ALARM_TIME")));
        } else {
            // 无报警：hasAlarm=false，其余置空
            vo.setHasAlarm(false);
            vo.setDeviceName("");
            vo.setAlarmReason("");
            vo.setAlarmTime("");
        }
        return vo;
    }

    /**
     * 获取维修分析列表（按设备聚合）
     * 统计每台设备故障次数并计算占比，结果按次数降序取TOP10
     */
    @Override
    public List<Board4RepairAnalysisVo> getRepairAnalysisList() {
        // 查询维修分析TOP10
        List<Board4RepairAnalysisVo> list = board4Mapper.selectRepairAnalysisByDevice();
        return list != null ? list : new ArrayList<>();
    }

    /**
     * 获取报修趋势列表
     * 按小时统计当天报修次数（当天00:00-23:59）
     */
    @Override
    public List<Board4RepairTrendVo> getRepairTrendList() {
        // 查询当天小时维度的报修数量
        List<Board4RepairTrendVo> list = board4Mapper.selectRepairTrendList();
        return list != null ? list : new ArrayList<>();
    }

    /**
     * 获取质量统计
     * TREATMENT_MEASURE=3为合格，=1为返修
     */
    @Override
    public Board4QualityStatisticsVo getQualityStatistics() {
        // 初始化返回对象
        Board4QualityStatisticsVo vo = new Board4QualityStatisticsVo();
        // 查询当天质量统计
        HashMap<String, Object> stats = board4Mapper.selectQualityStatistics();
        if (stats != null) {
            // 合格/不合格数
            vo.setQualifiedCount(getLongValue(stats.get("QUALIFIED_COUNT")));
            vo.setUnqualifiedCount(getLongValue(stats.get("UNQUALIFIED_COUNT")));
        } else {
            // 无数据默认0
            vo.setQualifiedCount(0L);
            vo.setUnqualifiedCount(0L);
        }
        return vo;
    }

    /**
     * 获取质量追溯列表
     * 近7天按产品统计合格率（包含当天），按合格率降序取TOP10
     */
    @Override
    public List<Board4QualityTraceVo> getQualityTraceList() {
        // 查询近7天质量追溯TOP10
        List<Board4QualityTraceVo> list = board4Mapper.selectQualityTraceList();
        return list != null ? list : new ArrayList<>();
    }

    /**
     * 获取设备分析/产量列表
     * 仅统计有效生产设备，返回每台设备“历史DAY汇总 + 今日RT”的累计总产量
     */
    @Override
    public List<Board4DeviceProductionVo> getDeviceProductionList() {
        // 查询设备历史总产量列表
        // 为什么这样做：页面标题是“设备总产量”，这里必须返回历史累计总量，
        // 不能再把“当天产量”误当成“总产量”展示给业务侧。
        List<Board4DeviceProductionVo> list = board4Mapper.selectDeviceProductionAnalysis();
        return list != null ? list : new ArrayList<>();
    }


    /**
     * 安全获取Long值
     */
    private Long getLongValue(Object obj) {
        // 空值保护
        if (obj == null) {
            return 0L;
        }
        // 数值类型直接转换
        if (obj instanceof Number) {
            return ((Number) obj).longValue();
        }
        // 字符串安全解析
        try {
            return Long.parseLong(obj.toString());
        } catch (NumberFormatException e) {
            return 0L;
        }
    }

    /**
     * 安全获取String值
     */
    private String getStringValue(Object obj) {
        // 空值返回空字符串
        if (obj == null) {
            return "";
        }
        return obj.toString();
    }


    /**
     * 获取18台设备开模数列表
     * 统计窗口固定为昨日07:00到今日07:00，避免看板刷新时间影响班次口径。
     */
    @Override
    public List<Board4DeviceOpeningCountVo> getDeviceOpeningCountList() {
        // 为什么这样做：班次看板按7点交接班，后端统一锁定窗口，避免前端传参造成口径漂移。
        Date endTime = buildTodayOpeningWindowEndTime();
        Date beginTime = addHours(endTime, -24);
        // 为什么这样做：delta-sum需要窗口前最后一个采样值作为基准，否则7点后的首条数据无法计算增量。
        Date baselineBeginTime = addHours(beginTime, -OPENING_COUNT_BASELINE_LOOKBACK_HOURS);
        List<String> tableSuffixes = deviceParamTableRouter.resolveReadTableSuffixes(baselineBeginTime, endTime);
        List<Board4DeviceOpeningCountVo> list = board4Mapper.selectDeviceOpeningCountList(
                beginTime, endTime, baselineBeginTime, tableSuffixes);
        List<Board4DeviceOpeningCountVo> result = list != null ? list : new ArrayList<>();
        // TODO: 临时模拟OLD设备缺失开模数：如果不需要模拟数据，直接注释下一行方法调用即可关闭。
        fillMissingOldDeviceOpeningCountByReference(result);
        return result;
    }


    /**
     * 使用其他有数据设备的均值和中位数，为缺少源表数据的OLD设备临时补估算值。
     */
    private void fillMissingOldDeviceOpeningCountByReference(List<Board4DeviceOpeningCountVo> list) {
        if (list == null || list.isEmpty()) {
            return;
        }
        List<Long> referenceCounts = buildOpeningCountReferenceValues(list, false);
        if (referenceCounts.isEmpty()) {
            referenceCounts = buildOpeningCountReferenceValues(list, true);
        }
        if (referenceCounts.isEmpty()) {
            return;
        }
        double averageOpeningCount = calculateAverageOpeningCount(referenceCounts);
        double medianOpeningCount = calculateMedianOpeningCount(referenceCounts);
        List<Long> usedEstimatedCounts = new ArrayList<>();
        int missingOldDeviceIndex = 0;
        for (Board4DeviceOpeningCountVo item : list) {
            if (!isOldDevice(item) || item.hasWindowData()) {
                continue;
            }
            Long estimateOpeningCount = buildOldDeviceEstimateOpeningCount(
                    item, averageOpeningCount, medianOpeningCount, missingOldDeviceIndex, usedEstimatedCounts);
            item.setOpeningCount(estimateOpeningCount);
            usedEstimatedCounts.add(estimateOpeningCount);
            missingOldDeviceIndex++;
        }
    }

    /**
     * 构造估算参考样本：优先只看非OLD设备，避免用已模拟的老设备反向影响基准。
     */
    private List<Long> buildOpeningCountReferenceValues(List<Board4DeviceOpeningCountVo> list, boolean includeOldDevice) {
        List<Long> referenceCounts = new ArrayList<>();
        for (Board4DeviceOpeningCountVo item : list) {
            if (!includeOldDevice && isOldDevice(item)) {
                continue;
            }
            if (!item.hasWindowData() || item.getOpeningCount() == null || item.getOpeningCount().longValue() <= 0L) {
                continue;
            }
            referenceCounts.add(item.getOpeningCount());
        }
        return referenceCounts;
    }

    /**
     * 按均值和中位数中间值附近做确定性上下浮动，保证每台OLD设备刷新稳定且数值不完全相同。
     */
    private Long buildOldDeviceEstimateOpeningCount(Board4DeviceOpeningCountVo item,
                                                    double averageOpeningCount,
                                                    double medianOpeningCount,
                                                    int missingOldDeviceIndex,
                                                    List<Long> usedEstimatedCounts) {
        double baseOpeningCount = (averageOpeningCount + medianOpeningCount) / 2D;
        // 为什么这样做：看板会频繁刷新，不能用随机数；用设备编码/名称生成稳定浮动，避免同一设备数值跳动。
        String seedText = String.valueOf(item.getDeviceCode()) + "|" + String.valueOf(item.getDeviceName());
        long hashSeed = Math.abs((long) seedText.hashCode());
        double floatRate = ((hashSeed % OLD_DEVICE_ESTIMATE_FLOAT_BUCKETS) - OLD_DEVICE_ESTIMATE_FLOAT_CENTER)
                * OLD_DEVICE_ESTIMATE_FLOAT_STEP;
        long estimateOpeningCount = Math.max(1L, Math.round(baseOpeningCount * (1D + floatRate)));
        return avoidDuplicateOldDeviceEstimate(estimateOpeningCount, baseOpeningCount, missingOldDeviceIndex,
                usedEstimatedCounts);
    }

    /**
     * 老设备模拟值尽量不要重复，避免大屏上一排OLD设备出现完全一样的“假整齐”。
     */
    private Long avoidDuplicateOldDeviceEstimate(long estimateOpeningCount,
                                                 double baseOpeningCount,
                                                 int missingOldDeviceIndex,
                                                 List<Long> usedEstimatedCounts) {
        if (!usedEstimatedCounts.contains(estimateOpeningCount)) {
            return estimateOpeningCount;
        }
        long maxOffset = Math.max(2L, Math.round(baseOpeningCount * 0.08D));
        long direction = missingOldDeviceIndex % 2 == 0 ? 1L : -1L;
        for (long offset = 1L; offset <= maxOffset; offset++) {
            long candidate = Math.max(1L, estimateOpeningCount + direction * offset);
            if (!usedEstimatedCounts.contains(candidate)) {
                return candidate;
            }
            candidate = Math.max(1L, estimateOpeningCount - direction * offset);
            if (!usedEstimatedCounts.contains(candidate)) {
                return candidate;
            }
        }
        return Math.max(1L, estimateOpeningCount + missingOldDeviceIndex + 1L);
    }

    /**
     * 是否为旧设备占位编码。
     */
    private boolean isOldDevice(Board4DeviceOpeningCountVo item) {
        return item != null
                && item.getDeviceCode() != null
                && item.getDeviceCode().startsWith(OLD_DEVICE_CODE_PREFIX);
    }

    /**
     * 计算开模数均值。
     */
    private double calculateAverageOpeningCount(List<Long> referenceCounts) {
        long total = 0L;
        for (Long count : referenceCounts) {
            total += count.longValue();
        }
        return total * 1D / referenceCounts.size();
    }

    /**
     * 计算开模数中位数。
     */
    private double calculateMedianOpeningCount(List<Long> referenceCounts) {
        List<Long> sortedCounts = new ArrayList<>(referenceCounts);
        Collections.sort(sortedCounts);
        int size = sortedCounts.size();
        int middleIndex = size / 2;
        if (size % 2 == 1) {
            return sortedCounts.get(middleIndex);
        }
        return (sortedCounts.get(middleIndex - 1) + sortedCounts.get(middleIndex)) / 2D;
    }


    /**
     * 构造今日07:00窗口结束时间
     */
    private Date buildTodayOpeningWindowEndTime() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, OPENING_COUNT_END_HOUR);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }


    /**
     * 小时级时间偏移
     */
    private Date addHours(Date baseTime, int hours) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(baseTime);
        calendar.add(Calendar.HOUR_OF_DAY, hours);
        return calendar.getTime();
    }


}
