package com.qmth.exam.reserve.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.boot.core.collection.PageResult;
import com.qmth.boot.core.exception.StatusException;
import com.qmth.boot.tools.excel.ExcelReader;
import com.qmth.boot.tools.excel.enums.ExcelType;
import com.qmth.boot.tools.excel.model.DataMap;
import com.qmth.exam.reserve.bean.login.LoginUser;
import com.qmth.exam.reserve.bean.room.ExamRoomReq;
import com.qmth.exam.reserve.bean.room.ExamRoomSaveReq;
import com.qmth.exam.reserve.bean.room.ExamRoomVO;
import com.qmth.exam.reserve.cache.impl.ApplyTaskCacheService;
import com.qmth.exam.reserve.dao.ExamRoomDao;
import com.qmth.exam.reserve.entity.ExamRoomEntity;
import com.qmth.exam.reserve.entity.ExamSiteEntity;
import com.qmth.exam.reserve.service.CategoryService;
import com.qmth.exam.reserve.service.ExamRoomService;
import com.qmth.exam.reserve.service.ExamSiteService;
import com.qmth.exam.reserve.util.PageUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.io.InputStream;
import java.util.*;

@Service
public class ExamRoomServiceImpl extends ServiceImpl<ExamRoomDao, ExamRoomEntity> implements ExamRoomService {

    private static final Logger log = LoggerFactory.getLogger(ExamRoomServiceImpl.class);
    private static final String[] EXCEL_HEADER = new String[]{"考场号", "考场名称", "考场地址", "考场容量", "所属考点代码", "所属考点名称"};

    @Autowired
    private ExamSiteService examSiteService;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private ApplyTaskCacheService cacheService;


    @Override
    public PageResult<ExamRoomVO> pageExamRoom(ExamRoomReq req) {
        IPage<ExamRoomVO> iPage = baseMapper.pageExamRoom(new Page<>(req.getPageNumber(), req.getPageSize()),
                req);
        return PageUtil.of(iPage);
    }

    @Transactional
    @Override
    public void saveExamRoom(LoginUser user, ExamRoomSaveReq req) {
        checkExamRoom(req);
        ExamRoomEntity examRoomEntity = new ExamRoomEntity();
        BeanUtils.copyProperties(req, examRoomEntity);
        ExamRoomEntity beforeUpdateRoom = new ExamRoomEntity();
        if (req.getId() == null) {
            examRoomEntity.setEnable(Boolean.TRUE);
            save(examRoomEntity);
        } else {
            beforeUpdateRoom = getById(req.getId());
            updateById(examRoomEntity);
        }
        //更新考点容量和教学点容量
        updateExamSiteAndTeachingCapacity(req.getExamSiteId());
        if(req.getId() != null && !req.getExamSiteId().equals(beforeUpdateRoom.getExamSiteId())) {
            updateExamSiteAndTeachingCapacity(beforeUpdateRoom.getExamSiteId());
        }
    }

    @Transactional
    @Override
    public void enable(Long id, Boolean enable) {
        ExamRoomEntity examRoom = getById(id);
        LambdaUpdateWrapper<ExamRoomEntity> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(ExamRoomEntity::getEnable, enable);
        wrapper.eq(ExamRoomEntity::getId, id);
        update(null, wrapper);

        updateExamSiteAndTeachingCapacity(examRoom.getExamSiteId());
    }

