package com.infynova.udi.service.helper;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.infynova.udi.entity.Task;
import com.infynova.udi.entity.TaskRecord;
import com.infynova.udi.enums.UdiEnumUtils;
import com.infynova.udi.enums.task.TaskMatchStatusEnum;
import com.infynova.udi.enums.task.TaskRecordEnum;
import com.infynova.udi.enums.task.TaskRecordTypeEnum;
import com.infynova.udi.enums.task.TaskStatusEnum;
import com.infynova.udi.mapper.TaskMapper;
import com.infynova.udi.mapper.TaskRecordMapper;
import com.infynova.udi.vo.TaskVo;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @author liudong
 * 2024/2/29 18:31
 * @version 1.0
 */
@Component("taskHelper")
public class TaskHelper {

    @Resource
    private TaskMapper taskMapper;

    @Resource
    private TaskRecordMapper taskRecordMapper;

    @Resource
    RedisTemplate<String, Object> redisTemplate;

    /**
     *  公平锁
     */
    private final ReentrantLock fairLock = new ReentrantLock(true);

    public List<TaskVo> getTaskVoList(List<Long> taskIdList){
        List<Task> tasks = taskMapper.selectBatchIds(taskIdList);
        if(CollectionUtils.isEmpty(tasks)){
            return Collections.emptyList();
        }
        LambdaQueryWrapper<TaskRecord> queryWrapper = new LambdaQueryWrapper<TaskRecord>();
        queryWrapper.in(TaskRecord::getTaskId, taskIdList)
                .orderByAsc(TaskRecord::getCreateTime);
        List<TaskRecord> taskRecordList = taskRecordMapper.selectList(queryWrapper);
        Map<Long, List<TaskRecord>> map = taskRecordList.stream().collect(Collectors.groupingBy(TaskRecord::getTaskId));
        List<TaskVo> taskVoList = new ArrayList<>();
        for (Task task : tasks) {
            Long taskId = task.getId();
            task.setTaskStatusDesc(UdiEnumUtils.getDescByCode(TaskStatusEnum.class,task.getTaskStatus()));
            //task.setTaskFileSizeDesc();
            task.setTaskMatchStatusDesc(UdiEnumUtils.getDescByCode(TaskMatchStatusEnum.class,task.getTaskMatchStatus()));
            List<TaskRecord> taskRecords = map.getOrDefault(taskId, Collections.emptyList());
            this.setUploadTime(task,taskRecords);
            this.setMatchTime(task,taskRecords);
            TaskVo taskVo = TaskVo.builder().task(task).taskRecordList(taskRecords).build();
            taskVoList.add(taskVo);
        }
        return taskVoList;
    }

    public TaskVo getTaskVo(Long taskId){
        Task task = taskMapper.selectById(taskId);
        if(Objects.isNull(task)){
            return null;
        }
        task.setTaskStatusDesc(UdiEnumUtils.getDescByCode(TaskStatusEnum.class,task.getTaskStatus()));
        //task.setTaskFileSizeDesc();
        task.setTaskMatchStatusDesc(UdiEnumUtils.getDescByCode(TaskMatchStatusEnum.class,task.getTaskMatchStatus()));

        LambdaQueryWrapper<TaskRecord> queryWrapper = new LambdaQueryWrapper<TaskRecord>();
        queryWrapper.eq(TaskRecord::getTaskId, taskId)
                .orderByAsc(TaskRecord::getCreateTime);
        List<TaskRecord> taskRecords = taskRecordMapper.selectList(queryWrapper);
        this.setUploadTime(task,taskRecords);
        this.setMatchTime(task,taskRecords);
        return TaskVo.builder().task(task).taskRecordList(taskRecords).build();
    }


    public Map<Long,List<TaskRecord>> getTaskRecordMap(List<Long> taskIdList){
        LambdaQueryWrapper<TaskRecord> queryWrapper = new LambdaQueryWrapper<TaskRecord>();
        queryWrapper.in(TaskRecord::getTaskId, taskIdList)
                .orderByAsc(TaskRecord::getCreateTime);
        List<TaskRecord> taskRecords = taskRecordMapper.selectList(queryWrapper);
        return taskRecords.stream().collect(Collectors.groupingBy(TaskRecord::getTaskId));
    }


