package com.zcloud.safetyDutyList.command.tasklist;

import com.zcloud.safetyDutyList.domain.enums.ExceptionTypeEnum;
import com.zcloud.safetyDutyList.domain.gateway.tasklist.FeedbackExceptionGateway;
import com.zcloud.safetyDutyList.domain.gateway.tasklist.FeedbackGateway;
import com.zcloud.safetyDutyList.domain.gateway.tasklist.TaskDetailGateway;
import com.zcloud.safetyDutyList.domain.gateway.tasklist.TaskListGateway;
import com.zcloud.safetyDutyList.domain.model.tasklist.FeedbackE;
import com.zcloud.safetyDutyList.domain.model.tasklist.FeedbackExceptionE;
import com.zcloud.safetyDutyList.domain.model.tasklist.TaskDetailE;
import com.zcloud.safetyDutyList.domain.model.tasklist.TaskListE;
import com.zcloud.safetyDutyList.domain.util.FeedbackCycleUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

/**
 * 反馈异常检测执行器
 * <p>
 * 处理定时任务2的业务逻辑：检测已下发清单下所有任务的反馈异常情况。
 * <p>
 * 处理流程：
 *   1. 查询所有已下发且周期时间完整的清单（issue_status=1, period_start_time/end_time不为空）
 *   2. 遍历每个清单，获取其下所有任务
 *   3. 对每个任务，以清单的周期起始时间判断是否仍处于首个反馈周期内
 *   4. 若仍处于首周期，跳过（首周期尚未结束，不应判定异常）
 *   5. 若已过首周期，计算上一阶段周期标识，检查是否有反馈记录
 *   6. 若上一阶段无反馈且无异常记录，则创建反馈异常记录
 * <p>
 * 首周期判断逻辑（以清单periodStartTime为准）：
 *   将periodStartTime和当前时间分别生成周期标识，若相同则仍在首周期内。
 *   例如：清单起始时间2026年7月，当前2026年7月12日：
 *     每月→首周期=2026-07=当前周期→跳过
 *     每季度→首周期=2026-Q3=当前周期→跳过
 *     每半年→首周期=2026-H2=当前周期→跳过
 *     每年→首周期=2026-Y=当前周期→跳过
 * <p>
 * 事务控制：整个检测过程在同一事务中执行，确保异常记录写入的原子性
 */
@Slf4j
@Component
@AllArgsConstructor
public class FeedbackExceptionCheckExe {

    private final TaskListGateway taskListGateway;

    private final TaskDetailGateway taskDetailGateway;

    private final FeedbackGateway feedbackGateway;

    private final FeedbackExceptionGateway feedbackExceptionGateway;

    /**
     * 执行反馈异常检测
     * <p>
     * 遍历所有已下发且周期时间完整的清单，对每个清单下的任务进行反馈异常检测。
     */
    @Transactional(rollbackFor = Exception.class)
    public void execute() {

        // 查询所有已下发且周期时间完整的清单
        List<TaskListE> issuedList = taskListGateway.listIssuedWithPeriod();
        if (issuedList == null || issuedList.isEmpty()) {
            log.info("【定时任务-安全责任清单-检测反馈异常】没有已下发且周期时间完整的清单需要检测");
            return;
        }

        log.info("【定时任务-安全责任清单-检测反馈异常】查询到{}条已下发清单需要检测反馈异常", issuedList.size());

        // 遍历每个清单
        for (TaskListE taskList : issuedList) {

            // 获取清单下的任务
            List<TaskDetailE> taskDetails = taskDetailGateway.listByTaskListId(taskList.getTaskListId());
            if (taskDetails == null || taskDetails.isEmpty()) {
                continue;
            }

            // 遍历每个任务
            for (TaskDetailE taskDetail : taskDetails) {
                checkTaskFeedback(taskList, taskDetail);
            }
        }
    }

