package cn.edu.whu.boot.xml.service.impl;

import cn.edu.whu.boot.allocation.services.TaskCellDataService;
import cn.edu.whu.boot.allocation.services.TaskCellService;
import cn.edu.whu.boot.allocation.services.TaskUserCellService;
import cn.edu.whu.boot.allocation.services.TaskUserRoleService;
import cn.edu.whu.boot.collection.service.ITaskSelectIdService;
import cn.edu.whu.boot.common.domain.RedisExpireTime;
import cn.edu.whu.boot.common.service.CacheService;
import cn.edu.whu.boot.common.utils.collection.PostgreSqlUtil;
import cn.edu.whu.boot.user.entities.User;
import cn.edu.whu.boot.xml.domain.RedisKeyType;
import cn.edu.whu.boot.xml.domain.Task;
import cn.edu.whu.boot.xml.domain.TaskExample;
import cn.edu.whu.boot.xml.domain.XmlEnity;
import cn.edu.whu.boot.xml.mapper.TaskMapper;
import cn.edu.whu.boot.xml.mapper.XmlEnityMapper;
import cn.edu.whu.boot.xml.service.ITaskService;
import cn.edu.whu.boot.xml.xmlreader.bean.Config;
import com.alibaba.fastjson.JSON;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * 注意有些地方涉及到 Redis 缓存!!!
 * @author Zhou Chao
 */
@Service
public class TaskServiceImpl implements ITaskService{

    private final TaskMapper taskMapper;
    private final XmlEnityMapper xmlEnityMapper;
    private final TaskCellDataService taskCellDataService;
    private final TaskUserCellService taskUserCellService;
    private final TaskUserRoleService taskUserRoleService;
    private final TaskCellService taskCellService;
    private final ITaskSelectIdService taskSelectIdService;
    private final CacheService cacheService;

