package com.ruoyi.ccdi.project.service.impl;

import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask;
import com.ruoyi.ccdi.project.domain.enums.TriggerType;
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
import com.ruoyi.common.exception.ServiceException;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import jakarta.annotation.Resource;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import lombok.extern.slf4j.Slf4j;

/**
 * 项目级流水标签重算协调器
 */
@Slf4j
@Component
public class ProjectBankTagRebuildCoordinator {

    private final ConcurrentHashMap<Long, Boolean> runningProjects = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<Long, Boolean> pendingAutoRerunProjects = new ConcurrentHashMap<>();

    @Resource
    private CcdiBankTagTaskMapper taskMapper;

    @Lazy
    @Resource
    private CcdiBankTagServiceImpl bankTagService;

    @Resource
    private ICcdiProjectService projectService;

    @Resource
    @Qualifier("tagRebuildExecutor")
    private Executor tagRebuildExecutor;

    /**
     * 提交手动重算
     *
     * @param projectId 项目ID
     * @param modelCode 模型编码
     * @param operator 操作人
     */
    public void submitManual(Long projectId, String modelCode, String operator) {
        log.info("【流水标签】手动重算开始排队: projectId={}, modelCode={}, operator={}",
            projectId, modelCode, operator);
        if (isProjectRunning(projectId)) {
            log.warn("【流水标签】项目已有运行中任务，拒绝手动重算: projectId={}, modelCode={}, operator={}",
                projectId, modelCode, operator);
            throw new ServiceException("当前项目标签正在重算中，请稍后再试");
        }

        projectService.ensureProjectCanStartTagging(projectId);
        executeWithLock(projectId, () -> bankTagService.rebuildProject(projectId, modelCode, operator, TriggerType.MANUAL));
    }

    /**
     * 提交自动重算
     *
     * @param projectId 项目ID
     * @param triggerType 触发类型
     */
    public void submitAuto(Long projectId, TriggerType triggerType) {
        CcdiBankTagTask runningTask = taskMapper.selectRunningTaskByProjectId(projectId);
        if (runningTask != null || runningProjects.containsKey(projectId)) {
            log.warn("【流水标签】项目正在重算，已标记完成后补跑: projectId={}, runningTaskId={}, triggerType={}",
                projectId, runningTask != null ? runningTask.getId() : null, triggerType);
            markNeedRerun(projectId, runningTask);
            return;
        }

        if (runningProjects.putIfAbsent(projectId, Boolean.TRUE) != null) {
            log.warn("【流水标签】项目自动重算已在排队，已标记完成后补跑: projectId={}, triggerType={}",
                projectId, triggerType);
            markNeedRerun(projectId, runningTask);
            return;
        }

        try {
            tagRebuildExecutor.execute(() -> executeAutoRebuild(projectId, triggerType));
            log.info("【流水标签】自动重算任务已异步提交: projectId={}, triggerType={}", projectId, triggerType);
        } catch (RuntimeException ex) {
            runningProjects.remove(projectId);
            throw ex;
        }
    }

    private void executeWithLock(Long projectId, Runnable action) {
        if (runningProjects.putIfAbsent(projectId, Boolean.TRUE) != null) {
            log.warn("【流水标签】项目已有运行中任务，拒绝获取项目锁: projectId={}", projectId);
            throw new ServiceException("当前项目标签正在重算中，请稍后再试");
        }
        log.info("【流水标签】获取项目重算锁成功: projectId={}", projectId);
        try {
            action.run();
        } finally {
            runningProjects.remove(projectId);
            log.info("【流水标签】释放项目重算锁: projectId={}", projectId);
        }
    }

    private boolean isProjectRunning(Long projectId) {
        return runningProjects.containsKey(projectId) || taskMapper.selectRunningTaskByProjectId(projectId) != null;
    }

    private void executeAutoRebuild(Long projectId, TriggerType triggerType) {
        try {
            projectService.ensureProjectCanStartTagging(projectId);
            boolean needRerun;
            do {
                Long taskId = bankTagService.rebuildProject(projectId, null, "system", triggerType);
                needRerun = taskId != null && consumeNeedRerun(projectId, taskId);
            } while (needRerun);
        } catch (Exception ex) {
            log.error("【流水标签】自动重算执行失败: projectId={}, triggerType={}, error={}",
                projectId, triggerType, ex.getMessage(), ex);
        } finally {
            runningProjects.remove(projectId);
            log.info("【流水标签】自动重算任务执行结束: projectId={}, triggerType={}", projectId, triggerType);
        }
    }

    private void markNeedRerun(Long projectId, CcdiBankTagTask runningTask) {
        if (runningTask == null) {
            pendingAutoRerunProjects.put(projectId, Boolean.TRUE);
            return;
        }
        runningTask.setNeedRerun(1);
        taskMapper.updateTask(runningTask);
    }

    private boolean consumeNeedRerun(Long projectId, Long taskId) {
        CcdiBankTagTask finishedTask = taskMapper.selectById(taskId);
        boolean taskNeedRerun = finishedTask != null && finishedTask.getNeedRerun() != null && finishedTask.getNeedRerun() == 1;
        boolean pendingNeedRerun = pendingAutoRerunProjects.remove(projectId) != null;
        if (!taskNeedRerun && !pendingNeedRerun) {
            return false;
        }

        if (taskNeedRerun) {
            CcdiBankTagTask update = new CcdiBankTagTask();
            update.setId(taskId);
            update.setNeedRerun(0);
            taskMapper.updateTask(update);
        }
        return true;
    }
}