    /**
     * 获取任务编码
     */
    public String getTaskNo(){
        String taskNo;
        try {
            fairLock.lock();
            while (true){
                taskNo = this.build();
                Task task = taskMapper.queryTaskNo(taskNo);
                if(Objects.isNull(task)){
                    break;
                }
            }
            return taskNo;
        }catch (Exception e){
            throw e;
        }finally {
            // 释放锁
            fairLock.unlock();
        }
    }

    private String build(){
        String taskNoPrefix = "T";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        Date now = new Date();
        String dateStr = sdf.format(now);
        String redisKey = String.format("UDI:%s:%s", dateStr, taskNoPrefix);
        int num = redisTemplate.opsForValue().increment(redisKey).intValue();
        if (num == 1) {
            //设置过期时间
            redisTemplate.expire(redisKey, 2, TimeUnit.DAYS);
        }
        String numStr = num + "";
        while (numStr.length() < 4) {
            numStr = "0" + numStr;
        }
        dateStr = taskNoPrefix + dateStr + numStr;
        return dateStr;
    }

    @Transactional(rollbackFor = Exception.class)
    public Task saveTask(Integer taskFileSize,Integer taskFileCount,String taskFileAddress,Long taskFileId){
        // 保存task
        Task task = new Task();
        task.setTaskNo(this.getTaskNo());
        task.setTaskStatus(TaskStatusEnum.NEW_TASK.getCode());
        task.setTaskFileSize(String.valueOf(taskFileSize));
        task.setTaskFileCount(taskFileCount);
        task.setTaskUploadNum(0);
        task.setTaskFileAddress(taskFileAddress);
        task.setTaskFileId(taskFileId);
        task.setUploadTime(0);
        task.setTaskMatchStatus(TaskMatchStatusEnum.WAITING.getCode());
        task.setMatchTime(0);
        task.setOperatorId(null);// todo
        task.setOperator(null);// todo
        task.setCreateBy(null);// todo
        task.setCreator(null);// todo
        task.setCreateTime(LocalDateTime.now());
        task.setUpdateTime(LocalDateTime.now());
        task.setDelFlag(false);
        task.setVersion(1);
        taskMapper.insert(task);

        // 保存TaskRecord
        TaskRecord taskRecord = this.getDefaultTaskRecord(task.getId(), TaskRecordEnum.IMPORT_BUILD.getDesc());

        taskRecordMapper.insert(taskRecord);
        return task;
    }


    /**
     * 启动上传状态变更
     */
    @Transactional(rollbackFor = Exception.class)
    public void taskStatusUploadStart(Long taskId){
        Task task = taskMapper.selectById(taskId);
        task.setTaskStatus(TaskStatusEnum.UPLOADING.getCode());
        task.setUpdateTime(LocalDateTime.now());
        taskMapper.updateById(task);

        TaskRecord taskRecord = this.getUploadTaskRecord(taskId, TaskRecordEnum.UPLOAD_START.getDesc());
        taskRecordMapper.insert(taskRecord);
    }

    /**
     * 修改已上传数据
     */
    public void taskUploadNum(Long taskId,int uploadNum){
        Task task = taskMapper.selectById(taskId);
        int num = task.getTaskUploadNum() + uploadNum;
        task.setTaskUploadNum(num);
        task.setUpdateTime(LocalDateTime.now());
        taskMapper.updateById(task);
    }

    /**
     * 暂停上传状态变更
     */
    @Transactional(rollbackFor = Exception.class)
    public void taskStatusUploadPause(Long taskId){
        Task task = taskMapper.selectById(taskId);
        task.setTaskStatus(TaskStatusEnum.PAUSE.getCode());
        task.setUpdateTime(LocalDateTime.now());
        taskMapper.updateById(task);

        TaskRecord taskRecord = this.getUploadTaskRecord(taskId, TaskRecordEnum.UPLOAD_PAUSE.getDesc());
        taskRecordMapper.insert(taskRecord);
    }