    @Autowired
    public TaskServiceImpl(TaskMapper taskMapper, XmlEnityMapper xmlEnityMapper,
                           TaskCellDataService taskCellDataService, TaskUserCellService taskUserCellService,
                           TaskUserRoleService taskUserRoleService, TaskCellService taskCellService,
                           ITaskSelectIdService taskSelectIdService, CacheService cacheService){
        this.taskMapper = taskMapper;
        this.xmlEnityMapper = xmlEnityMapper;
        this.taskCellDataService = taskCellDataService;
        this.taskUserCellService = taskUserCellService;
        this.taskUserRoleService = taskUserRoleService;
        this.taskCellService = taskCellService;
        this.taskSelectIdService = taskSelectIdService;
        this.cacheService = cacheService;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void updateTaskAndXmlEnity(Task task, XmlEnity xmlEnity){
        xmlEnityMapper.updateByPrimaryKey(xmlEnity);
        taskMapper.updateByPrimaryKey(task);
        deleteAllTaskKey();
        deleteAllHasCreatedTaskKey();
        deleteSomeTaskKey(task.getId());
        deleteSomeXmlFileDataKey(xmlEnity.getId());
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void updateTask(Task task){
        taskMapper.updateByPrimaryKey(task);
        deleteAllTaskKey();
        deleteAllHasCreatedTaskKey();
        deleteSomeTaskKey(task.getId());
        deleteSomeXmlFileDataKey(task.getXmlId());
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void insertTaskAndXmlEnity(Task task, XmlEnity xmlEnity){
        xmlEnityMapper.insert(xmlEnity);
        task.setXmlId(xmlEnity.getId());
        taskMapper.insert(task);
        deleteAllTaskKey();
        deleteAllHasCreatedTaskKey();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void deleteTaskAndXmlEnity(Task task){
        xmlEnityMapper.deleteByPrimaryKey(task.getXmlId());
        int taskId = task.getId();
        taskMapper.deleteByPrimaryKey(taskId);
        taskCellDataService.deleteByTaskId(taskId);
        taskUserCellService.deleteByTaskId(taskId);
        taskUserRoleService.deleteByTaskId(taskId);
        taskCellService.deleteByTaskId(taskId);
        taskSelectIdService.delete(taskId);
        deleteAllTaskKey();
        deleteAllHasCreatedTaskKey();
        deleteSomeTaskKey(task.getId());
        deleteSomeXmlFileDataKey(task.getXmlId());
    }

    @Override
    public Integer getXmlId(Integer id){
        return taskMapper.selectByPrimaryKey(id).getXmlId();
    }

    @Override
    public Task getTaskById(Integer id){
        String someTaskRedisKey = RedisKeyType.TASK + String.valueOf(id);
        Object redisObject = cacheService.get(someTaskRedisKey);
        if(redisObject != null){
            return (Task) redisObject;
        }
        Task task = taskMapper.selectByPrimaryKey(id);
        if(task != null){
            cacheService.set(someTaskRedisKey, task, RedisExpireTime.ONE_DAY.getTime());
        }
        return task;
    }

    @Override
    public List<Task> getTaskHasCreateTable(){
        String hasCreateTaskRedisKey = RedisKeyType.TASK + "hasCreate";
        Object redisObject = cacheService.get(hasCreateTaskRedisKey);
        if(redisObject != null){
            return (List<Task>) redisObject;
        }
        TaskExample taskExample = new TaskExample();
        taskExample.createCriteria().andHasCreateTablesEqualTo((short)1);
        List<Task> taskList = taskMapper.selectByExample(taskExample);
        if(!CollectionUtils.isEmpty(taskList)){
            cacheService.set(hasCreateTaskRedisKey, taskList, RedisExpireTime.ONE_DAY.getTime());
        }
       return taskList;
    }

    @Override
    public List<Task> getAllTask(){
        String allTaskRedisKey = RedisKeyType.TASK + "all";
        Object redisObject = cacheService.get(allTaskRedisKey);
        if(redisObject != null){
            return (List<Task>) redisObject;
        }
        List<Task> taskList = taskMapper.selectByExample(new TaskExample());
        if(!CollectionUtils.isEmpty(taskList)){
            cacheService.set(allTaskRedisKey, taskList, RedisExpireTime.ONE_DAY.getTime());
        }
        return taskList;
    }

    @Override
    public boolean hasDatabaseName(String databaseName){
        TaskExample taskExample = new TaskExample();
        taskExample.createCriteria().andDatabaseNameEqualTo(databaseName);
        List<Task> taskList = taskMapper.selectByExample(taskExample);
        return taskList.size() != 0;
    }

    /**
     * 清除 Redis中的 redis_key_prefix_task:all 键
     */
    private void deleteAllTaskKey(){
        String allTaskRedisKey = RedisKeyType.TASK +  "all";
        cacheService.delete(allTaskRedisKey);
    }

    /**
     * 清除 Redis中的 redis_key_prefix_task:all 键
     */
    private void deleteAllHasCreatedTaskKey(){
        String allHasCreatedTaskRedisKey = RedisKeyType.TASK +  "hasCreate";
        cacheService.delete(allHasCreatedTaskRedisKey);
    }

    /**
     * 清除 Redis 中的 redis_key_prefix_task:"id" 键
     */
    private void deleteSomeTaskKey(int taskId){
        String someTaskRedisKey = RedisKeyType.TASK + String.valueOf(taskId);
        cacheService.delete(someTaskRedisKey);
    }

    /**
     * 清除 Redis 中的 redis_key_prefix_xml_file_data:"id" 键
     */
    private void deleteSomeXmlFileDataKey(int xmlEnityId){
        String someTaskRedisKey = RedisKeyType.XML_FILE_DATA + String.valueOf(xmlEnityId);
        cacheService.delete(someTaskRedisKey);
    }

    @Override
    public List<User> getWorkersByTask(int taskId) {
        Task task = getTaskById(taskId);
        if (task == null) {
            return Collections.emptyList();
        }

        if (task.getIsAllocationOptional() == 0) {
            // 该任务需要分配，工作人员的信息从 task_user_role 表中获取
            return taskMapper.selectUsersByTaskId(taskId);
        } else {
            // 该任务无需分配，工作人员为上传数据的人员 + 上传轨迹的人员 + 上传草图的人员
            String databaseName = task.getDatabaseName();
            Set<String> rootTables = JSON.toJavaObject(JSON.parseObject(task.getConfig()), Config.class).getRootTables().keySet();
            Map<Integer, User> idToUser = new HashMap<>();
            // 1. 从根表中获取所有上传了数据的用户
            for (String tableName : rootTables) {
                for (User user : taskMapper.selectUsersByTableNameAndIdFieldName(PostgreSqlUtil.buildTableNameString(databaseName, tableName, false, false), "collector")) {
                    idToUser.put(user.getId(), user);
                }
            }
            // 2. 从轨迹表中获取上传了轨迹的用户
            for (User user : taskMapper.selectUsersByTableNameAndIdFieldName(PostgreSqlUtil.buildTableNameString(databaseName, "route_line", false, false), "collector_id")) {
                idToUser.put(user.getId(), user);
            }
            // 3. 从草图表中获取上传了草图的用户
            for (User user : taskMapper.selectUsersByTableNameAndIdFieldName(PostgreSqlUtil.buildTableNameString(databaseName, "draft", false, false), "collector_id")) {
                idToUser.put(user.getId(), user);
            }
            return new ArrayList<>(idToUser.values());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void setReviewerMode(int taskId, int isReviewerRestricted) {
        Task task = getTaskById(taskId);
        if (task == null) {
            return;
        }

        task.setIsReviewerRestricted(isReviewerRestricted);
        updateTask(task);
    }
}
