package com.salpa.web.job;

import com.alibaba.fastjson2.JSON;
import com.salpa.common.core.domain.AjaxResult;
import com.salpa.common.core.domain.entity.SysUser;
import com.salpa.subject.domain.MonitorOperationLog;
import com.salpa.subject.domain.MonitorProjectSyncTask;
import com.salpa.subject.domain.data.ProjectSyncTaskStatus;
import com.salpa.subject.mapper.OperationLogMapper;
import com.salpa.subject.mapper.ProjectSyncTaskMapper;
import com.salpa.subject.service.DocumentSyncService;
import com.salpa.system.mapper.SysUserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Objects;

@Component
@RequiredArgsConstructor
@Slf4j
public class ProjectSyncTaskScheduler {

    @Autowired
    private DocumentSyncService documentSyncService;

    @Autowired
    private OperationLogMapper operationLogMapper;

    @Autowired
    private ProjectSyncTaskMapper projectSyncTaskMapper;

    @Autowired
    private SysUserMapper sysUserMapper;

    private final ThreadPoolTaskExecutor projectSyncTaskThreadPoolTaskExecutor;


    /*每隔 5s 执行一次*/
    @Scheduled(fixedRate = 5000L)
    public void startSyncTask() {
        final int size = 10;
        List<MonitorProjectSyncTask> tasks = projectSyncTaskMapper.listNewTasks(size);
        tasks.forEach(task -> {
            projectSyncTaskThreadPoolTaskExecutor.execute(() -> {
                Integer taskId = task.getId();
                Integer projectId = task.getProjectId();
                Long userId = task.getUserId();
                sync(taskId, projectId, userId);
            });
        });
    }

    private void sync(Integer taskId, Integer projectId, Long userId) {
        try {
            updateSyncTaskStatus(taskId, ProjectSyncTaskStatus.RUNNING.toString(), "running");
            Integer docId = documentSyncService.syncByProjectId(projectId, taskId);
            String status = documentSyncService.selectStatusById(taskId);
            if (!"CANCELED".equals(status)) {
                updateSyncTaskStatus(taskId, ProjectSyncTaskStatus.FINISHED.toString(), "ok");
                saveOperationLog(projectId, userId, null);
            } else {
                if (docId != null) {
                    Long version = documentSyncService.getDocumentVersionById(docId);
                    documentSyncService.deleteDocumentById(docId);
                    documentSyncService.updateDocument(projectId,version-1);
                }
            }
        } catch (Exception e) {
            String result = Objects.requireNonNullElse(e.getMessage(), "unknown");
            updateSyncTaskStatus(taskId, ProjectSyncTaskStatus.FAILED.toString(), result);
            saveOperationLog(projectId, userId, e);
            throw e;
        }
    }

    private void updateSyncTaskStatus(Integer taskId, String status, String result) {
        projectSyncTaskMapper.updateStatusAndResultById(taskId, status, result);
    }

    private void saveOperationLog(Integer projectId, Long userId, Exception ex) {
        String operatorNickName;
        String operatorUsername;
        String operationName;
        if (Objects.equals(-1, userId)) {
            operatorNickName = "system";
            operatorUsername = "system";
            operationName = "定时同步";
        } else {
            SysUser user = sysUserMapper.selectUserById(userId);
            operatorNickName = user.getNickName();
            operatorUsername = user.getUserName();
            operationName = "手动同步";
        }
        AjaxResult response;
        if (ex == null) {
            response = AjaxResult.success();
        } else {
            response = AjaxResult.error(-1,ex.getMessage());
        }
        MonitorOperationLog operationLog = MonitorOperationLog.builder()
                .operatorNickname(operatorNickName)
                .operatorUsername(operatorUsername)
                .operatorUserId(userId)
                .operationName(operationName)
                .operationCode("autoSyncDocumentation")
                .operationModule("project")
                .operationResponse(JSON.toJSONString(response))
                .success(ex == null)
                .involvedProjectId(projectId)
                .build();
        operationLogMapper.addOperationLog(operationLog);
    }
}