    /**
     * 上传完成状态变更
     */
    @Transactional(rollbackFor = Exception.class)
    public void taskStatusUploadFinish(Long taskId){
        Task task = taskMapper.selectById(taskId);
        task.setTaskStatus(TaskStatusEnum.FINISH.getCode());
        task.setUpdateTime(LocalDateTime.now());
        taskMapper.updateById(task);

        TaskRecord taskRecord = this.getUploadTaskRecord(taskId, TaskRecordEnum.UPLOAD_FINISH.getDesc());
        taskRecordMapper.insert(taskRecord);
    }

    /**
     * 对码开始
     */
    @Transactional(rollbackFor = Exception.class)
    public void taskMatchStatusStart(Long taskId){
        Task task = taskMapper.selectById(taskId);
        if(!Objects.equals(task.getTaskMatchStatus(),TaskMatchStatusEnum.WAITING.getCode())){
            return;
        }
        task.setTaskMatchStatus(TaskMatchStatusEnum.MATCHING.getCode());
        task.setUpdateTime(LocalDateTime.now());
        taskMapper.updateById(task);

        TaskRecord taskRecord = this.getMatchTaskRecord(taskId, TaskRecordEnum.MATCH_START.getDesc());
        taskRecordMapper.insert(taskRecord);
    }

    /**
     * 对码完成
     */
    @Transactional(rollbackFor = Exception.class)
    public void taskMatchStatusFinish(Long taskId){
        Task task = taskMapper.selectById(taskId);
        task.setTaskMatchStatus(TaskMatchStatusEnum.FINISH.getCode());
        task.setUpdateTime(LocalDateTime.now());
        taskMapper.updateById(task);

        TaskRecord taskRecord = this.getMatchTaskRecord(taskId, TaskRecordEnum.MATCH_FINISH.getDesc());
        taskRecordMapper.insert(taskRecord);
    }

    /**
     * 删除
     */
    @Transactional(rollbackFor = Exception.class)
    public void taskDelete(Long taskId){
        Task task = taskMapper.selectById(taskId);
        task.setTaskStatus(TaskStatusEnum.DELETE.getCode());
        task.setUpdateTime(LocalDateTime.now());
        task.setDelFlag(true);
        taskMapper.updateById(task);

        TaskRecord taskRecord = this.getDefaultTaskRecord(taskId, TaskRecordEnum.DELETE.getDesc());
        taskRecordMapper.insert(taskRecord);
    }


    public TaskRecord getDefaultTaskRecord(Long taskId,String record){
        TaskRecord taskRecord = new TaskRecord();
        taskRecord.setTaskId(taskId);
        taskRecord.setRecord(record);
        taskRecord.setRecordType(TaskRecordTypeEnum.DEFAULT.getCode());
        taskRecord.setCreateBy(null);// todo
        taskRecord.setCreator(null);// todo
        taskRecord.setCreateTime(LocalDateTime.now());
        return taskRecord;
    }

    public TaskRecord getUploadTaskRecord(Long taskId,String record){
        TaskRecord taskRecord = new TaskRecord();
        taskRecord.setTaskId(taskId);
        taskRecord.setRecord(record);
        taskRecord.setRecordType(TaskRecordTypeEnum.UPLOAD.getCode());
        taskRecord.setCreateBy(null);// todo
        taskRecord.setCreator(null);// todo
        taskRecord.setCreateTime(LocalDateTime.now());
        return taskRecord;
    }

    public TaskRecord getMatchTaskRecord(Long taskId,String record){
        TaskRecord taskRecord = new TaskRecord();
        taskRecord.setTaskId(taskId);
        taskRecord.setRecord(record);
        taskRecord.setRecordType(TaskRecordTypeEnum.MATCH.getCode());
        taskRecord.setCreateBy(null);// todo
        taskRecord.setCreator(null);// todo
        taskRecord.setCreateTime(LocalDateTime.now());
        return taskRecord;
    }

    /**
     * 设置上传时间
     */
    public void setUploadTime(Task task,List<TaskRecord> taskRecords){
        Integer uploadTime = this.getUploadTime(taskRecords);
        task.setUploadTime(uploadTime);
        task.setUploadTimeDesc(this.formatDuration(uploadTime));
    }