    @Override
    public List<Map<String, Object>> importExamRoom(LoginUser user, InputStream inputStream) {
        List<DataMap> lineList = null;
        ExcelReader reader = ExcelReader.create(ExcelType.XLSX, inputStream, 0);
        try {
            lineList = reader.getDataMapList();
        } catch (Exception e) {
            throw new StatusException("Excel 解析失败");
        }
        if (com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isEmpty(lineList)) {
            throw new StatusException("Excel无内容");
        }
        List<Map<String, Object>> failRecords = new ArrayList<>();
        List<ExamRoomEntity> examRoomList = new ArrayList<>();
        Long teachingId = user.getCategoryId();
        for (int i = 0; i < lineList.size(); i++) {
            DataMap line = lineList.get(i);
            ExamRoomEntity room = new ExamRoomEntity();
            StringBuilder msg = new StringBuilder();
            String code = trimAndNullIfBlank(line.get(EXCEL_HEADER[0]));
            if (StringUtils.isBlank(code)) {
                msg.append(" 考场号不能为空");
            } else if(code.length() > 20) {
                msg.append(" 考场号不能超过20个字符");
            } else {
                room.setCode(code);
            }
            String name = trimAndNullIfBlank(line.get(EXCEL_HEADER[1]));
            if (StringUtils.isBlank(name)) {
                msg.append(" 考场名称不能为空");
            } else if (name.length() > 50) {
                msg.append(" 考场名称不能超过50个字符");
            } else {
                room.setName(name);
            }
            String address = trimAndNullIfBlank(line.get(EXCEL_HEADER[2]));
            if (StringUtils.isBlank(address)) {
                msg.append(" 考场地址不能为空");
            } else if (address.length() > 100) {
                msg.append(" 考场地址不能超过100个字符");
            } else {
                room.setAddress(address);
            }
            String capacity = trimAndNullIfBlank(line.get(EXCEL_HEADER[3]));
            if (StringUtils.isBlank(capacity) || !capacity.matches("\\d+") || Integer.parseInt(capacity) <= 0 || Integer.parseInt(capacity) > 99999) {
                msg.append(" 考场容量必须为大于0且小于100000的整数");
            } else {
                room.setCapacity(Integer.parseInt(capacity));
            }
            String examSiteCode = trimAndNullIfBlank(line.get(EXCEL_HEADER[4]));
            if (StringUtils.isBlank(examSiteCode)) {
                msg.append(" 所属考点代码不能为空");
            }
            String examSiteName = trimAndNullIfBlank(line.get(EXCEL_HEADER[5]));
            if (StringUtils.isBlank(examSiteName)) {
                msg.append(" 所属考点名称不能为空");
            }
            List<ExamSiteEntity> examSiteList = listExamSite(examSiteCode, examSiteName, teachingId);
            if (examSiteList.isEmpty()) {
                msg.append(" 找不到所属考点");
            }
            if (examSiteList.size() > 1) {
                msg.append(" 找到的所属考点大于1个");
            }
            if (examSiteList.size() == 1) {
//                ExamRoomEntity examRoom = getExamRoom(examSiteList.get(0).getId(), code);
//                if (examRoom != null) {
//                    msg.append(" 当前考点下已经存在考场代码为：").append(code).append("的考场");
//                } else {
                    room.setExamSiteId(examSiteList.get(0).getId());
                    room.setEnable(Boolean.TRUE);
//                }
            }

            if (msg.length() > 0) {
                failRecords.add(newError(i + 1, msg.toString()));
            } else {
                examRoomList.add(room);
            }
        }

        if (!failRecords.isEmpty())
            return failRecords;

        for (int i = 0; i < examRoomList.size(); i++) {
            ExamRoomEntity examRoomEntity = examRoomList.get(i);
            try {
                saveRoom(examRoomEntity);
            } catch (Exception e) {
                failRecords.add(newError(i + 1, " 系统异常"));
                log.error("导入异常", e);
            }
        }

        if (CollectionUtils.isNotEmpty(failRecords)) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return failRecords;
        }

