package cn.edu.whu.boot.allocation.services.impl;

import cn.edu.whu.boot.allocation.entities.TaskCell;
import cn.edu.whu.boot.allocation.entities.TaskCellData;
import cn.edu.whu.boot.allocation.entities.TaskUserCell;
import cn.edu.whu.boot.allocation.entities.TaskUserRole;
import cn.edu.whu.boot.allocation.exceptions.InvalidAssignmentStatus;
import cn.edu.whu.boot.allocation.mapper.SimpleTaskMapper;
import cn.edu.whu.boot.allocation.services.*;
import cn.edu.whu.boot.collection.service.IBaseService;
import cn.edu.whu.boot.common.domain.CommonConstant;
import cn.edu.whu.boot.common.service.CacheService;
import cn.edu.whu.boot.common.utils.collection.PostgreSqlUtil;
import cn.edu.whu.boot.common.utils.collection.TableOperationUtil;
import cn.edu.whu.boot.xml.domain.RedisKeyType;
import cn.edu.whu.boot.xml.domain.Task;
import cn.edu.whu.boot.xml.service.ITaskService;
import cn.edu.whu.boot.xml.xmlreader.bean.Config;
import cn.edu.whu.boot.xml.xmlreader.bean.Table;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class TaskInfoServiceImpl implements TaskInfoService {

    private final Logger log = LoggerFactory.getLogger(TaskInfoServiceImpl.class);

    @Autowired
    private TaskCellDataService taskCellDataService;

    @Autowired
    private TaskCellService taskCellService;

    @Autowired
    private TaskUserCellService taskUserCellService;

    @Autowired
    private TaskUserRoleService taskUserRoleService;

    @Autowired
    private IBaseService baseService;

    @Autowired
    private ITaskService taskService;

    @Autowired
    private SimpleTaskMapper taskMapper;

    @Autowired
    private CacheService cacheService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateTaskInfo(List<TaskCellData> cellToData,
                                  List<TaskCell> taskCells,
                                  List<TaskUserCell> taskUserCell,
                                  List<TaskUserRole> taskUserRole,
                                  Task task,
                                  Map<String, Map<Long, Integer>> assignedData) {
        // 将数据-网格的对应关系保存到数据库
        taskCellDataService.insertList(cellToData);
        // 将网格的信息保存到数据库
        taskCellService.insertList(taskCells);
        // 将人员-网格的对应关系保存到数据库
        taskUserCellService.insertList(taskUserCell);
        // 将该任务中给个外业人员的具体角色保存到数据库中
        taskUserRoleService.insertList(taskUserRole);
        // 将数据-人员的对应关系保存到数据库
        if (task == null || assignedData == null || assignedData.isEmpty()) {
            return true;
        }
        String updateReviewerResult = baseService.updateRootTableDataForReviewer(task, assignedData);
        if (!updateReviewerResult.equals("succeed")) {
            log.error("更新任务{}的数据-人员对应关系时发生异常", task.getId());
            return false;
        }

        // 更新缓存
        cacheService.delete(RedisKeyType.TASK + String.valueOf(task.getId()));
        cacheService.delete(RedisKeyType.TASK + "all");

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateCell2WorkerAndData2Worker(int taskId,
                                                Map<String, Map<Long, Integer>> tbToDataAndWorker) throws Exception {
        // 如果巡查人员之前在某个网格中没有任务，那么需要更新 task_user_cell
        List<TaskUserCell> taskUserCells = getNewTaskUserCells(taskId, tbToDataAndWorker);
        if (!taskUserCells.isEmpty()) {
            taskUserCellService.insertList(taskUserCells);
        }

        // 更新数据-人员的对应关系
        String updateReviewerResult = baseService.updateRootTableDataForReviewer(taskService.getTaskById(taskId), tbToDataAndWorker);
        if (!updateReviewerResult.equals("succeed")) {
            throw new Exception(String.format("更新任务 %d 的数据-人员对应关系时发生异常", taskId));
        }

        if (!taskUserCells.isEmpty()) {
            cacheService.delete(RedisKeyType.CELL.toString() + taskId);
        }
    }

    private List<TaskUserCell> getNewTaskUserCells(int taskId, Map<String, Map<Long, Integer>> tbToDataAndWorker) throws InvalidAssignmentStatus {
        List<TaskUserCell> newTaskUserCells = new ArrayList<>(); // 需要往 task_user_cell 中插入的记录
        Set<Integer> validatedNewReviewers = new HashSet<>(); // 已经通过身份检查的新的巡查人员
        for (Map.Entry<String, Map<Long, Integer>> e1 : tbToDataAndWorker.entrySet()) {
            Map<Integer, List<Long>> reviewerToData = new HashMap<>(); // 每个巡查人员对应的数据
            String tableName = e1.getKey();
            for (Map.Entry<Long, Integer> e2 : e1.getValue().entrySet()) {
                long dataId = e2.getKey();
                int reviewerId = e2.getValue();
                if (validatedNewReviewers.add(reviewerId)) {
                    // 检查该用户的身份是否合法
                    Optional<Integer> optionalRole = taskUserRoleService.findWorkRoleByTaskIdAndUserId(taskId, reviewerId);
                    if (!optionalRole.isPresent()) {
                        throw new InvalidAssignmentStatus(String.format("人员 %d 不在 %d 号任务中", reviewerId, taskId));
                    }
                    if (optionalRole.get().equals(0)) {
                        throw new InvalidAssignmentStatus(String.format("人员 %d 是采集人员，不能审核数据", reviewerId));
                    }
                }
                reviewerToData.computeIfAbsent(reviewerId, k -> new ArrayList<>()).add(dataId);
            }

            for (Map.Entry<Integer, List<Long>> e : reviewerToData.entrySet()) {
                List<Long> dataIds = e.getValue();
                Integer reviewerId = e.getKey();
                List<String> geohash = taskCellDataService.findCellsWhereUserIsAbsent(taskId, tableName, reviewerId, dataIds);
                if (geohash != null) {
                    geohash.forEach(h -> newTaskUserCells.add(new TaskUserCell(taskId, reviewerId, h)));
                }
            }
        }
        return newTaskUserCells;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateCell2WorkerAndData2Worker(int taskId,
                                                Map<String, Integer> cellToWorker,
                                                Map<String, Map<Long, Integer>> dataToWorker) throws Exception {
        // 更新表 task_user_cell
        taskUserCellService.updateUserIdByTaskIdAndCellGeohash(taskId, cellToWorker);
        // 更新数据-人员的对应关系
        String updateReviewerResult = baseService.updateRootTableDataForReviewer(taskService.getTaskById(taskId), dataToWorker);
        if (!updateReviewerResult.equals("succeed")) {
            throw new Exception(String.format("更新任务 %d 的数据-人员对应关系时发生异常", taskId));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUser2RoleAndUser2CellAndUser2Data(int taskId, int userId, boolean isReviewer) throws Exception {
        // 更新 task_user_role
        taskUserRoleService.deleteByTaskIdAndUserId(taskId, userId);
        // 更新 task_user_cell
        taskUserCellService.deleteByTaskIdAndUserId(taskId, userId);
        // TODO: 更新人员-数据的对应关系
        if (!isReviewer) {
            return;
        }
        Task task = taskService.getTaskById(taskId);
        String databaseName = task.getDatabaseName();
        Map<String, Table> tables = JSON.toJavaObject(JSON.parseObject(task.getConfig()), Config.class).getRootTables();
        String formatUpdateSql = "UPDATE {0} SET reviewer = -1 where {1} IN (SELECT {1} from {0} WHERE reviewer = {2});";
        for (Map.Entry<String, Table> tableEntry : tables.entrySet()) {
            String updateResult = baseService.updateSqlString(
                    MessageFormat.format(formatUpdateSql, PostgreSqlUtil.buildTableNameString(databaseName, tableEntry.getKey(), false, false),
                            PostgreSqlUtil.transformString(TableOperationUtil.getPrimaryKey(tableEntry.getValue())), userId));
            if (updateResult.equals(CommonConstant.FAILED)) {
                throw new Exception("失败");
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateUser2RoleAndUser2CellAndUser2Data(int taskId, String geohash, int userId, boolean isReviewer) throws Exception {
        boolean free = false;
        // 从 task_user_cell 中删除
        taskUserCellService.deleteByTaskIdAndCellGeohashAndUserId(taskId, geohash, userId);
//        if (taskUserCellService.countByTaskIdAndUserId(taskId, userId) == 0) {
//            // 从 task_user_role 中删除
//            taskUserRoleService.deleteByTaskIdAndUserId(taskId, userId);
//            free =  true;
//        }
        // TODO: 如果是审核人员，更新人员-数据的对应关系
        if (isReviewer) {
            Map<String, Map<Long, Integer>> updateData = new HashMap<>();
            taskCellDataService
                    .findByTaskIdAndGeohash(taskId, geohash)
                    .stream()
                    .collect(Collectors.groupingBy(TaskCellData::getTableName))
                    .forEach((table, list) -> {
                        Map<Long, Integer> dataToWorker = list
                                .stream()
                                .map(TaskCellData::getDataId)
                                .map(dataId -> new AbstractMap.SimpleEntry<>(dataId, -1))
                                .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
                        updateData.put(table, dataToWorker);
                    });
            String updateReviewerResult = baseService.updateRootTableDataForReviewer(taskService.getTaskById(taskId), updateData);
            if (!updateReviewerResult.equals("succeed")) {
                throw new Exception(String.format("更新任务 %d 的数据-人员对应关系时发生异常", taskId));
            }
        }
        return free;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteTaskInfo(int taskId) {
        taskCellDataService.deleteByTaskId(taskId);
        taskCellService.deleteByTaskId(taskId);
        taskUserCellService.deleteByTaskId(taskId);
        taskUserRoleService.deleteByTaskId(taskId);
        cacheService.delete(RedisKeyType.CELL.toString() + taskId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void completeChangingWorkersInCell(int taskId, String geohash, Collection<Integer> userIds, Map<String, Map<Long, Integer>> assignedData) throws Exception {
        List<TaskUserCell> taskUserCells = userIds
                .stream()
                .map(userId -> {
                    TaskUserCell tuc = new TaskUserCell();
                    tuc.setUserId(userId);
                    tuc.setCellGeohash(geohash);
                    tuc.setTaskId(taskId);
                    return tuc;
                })
                .collect(Collectors.toList());

        taskUserCellService.deleteByTaskIdAndCellGeohash(taskId, geohash);
        taskUserCellService.insertList(taskUserCells);
        if (assignedData != null && !assignedData.isEmpty()) {
            String updateReviewerResult = baseService.updateRootTableDataForReviewer(taskService.getTaskById(taskId), assignedData);
            if (!updateReviewerResult.equals("succeed")) {
                throw new Exception(String.format("更新任务 %d 的数据-人员对应关系时发生异常", taskId));
            }
        }

        // 使网格相关的缓存失效
        cacheService.delete(RedisKeyType.CELL.toString() + taskId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void completeChangingWorkers(int taskId, Map<Integer, Integer> appendWorker2role, Map<Integer, Integer> deleteWorker2role) throws Exception {
        // 注意要先删后增，这样就可以修改已有工作人员的工作角色
        // 删除工作人员
        if (deleteWorker2role != null && !deleteWorker2role.isEmpty()) {
            for (Map.Entry<Integer, Integer> e : deleteWorker2role.entrySet()) {
                updateUser2RoleAndUser2CellAndUser2Data(taskId, e.getKey(), e.getValue().equals(1));
            }
            cacheService.delete(RedisKeyType.CELL.toString() + taskId);
        }

        // 添加工作人员
        if (appendWorker2role != null && !appendWorker2role.isEmpty()) {
            List<TaskUserRole> userRoles = appendWorker2role
                    .entrySet()
                    .stream()
                    .map(e -> new TaskUserRole(taskId, e.getKey(), e.getValue()))
                    .collect(Collectors.toList());
            taskUserRoleService.insertList(userRoles);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void setTaskAllocationOptional(int taskId, boolean optional) {
        taskMapper.updateTaskAllocationOptional(taskId, optional ? 1 : 0);
        // 如果不需要分配，把原先的分配结果清空
        if (optional) {
            deleteTaskInfo(taskId);
        }
        // 更新缓存
        cacheService.delete(RedisKeyType.TASK + String.valueOf(taskId));
        cacheService.delete(RedisKeyType.TASK + "hasCreate");
        cacheService.delete(RedisKeyType.TASK + "all");
    }

    @Override
    public boolean isTaskAllocationOptional(int taskId) {
        Integer result = taskMapper.isTaskAllocationOptional(taskId);
        return result != null && (result == 1);
    }
}