    /**
     * 设置对码时间
     */
    public void setMatchTime(Task task,List<TaskRecord> taskRecords){
        Integer matchTime = this.getMatchTime(taskRecords);
        task.setMatchTime(matchTime);
        task.setMatchTimeDesc(this.formatDuration(matchTime));
    }

    /**
     * 计算对码时间
     */
    public Integer getMatchTime(List<TaskRecord> recordList){
        long matchTimeInSeconds = 0;
        LocalDateTime startTime = null;

        for (int i = 0; i < recordList.size(); i++) {
            TaskRecord currentRecord = recordList.get(i);

            if (currentRecord.getRecordType() == 2) { // 对码记录
                if( Objects.equals(currentRecord.getRecord(),TaskRecordEnum.MATCH_START.getDesc()) ){
                    startTime = currentRecord.getCreateTime();
                } else if (Objects.equals(currentRecord.getRecord(),TaskRecordEnum.MATCH_PAUSE.getDesc())) {
                    // 计算本次对码时间并累加到总对码时间
                    if (startTime != null) {
                        LocalDateTime pauseTime = currentRecord.getCreateTime();
                        matchTimeInSeconds += Duration.between(startTime, pauseTime).getSeconds();
                        startTime = null;
                    }
                } else if (Objects.equals(currentRecord.getRecord(),TaskRecordEnum.MATCH_FINISH.getDesc())) {
                    // 如果启动但没有暂停,直接计算上传时间
                    if (startTime != null) {
                        LocalDateTime endTime = currentRecord.getCreateTime();
                        matchTimeInSeconds += Duration.between(startTime, endTime).getSeconds();
                        startTime = null;
                    }
                }
            }
        }
        return Math.toIntExact(matchTimeInSeconds);
    }

    /**
     * 计算上传时间
     */
    public Integer getUploadTime(List<TaskRecord> recordList){
        long matchTimeInSeconds = 0;
        LocalDateTime startTime = null;

        for (int i = 0; i < recordList.size(); i++) {
            TaskRecord currentRecord = recordList.get(i);

            if (currentRecord.getRecordType() == 1) { // 上传记录
                if( Objects.equals(currentRecord.getRecord(),TaskRecordEnum.UPLOAD_START.getDesc()) ){
                    startTime = currentRecord.getCreateTime();
                } else if (Objects.equals(currentRecord.getRecord(),TaskRecordEnum.UPLOAD_PAUSE.getDesc())) {
                    // 计算本次上传时间并累加到总上传时间
                    if (startTime != null) {
                        LocalDateTime pauseTime = currentRecord.getCreateTime();
                        matchTimeInSeconds += Duration.between(startTime, pauseTime).getSeconds();
                        startTime = null;
                    }
                } else if (Objects.equals(currentRecord.getRecord(),TaskRecordEnum.UPLOAD_FINISH.getDesc())) {
                    // 如果启动但没有暂停,直接计算上传时间
                    if (startTime != null) {
                        LocalDateTime endTime = currentRecord.getCreateTime();
                        matchTimeInSeconds += Duration.between(startTime, endTime).getSeconds();
                        startTime = null;
                    }
                }
            }
        }
        return Math.toIntExact(matchTimeInSeconds);
    }

    /**
     * 时间转换(秒转特定格式)
     */
    public String formatDuration(int durationInSeconds) {
        int days = durationInSeconds / (24 * 3600);
        int hours = (durationInSeconds % (24 * 3600)) / 3600;
        int minutes = ((durationInSeconds % (24 * 3600)) % 3600) / 60;
        int seconds = ((durationInSeconds % (24 * 3600)) % 3600) % 60;

        StringBuilder formattedDuration = new StringBuilder();
        if (days > 0) {
            formattedDuration.append(days).append("天");
        }
        if (hours > 0) {
            formattedDuration.append(hours).append("小时");
        }
        if (minutes > 0) {
            formattedDuration.append(minutes).append("分钟");
        }
        if (seconds > 0 || (days == 0 && hours == 0 && minutes == 0)) {
            formattedDuration.append(seconds).append("秒");
        }
        return formattedDuration.toString().trim();
    }
}