        return failRecords;
    }

    @Override
    public List<ExamRoomEntity> listExamRoom(Long examSiteId) {
        LambdaQueryWrapper<ExamRoomEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ExamRoomEntity::getExamSiteId, examSiteId);
        wrapper.eq(ExamRoomEntity::getEnable, Boolean.TRUE);
        wrapper.orderByAsc(ExamRoomEntity::getCode);
        return list(wrapper);
    }

    private void saveRoom(ExamRoomEntity room) {
        ExamRoomEntity examRoom = getExamRoom(room.getExamSiteId(), room.getCode());
        if (examRoom != null) {
            examRoom.setCapacity(room.getCapacity());
            LambdaUpdateWrapper<ExamRoomEntity> wrapper = new LambdaUpdateWrapper<>();
            wrapper.set(ExamRoomEntity::getName, room.getName());
            wrapper.set(ExamRoomEntity::getAddress, room.getAddress());
            wrapper.set(ExamRoomEntity::getCapacity, room.getCapacity());
            wrapper.set(ExamRoomEntity::getEnable, Boolean.TRUE);
            wrapper.eq(ExamRoomEntity::getId, examRoom.getId());
            update(null, wrapper);
        } else {
            save(room);
        }
        updateExamSiteAndTeachingCapacity(room.getExamSiteId());
    }

    private List<ExamSiteEntity> listExamSite(String examSiteCode, String examSiteName, Long categoryId) {
        ExamSiteEntity examSiteEntity = null;
        LambdaQueryWrapper<ExamSiteEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ExamSiteEntity::getCode, examSiteCode);
        wrapper.eq(ExamSiteEntity::getName, examSiteName);
        wrapper.eq(categoryId != null, ExamSiteEntity::getCategoryId, categoryId);
        return examSiteService.list(wrapper);
    }

    private void updateExamSiteAndTeachingCapacity(Long examSiteId) {
        ExamSiteEntity oldExamSite = examSiteService.getById(examSiteId);
        // 更新考点容量
        examSiteService.updateExamSiteCapacity(examSiteId);

        ExamSiteEntity newExamSite = examSiteService.getById(examSiteId);
        //更新教学点容量
        categoryService.updateTeachingCapacity(newExamSite.getCategoryId());

        //刷新考点容量缓存
        cacheService.refreshApplyAvailableCountCache(examSiteId, oldExamSite.getCapacity(), newExamSite.getCapacity());
    }

    private void checkExamRoom(ExamRoomSaveReq req) {
        if (StringUtils.isEmpty(req.getCode())) {
            throw new StatusException("考场代码不能为空");
        }
        if (StringUtils.isEmpty(req.getName())) {
            throw new StatusException("考场名称不能为空");
        }
        if (StringUtils.isEmpty(req.getAddress())) {
            throw new StatusException("考场地址不能为空");
        }
        if (req.getCapacity() == null || req.getCapacity() <= 0) {
            throw new StatusException("考场容量必须大于0");
        }
        if (req.getExamSiteId() == null) {
            throw new StatusException("请选择考点");
        }
        checkExamRoomExist(req, req.getId() != null);
    }

    private void checkExamRoomExist(ExamRoomSaveReq req, boolean idFlag) {
        LambdaQueryWrapper<ExamRoomEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ExamRoomEntity::getExamSiteId, req.getExamSiteId());
        wrapper.eq(ExamRoomEntity::getCode, req.getCode());
        wrapper.notIn(idFlag, ExamRoomEntity::getId, req.getId());
        ExamRoomEntity examRoomEntity = baseMapper.selectOne(wrapper);
        if (examRoomEntity != null) {
            ExamSiteEntity site = examSiteService.getById(req.getExamSiteId());
            throw new StatusException(site.getName() + "下已存在代码为:" + req.getCode() + "的考场");
        }
    }

    private ExamRoomEntity getExamRoom(Long examSiteId, String roomCode) {
        LambdaQueryWrapper<ExamRoomEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ExamRoomEntity::getExamSiteId, examSiteId);
        wrapper.eq(ExamRoomEntity::getCode, roomCode);
        return baseMapper.selectOne(wrapper);
    }

    private String trimAndNullIfBlank(String s) {
        if (StringUtils.isBlank(s)) {
            return null;
        }
        return s.trim();
    }

    private Map<String, Object> newError(int lineNum, String msg) {
        Map<String, Object> map = new HashMap<>();
        map.put("lineNum", lineNum);
        map.put("msg", msg);
        return map;
    }
}