    /**
     * 检测单个任务的反馈异常
     *
     * @param taskList   所属清单（用于获取周期起始时间）
     * @param taskDetail 任务详情
     */
    private void checkTaskFeedback(TaskListE taskList, TaskDetailE taskDetail) {
        Integer feedbackCycleType = taskDetail.getFeedbackCycleType();
        if (feedbackCycleType == null) {
            log.warn("【定时任务-安全责任清单-检测反馈异常】任务[{}]反馈周期类型为空，跳过检测", taskDetail.getTaskDetailId());
            return;
        }

        // 当前时间处于首周期，跳过
        if (FeedbackCycleUtil.isFirstCycle(feedbackCycleType, taskList.getPeriodStartTime())) {
            log.info("【定时任务-安全责任清单-检测反馈异常】清单[{}]任务[{}]周期类型为{}，仍处于首周期内，跳过检测",
                    taskList.getTaskListId(), taskDetail.getTaskDetailId(), feedbackCycleType);
            return;
        }

        // 上一阶段周期标识
        String previousPeriodFlag = FeedbackCycleUtil.generatePreviousPeriodFlag(feedbackCycleType);
        if (previousPeriodFlag == null) {
            log.warn("【定时任务-安全责任清单-检测反馈异常】任务[{}]无法生成上一阶段周期标识，feedbackCycleType={}",
                    taskDetail.getTaskDetailId(), feedbackCycleType);
            return;
        }

        // 异常记录是否存在
        boolean alreadyHasException = checkExistingException(taskDetail.getTaskDetailId(), previousPeriodFlag);
        if (alreadyHasException) {
            log.info("【定时任务-安全责任清单-检测反馈异常】任务[{}]在周期[{}]已存在异常记录，跳过",
                    taskDetail.getTaskDetailId(), previousPeriodFlag);
            return;
        }

        // 上一阶段周期标识对应的反馈记录是否存在
        boolean hasFeedback = checkPreviousPeriodFeedback(taskDetail.getTaskDetailId(), previousPeriodFlag);

        if (!hasFeedback) {
            createFeedbackException(taskDetail, previousPeriodFlag);
            log.info("【定时任务-安全责任清单-检测反馈异常】任务[{}]在周期[{}]未反馈，已添加异常记录",
                    taskDetail.getTaskDetailId(), previousPeriodFlag);
        } else {
            log.info("【定时任务-安全责任清单-检测反馈异常】任务[{}]在周期[{}]已正常反馈",
                    taskDetail.getTaskDetailId(), previousPeriodFlag);
        }
    }

    /**
     * 检查上一阶段是否已有异常记录
     *
     * @param taskDetailId       任务详情ID
     * @param previousPeriodFlag 上一阶段的周期标识
     * @return true-已存在异常记录，false-不存在异常记录
     */
    private boolean checkExistingException(String taskDetailId, String previousPeriodFlag) {
        List<FeedbackExceptionE> exceptionList = feedbackExceptionGateway.listByTaskDetailId(taskDetailId);
        if (exceptionList == null || exceptionList.isEmpty()) {
            return false;
        }
        return exceptionList.stream()
                .anyMatch(e -> previousPeriodFlag.equals(e.getExceptionPeriodFlag()));
    }

    /**
     * 检查上一阶段是否有反馈记录
     *
     * @param taskDetailId       任务详情ID
     * @param previousPeriodFlag 上一阶段的周期标识
     * @return true-存在反馈记录，false-不存在反馈记录
     */
    private boolean checkPreviousPeriodFeedback(String taskDetailId, String previousPeriodFlag) {
        List<FeedbackE> feedbackList = feedbackGateway.listByTaskDetailId(taskDetailId);
        if (feedbackList == null || feedbackList.isEmpty()) {
            return false;
        }
        return feedbackList.stream()
                .anyMatch(f -> previousPeriodFlag.equals(f.getFeedbackPeriodFlag()));
    }

    /**
     * 创建反馈异常记录
     *
     * @param taskDetail         未按时反馈的任务详情
     * @param previousPeriodFlag 上一阶段的周期标识
     */
    private void createFeedbackException(TaskDetailE taskDetail, String previousPeriodFlag) {
        FeedbackExceptionE exceptionE = new FeedbackExceptionE();
        exceptionE.init();

        exceptionE.setTaskDetailId(taskDetail.getTaskDetailId());
        exceptionE.setTaskListId(taskDetail.getTaskListId());

        exceptionE.setExceptionPeriodFlag(previousPeriodFlag);

        LocalDateTime[] periodTime = FeedbackCycleUtil.getPeriodTime(previousPeriodFlag);
        if (periodTime != null && periodTime.length == 2) {
            exceptionE.setExceptionPeriodStartTime(periodTime[0]);
            exceptionE.setExceptionPeriodEndTime(periodTime[1]);
        }

        exceptionE.setExceptionType(ExceptionTypeEnum.NOT_FEEDBACK.getCode());
        exceptionE.setExceptionTime(LocalDateTime.now());

        feedbackExceptionGateway.add(exceptionE);
    }
}
