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

import cn.edu.whu.boot.allocation.controllers.dtos.Worker;
import cn.edu.whu.boot.allocation.entities.TaskUserCell;
import cn.edu.whu.boot.allocation.mapper.TaskUserCellMapper;
import cn.edu.whu.boot.allocation.services.TaskUserCellService;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
public class TaskUserCellServiceImpl implements TaskUserCellService {

    private final TaskUserCellMapper taskUserCellMapper;

    private final SqlSessionFactory sqlSessionFactory;

    public TaskUserCellServiceImpl(TaskUserCellMapper taskUserCellMapper, SqlSessionFactory sqlSessionFactory) {
        this.taskUserCellMapper = taskUserCellMapper;
        this.sqlSessionFactory = sqlSessionFactory;
    }

    @Override
    public Optional<TaskUserCell> findById(int id) {
        return Optional.ofNullable(taskUserCellMapper.findById(id));
    }

    @Override
    public List<TaskUserCell> findByTaskIdAndUserId(int taskId, int userId) {
        return taskUserCellMapper.findByTaskIdAndUserId(taskId, userId);
    }

    @Override
    public List<Worker> findWorkerByTaskId(int taskId) {
        return taskUserCellMapper.findCellAndUserByTaskId(taskId);
    }

    @Override
    public List<Worker> findWorkerByTaskIdAndUserId(int taskId, int userId) {
        return taskUserCellMapper.findCellAndUserByTaskIdAndUserId(taskId, userId);
    }

    @Override
    public Optional<TaskUserCell> findByTaskIdAndUserIdAndCellGeohash(int taskId, int userId, String geohash) {
        return Optional.ofNullable(taskUserCellMapper.findByTaskIdAndUserIdAndCellGeohash(taskId, userId, geohash));
    }

    @Override
    public int countByTaskIdAndUserId(int taskId, int userId) {
        return taskUserCellMapper.countByTaskIdAndUserId(taskId, userId);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insert(TaskUserCell taskUserCell) {
        return taskUserCellMapper.insert(taskUserCell);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertList(List<TaskUserCell> list) {
        if (list == null || list.isEmpty()) {
            return;
        }

        if (list.size() * 3 <= 2000) {
            taskUserCellMapper.insertForeach(list);
        } else {
            try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
                TaskUserCellMapper mapper = session.getMapper(TaskUserCellMapper.class);
                list.forEach(mapper::insert);
                session.commit();
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUserIdByTaskIdAndCellGeohash(int taskId, Map<String, Integer> cellToWorker) throws Exception {
        if (cellToWorker == null || cellToWorker.isEmpty()) {
            return;
        }
        for (Map.Entry<String, Integer> entry : cellToWorker.entrySet()) {
            String geohash = entry.getKey();
            Integer userId = entry.getValue();
            int affectedRow = taskUserCellMapper.updateUserIdByTaskIdAndCellGeohash(taskId, geohash, userId);
            if (affectedRow == 0) {
                throw new Exception(
                        String.format(
                                "修改失败，不存在 %d 号任务或是该任务中不存在编号为 %s 的网格",
                                taskId,
                                geohash
                        )
                );
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteByTaskId(int taskId) {
        taskUserCellMapper.deleteByTaskId(taskId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteByTaskIdAndUserId(int taskId, int userId) {
        taskUserCellMapper.deleteByTaskIdAndUserId(taskId, userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteByTaskIdAndCellGeohashAndUserId(int taskId, String geohash, int userId) {
        taskUserCellMapper.deleteByTaskIdAndCellGeohashAndUserId(taskId, geohash, userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteByTaskIdAndCellGeohash(int taskId, String geohash) {
        taskUserCellMapper.deleteByTaskIdAndCellGeohash(taskId, geohash);
    }
}
