package com.njcn.gather.tool.adddata.component;

import com.njcn.gather.tool.adddata.pojo.bo.AddDataTaskCommand;
import com.njcn.gather.tool.adddata.pojo.enums.AddDataTaskStatusEnum;
import com.njcn.gather.tool.adddata.pojo.vo.AddDataTaskStatusVO;
import com.njcn.gather.tool.adddata.util.AddDataDateTimeUtil;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 任务状态持有器。
 */
@Component
public class AddDataTaskStatusHolder {

    /** 任务记录缓存。 */
    private final ConcurrentMap<String, TaskRecord> taskRecordMap = new ConcurrentHashMap<String, TaskRecord>();

    /** 时间槽计算组件。 */
    private final AddDataTimeSlotCalculator addDataTimeSlotCalculator;

    public AddDataTaskStatusHolder(AddDataTimeSlotCalculator addDataTimeSlotCalculator) {
        this.addDataTimeSlotCalculator = addDataTimeSlotCalculator;
    }

    /**
     * 创建等待中的任务。
     *
     * @param command 任务命令
     * @return 任务状态快照
     */
    public AddDataTaskStatusVO createWaitingTask(AddDataTaskCommand command) {
        String taskId = UUID.randomUUID().toString().replace("-", "");
        TaskRecord record = new TaskRecord(taskId, command, buildHourlyTimeResults(command));
        taskRecordMap.put(taskId, record);
        return copySnapshot(record);
    }

    /**
     * 标记为运行中。
     *
     * @param taskId 任务编号
     */
    public void markRunning(String taskId) {
        TaskRecord record = requireRecord(taskId);
        synchronized (record) {
            record.status = AddDataTaskStatusEnum.RUNNING;
            record.startTime = LocalDateTime.now();
            record.currentBatchInfo = "任务已启动";
        }
    }

    /**
     * 更新当前批次信息。
     *
     * @param taskId 任务编号
     * @param tableName 当前表名
     * @param batchNo 批次号
     * @param batchSize 批次大小
     */
    public void updateCurrentBatch(String taskId, String tableName, int batchNo, int batchSize) {
        TaskRecord record = requireRecord(taskId);
        synchronized (record) {
            record.currentTableName = tableName;
            record.currentBatchInfo = "第 " + batchNo + " 批，" + batchSize + " 行";
        }
    }

    /**
     * 累加执行统计。
     *
     * @param taskId 任务编号
     * @param insertedCount 新增成功数
     * @param skippedCount 跳过数
     * @param failedCount 失败数
     */
    public void addProgress(String taskId, long insertedCount, long skippedCount, long failedCount) {
        TaskRecord record = requireRecord(taskId);
        synchronized (record) {
            record.insertedCount += insertedCount;
            record.skippedCount += skippedCount;
            record.failedCount += failedCount;
        }
    }

    /**
     * 标记任务成功。
     *
     * @param taskId 任务编号
     */
    public void markSuccess(String taskId) {
        TaskRecord record = requireRecord(taskId);
        synchronized (record) {
            record.status = AddDataTaskStatusEnum.SUCCESS;
            record.endTime = LocalDateTime.now();
            record.currentBatchInfo = "执行完成";
        }
    }

    /**
     * 标记任务失败。
     *
     * @param taskId 任务编号
     * @param failureReason 失败原因
     */
    public void markFailed(String taskId, String failureReason) {
        TaskRecord record = requireRecord(taskId);
        synchronized (record) {
            record.status = AddDataTaskStatusEnum.FAILED;
            record.endTime = LocalDateTime.now();
            record.failureReason = failureReason;
            record.currentBatchInfo = "执行失败";
        }
    }

    /**
     * 查询任务状态。
     *
     * @param taskId 任务编号
     * @return 状态快照
     */
    public AddDataTaskStatusVO getStatus(String taskId) {
        return copySnapshot(requireRecord(taskId));
    }

    /**
     * 获取任务命令。
     *
     * @param taskId 任务编号
     * @return 任务命令
     */
    public AddDataTaskCommand getCommand(String taskId) {
        return requireRecord(taskId).command;
    }

    /**
     * 获取全部任务编号。
     *
     * @return 任务编号列表
     */
    public List<String> getTaskIds() {
        return new ArrayList<String>(taskRecordMap.keySet());
    }

    /**
     * 复制状态快照。
     *
     * @param record 任务记录
     * @return 状态快照
     */
    private AddDataTaskStatusVO copySnapshot(TaskRecord record) {
        synchronized (record) {
            AddDataTaskStatusVO status = new AddDataTaskStatusVO();
            status.setTaskId(record.taskId);
            status.setStatus(record.status.name());
            status.setCurrentTableName(record.currentTableName);
            status.setCurrentBatchInfo(record.currentBatchInfo);
            status.setInsertedCount(record.insertedCount);
            status.setSkippedCount(record.skippedCount);
            status.setFailedCount(record.failedCount);
            status.setFailureReason(record.failureReason);
            status.setHourlyTimeResults(new ArrayList<String>(record.hourlyTimeResults));
            status.setStartTime(AddDataDateTimeUtil.format(record.startTime));
            status.setEndTime(AddDataDateTimeUtil.format(record.endTime));
            return status;
        }
    }

    /**
     * 根据任务时间范围生成前端展示的每小时时刻。
     *
     * @param command 任务命令
     * @return 每小时时刻文本
     */
    private List<String> buildHourlyTimeResults(AddDataTaskCommand command) {
        List<LocalDateTime> timeSlots = addDataTimeSlotCalculator.buildHourlyTimeSlots(command.getStartTime(), command.getEndTime());
        List<String> result = new ArrayList<String>(timeSlots.size());
        for (LocalDateTime timeSlot : timeSlots) {
            result.add(AddDataDateTimeUtil.format(timeSlot));
        }
        return result;
    }

    /**
     * 获取任务记录。
     *
     * @param taskId 任务编号
     * @return 任务记录
     */
    private TaskRecord requireRecord(String taskId) {
        TaskRecord record = taskRecordMap.get(taskId);
        if (record == null) {
            throw new IllegalArgumentException("未找到补数任务：" + taskId);
        }
        return record;
    }

    /**
     * 内存态任务记录。
     */
    private static final class TaskRecord {

        /** 任务编号。 */
        private final String taskId;
        /** 任务命令。 */
        private final AddDataTaskCommand command;
        /** 每小时返回给前端展示的业务时刻。 */
        private final List<String> hourlyTimeResults;
        /** 当前状态。 */
        private AddDataTaskStatusEnum status = AddDataTaskStatusEnum.WAITING;
        /** 当前表名。 */
        private String currentTableName;
        /** 当前批次。 */
        private String currentBatchInfo = "等待执行";
        /** 成功数。 */
        private long insertedCount;
        /** 跳过数。 */
        private long skippedCount;
        /** 失败数。 */
        private long failedCount;
        /** 失败原因。 */
        private String failureReason;
        /** 开始时间。 */
        private LocalDateTime startTime;
        /** 结束时间。 */
        private LocalDateTime endTime;

        private TaskRecord(String taskId, AddDataTaskCommand command, List<String> hourlyTimeResults) {
            this.taskId = taskId;
            this.command = command;
            this.hourlyTimeResults = hourlyTimeResults;
        }
    }
}
