package com.testor.module.hazard.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.testor.biz.sys.dict.data.model.domain.SysDictData;
import com.testor.common.util.DangerousOperationValidator;
import com.testor.module.hazard.dao.THazardWorkPlanDao;
import com.testor.module.hazard.dao.THazardWorkPlanExpiredLogDao;
import com.testor.module.hazard.model.domain.THazardWorkPlan;
import com.testor.module.hazard.model.domain.THazardWorkPlanExpiredLog;
import com.testor.module.hazard.model.enums.WorkPlanStatusEnum;
import com.testor.module.hazard.service.THazardWorkPlanExpiredLogService;
import com.testor.module.sys.service.NewSysDictDataService;
import com.tongtech.tfw.backend.common.models.supers.SuperServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THazardWorkPlanExpiredLogDao, THazardWorkPlanExpiredLog> implements THazardWorkPlanExpiredLogService {

    private static final String DICT_ENABLE_ID = "1";
    private static final String DICT_ENABLE_VALUE = "可以执行";

    // 字典id
    private static final String DICT_WORK_TYPE = "42a87414a06a4f57b9d3ffb1907284b4";
    private static final String DICT_WORK_LEVEL = "222842ea3bb4468bbbeaa3856a4f2731";

    @Autowired
    private THazardWorkPlanDao hazardWorkPlanDao;

    @Autowired
    private NewSysDictDataService dictDataService;

    @Autowired
    private THazardWorkPlanExpiredLogDao hazardWorkPlanExpiredLogDao;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private HistoryService historyService;

    // 缓存：key = dictValue, value = SysDictData
    private final Map<String, SysDictData> workTypeCache = new ConcurrentHashMap<>();
    private final Map<String, SysDictData> workLevelCache = new ConcurrentHashMap<>();

    @PostConstruct
    public void init() {
        refreshDictCache();
    }

    /**
     * 定时刷新字典缓存（每 10 分钟刷新一次，保证字典变更能生效）
     */
    public void refreshDictCacheScheduled() {
        refreshDictCache();
    }

    private void refreshDictCache() {
        try {
            Map<String, SysDictData> typeMap = dictDataService.getDictId(DICT_WORK_TYPE)
                    .stream()
                    .collect(Collectors.toMap(SysDictData::getDictValue, Function.identity(), (a, b) -> a));
            Map<String, SysDictData> levelMap = dictDataService.getDictId(DICT_WORK_LEVEL)
                    .stream()
                    .collect(Collectors.toMap(SysDictData::getDictValue, Function.identity(), (a, b) -> a));

            workTypeCache.clear();
            workTypeCache.putAll(typeMap);

            workLevelCache.clear();
            workLevelCache.putAll(levelMap);

            log.info("HazardWorkPlanScheduler 字典缓存刷新成功：workType={}, workLevel={}",
                    workTypeCache.size(), workLevelCache.size());
        } catch (Exception ex) {
            log.error("刷新字典缓存失败", ex);
        }
    }

    /**
     * 每5分钟检查安全许可通过后仍在运行的作业
     */
    @Override
    public void refreshSwitch() {
        if (!isSchedulerEnabled()) {
            log.debug("refreshSwitch: 调度被禁用（字典值非 {}）", DICT_ENABLE_VALUE);
            return;
        }

        log.info("开始---refreshSwitch----查询所有危险作业已安全许可申请审批通过后未完成的作业");

        List<THazardWorkPlan> plans;
        try {
            plans = hazardWorkPlanDao.selectHazardLicensePassTime();
        } catch (Exception e) {
            log.error("查询 selectHazardLicensePassTime 失败", e);
            return;
        }

        if (plans == null || plans.isEmpty()) {
            log.info("refreshSwitch: 无需处理的计划");
            return;
        }

        for (THazardWorkPlan plan : plans) {
            try {
                processPlanWithCaches(plan);
            } catch (Exception e) {
                // 单个 plan 异常不影响其他计划
                log.error("处理计划异常，planId={}, code={}", plan == null ? null : plan.getId(),
                        plan == null ? null : plan.getCode(), e);
            }
        }

        log.info("结束---refreshSwitch-----查询所有危险作业已安全许可申请审批通过后未完成的作业");
    }




    /****************************
     * 核心处理函数
     ****************************/

    private void processPlanWithCaches(THazardWorkPlan plan) {
        if (plan == null) {
            return;
        }

        // 从缓存读取字典，减少 DB 查询
        SysDictData workTypeDict = workTypeCache.get(plan.getWorkType());
        SysDictData workLevelDict = workLevelCache.get(plan.getWorkLevel());

        if (workTypeDict == null || workLevelDict == null) {
            log.warn("字典缺失，跳过处理 planId={}, workType={}, workLevel={}",
                    plan.getId(), plan.getWorkType(), plan.getWorkLevel());
            return;
        }

        processHazardWorkPlan(plan, workTypeDict, workLevelDict);
    }

    public void processHazardWorkPlan(THazardWorkPlan plan, SysDictData workTypeDict, SysDictData workLevelDict) {
        if (workTypeDict == null || workLevelDict == null || plan == null) {
            return;
        }

        String workTypeName = workTypeDict.getDictValue();
        String dangerLevelName = workLevelDict.getDictValue();

        DangerousOperationValidator.OperationType operationType =
                DangerousOperationValidator.OperationType.fromDictValue(workTypeName);
        DangerousOperationValidator.DangerLevel dangerLevel =
                DangerousOperationValidator.DangerLevel.fromDictValue(dangerLevelName);

        if (operationType == null || dangerLevel == null) {
            log.warn("无法识别的作业类型或危险等级: {} - {} for planId={}", workTypeName, dangerLevelName, plan.getId());
            return;
        }

        // 计算从审批通过到现在的小时数
        double actualHours = calculateHoursBetween(plan.getHazardLicensePassTime());

        // 验证规则
        String validationResult = DangerousOperationValidator.validateOperation(
                workTypeName, dangerLevelName, actualHours, plan);

        if (!"在安全时间内".equals(validationResult)) {
            handleExpiredOperation(plan, validationResult, operationType, dangerLevel, actualHours);
        } else {
            log.debug("作业在安全时间内, planId={}, hours={}", plan.getId(), actualHours);
        }
    }

    /**
     * 处理过期（核心步骤：记录日志 -> 停止流程 -> 更新业务状态）
     */
    @Transactional
    public void handleExpiredOperation(THazardWorkPlan plan,
                                       String validationResult,
                                       DangerousOperationValidator.OperationType operationType,
                                       DangerousOperationValidator.DangerLevel dangerLevel,
                                       double actualHours) {

        log.warn("作业过期 - planId={}, code={}, type={}, level={}, hours={}, reason={}",
                plan.getId(), plan.getCode(),
                operationType == null ? null : operationType.getName(),
                dangerLevel == null ? null : dangerLevel.getLevel(),
                actualHours, validationResult);

        // 1) 记录过期日志
        try {
            THazardWorkPlanExpiredLog expiredLog = new THazardWorkPlanExpiredLog();
            expiredLog.setPlanId(plan.getId());
            expiredLog.setCode(plan.getCode());
            expiredLog.setWorkType(operationType == null ? null : operationType.getName());
            expiredLog.setDangerLevel(dangerLevel == null ? null : dangerLevel.getLevel());
            expiredLog.setExpiredReason("作业未在安全时间内完成: " + validationResult);
            expiredLog.setBeforeExpiration(plan.getStatus());
            expiredLog.setHazardLicensePassTime(plan.getHazardLicensePassTime());
            expiredLog.setActualDurationHours(BigDecimal.valueOf(actualHours));
            expiredLog.setPlanCreateDate(plan.getCreateDate());
            expiredLog.setCreateBy("system-auto");
            expiredLog.setCreateDate(new Date());

            hazardWorkPlanExpiredLogDao.insert(expiredLog);
        } catch (Exception e) {
            log.error("插入过期日志失败，planId={}", plan.getId(), e);
        }

        // 2) 停止 Flowable 流程
        stopFlowableProcess(plan);

        // 3) 更新业务状态
        try {
            updateOperationStatus(plan, WorkPlanStatusEnum.TIMEOUT_CANCELED.getValue());
        } catch (Exception e) {
            log.error("更新作业状态失败，planId={}, code={}", plan.getId(), plan.getCode(), e);
        }
    }

    private void handleThreeDaysExpired(THazardWorkPlan plan, double actualHours) {
        log.warn("3天过期 - planId={}, code={}, hoursSinceCreation={}",
                plan.getId(), plan.getCode(), actualHours);

        // 记录日志
        try {
            THazardWorkPlanExpiredLog expiredLog = new THazardWorkPlanExpiredLog();
            expiredLog.setPlanId(plan.getId());
            expiredLog.setCode(plan.getCode());
            expiredLog.setWorkType(plan.getWorkType());
            expiredLog.setDangerLevel(plan.getWorkLevel());
            expiredLog.setExpiredReason("作业创建后3天内未完成，需要重新发起申请");
            expiredLog.setBeforeExpiration(plan.getWorkStatus());
            expiredLog.setHazardLicensePassTime(plan.getHazardLicensePassTime());
            expiredLog.setActualDurationHours(BigDecimal.valueOf(actualHours));
            expiredLog.setPlanCreateDate(plan.getCreateDate());
            expiredLog.setStatus("0");
            expiredLog.setCreateBy("system-relaxed");
            expiredLog.setCreateDate(new Date());

            hazardWorkPlanExpiredLogDao.insert(expiredLog);
        } catch (Exception e) {
            log.error("记录3天过期日志失败，planId={}", plan.getId(), e);
        }

        // 停止流程并更新状态
        stopFlowableProcess(plan);
        try {
            // 更新状态
            updateOperationStatus(plan, WorkPlanStatusEnum.TIMEOUT_CANCELED.getValue());
        } catch (Exception e) {
            log.error("3天过期后更新作业状态失败，planId={}", plan.getId(), e);
        }
    }

    /**
     * 检查调度是否启用（通过字典开关）
     */
    private boolean isSchedulerEnabled() {
        try {
            List<SysDictData> dict = dictDataService.getDictId(DICT_ENABLE_ID);
            if (dict != null && !dict.isEmpty()) {
                return DICT_ENABLE_VALUE.equals(dict.get(0).getDictValue());
            }
        } catch (Exception e) {
            log.error("检查调度开关失败", e);
        }
        return false;
    }

    /**
     * 计算从 start 到当前的小时数（对 null 做防护）
     */
    private double calculateHoursBetween(Date start) {
        if (start == null) {
            return 0d;
        }
        long diffMillis = System.currentTimeMillis() - start.getTime();
        return diffMillis / (1000.0 * 60 * 60);
    }

    /**
     * 停止流程（更稳健的实现：先查询流程实例是否存在，再删除运行时并删除历史）
     * 内部捕获异常，保证调度器稳定运行。
     *
     * 注意：如果你希望在删除历史前保留审计，请删除 historyService.deleteHistoricProcessInstance 调用。
     */
    private void stopFlowableProcess(THazardWorkPlan plan) {
        if (plan == null) {
            return;
        }
        String processInstanceId = plan.getProcessId(); // 保持你当前字段名
        if (StringUtils.isBlank(processInstanceId)) {
            log.debug("stopFlowableProcess: processId 为空，跳过，planId={}", plan.getId());
            return;
        }

        try {
            ProcessInstance pi = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .singleResult();

            if (pi == null) {
                log.info("流程实例不存在或已结束，跳过删除，processInstanceId={}, planId={}", processInstanceId, plan.getId());
                // 如果你希望同时清理历史未完成记录，可尝试删除历史（此处不做以免误删）
                try {
                    // 如果确实需要删除历史：historyService.deleteHistoricProcessInstance(processInstanceId);
                } catch (Exception ex) {
                    log.warn("尝试删除历史流程失败，processInstanceId={}", processInstanceId, ex);
                }
                return;
            }

            // 删除运行时流程实例
            runtimeService.deleteProcessInstance(processInstanceId, "系统超时自动取消");

            // 根据业务决定是否删除历史记录；此处删除历史，避免残留（可注释）
            try {
                historyService.deleteHistoricProcessInstance(processInstanceId);
            } catch (Exception e) {
                // 删除历史非关键路径：记录日志即可
                log.warn("删除历史流程实例失败 processInstanceId={}, err={}", processInstanceId, e.getMessage());
            }

            log.info("已停止流程实例 processInstanceId={} (planId={})", processInstanceId, plan.getId());
        } catch (Exception e) {
            log.error("停止流程失败 processInstanceId={}, planId={}", processInstanceId, plan.getId(), e);
        }
    }

    /**
     * 更新业务作业状态（局部更新，避免覆盖其他字段）
     * 如果你希望该更新与过期日志插入为同一事务，请在本方法或调用端加 @Transactional
     */
    private void updateOperationStatus(THazardWorkPlan plan, String newStatus) {
        if (plan == null) {
            return;
        }
        try {
            THazardWorkPlan update = new THazardWorkPlan();
            update.setId(plan.getId());
            update.setWorkStatus(newStatus);
            update.setUpdateDate(new Date()); // 假设字段名为 updateDate；如果不同请改为你项目字段
            hazardWorkPlanDao.updateById(update);
            log.info("更新作业状态成功 planId={}, newStatus={}", plan.getId(), newStatus);
        } catch (Exception e) {
            log.error("更新作业状态失败 planId={}, newStatus={}", plan.getId(), newStatus, e);
            throw e;
        }
    }


    @Override
    public THazardWorkPlanExpiredLog selectByPlanId(String planId) {
        return hazardWorkPlanExpiredLogDao.selectOne(new LambdaQueryWrapper<THazardWorkPlanExpiredLog>()
                .eq(THazardWorkPlanExpiredLog::getPlanId, planId));
    }

}
