package com.ruoyi.sim.service.impl;

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

import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.sim.domain.*;
import com.ruoyi.sim.domain.vo.RealExamVo;
import com.ruoyi.sim.domain.vo.StudentRealExamIngVo;
import com.ruoyi.sim.domain.vo.StudentRealExamPostVo;
import com.ruoyi.sim.domain.vo.StudentRealExamPreVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.sim.mapper.RealExamMapper;
import org.springframework.transaction.annotation.Transactional;

/**
 * 考试Service业务层处理
 *
 * @author tom
 * @date 2024-12-15
 */
@Service
public class RealExamService {
    @Autowired
    private RealExamMapper realExamMapper;

    /**
     * 查询考试
     *
     * @param examId 考试主键
     * @return 考试
     */
    public RealExam selectRealExamByExamId(Long examId) {
        return realExamMapper.selectRealExamByExamId(examId);
    }

    /**
     * 查询考试列表
     *
     * @param realExam 考试
     * @return 考试
     */
    public List<RealExam> selectRealExamList(RealExam realExam) {
        return realExamMapper.selectRealExamList(realExam);
    }

    /**
     * 新增考试
     *
     * @param realExam 考试
     * @return 结果
     */
    public int insertRealExam(RealExam realExam) {
        realExam.setCreateTime(DateUtils.getNowDate());
        return realExamMapper.insertRealExam(realExam);
    }

    /**
     * 修改考试
     *
     * @param realExam 考试
     * @return 结果
     */
    public int updateRealExam(RealExam realExam) {
        realExam.setUpdateTime(DateUtils.getNowDate());
        return realExamMapper.updateRealExam(realExam);
    }

    /**
     * 批量删除考试
     *
     * @param examIds 需要删除的考试主键
     * @return 结果
     */
    public int deleteRealExamByExamIds(Long[] examIds) {
        return realExamMapper.deleteRealExamByExamIds(examIds);
    }

    /**
     * 删除考试信息
     *
     * @param examId 考试主键
     * @return 结果
     */
    public int deleteRealExamByExamId(Long examId) {
        return realExamMapper.deleteRealExamByExamId(examId);
    }

    // -------------------------------- tom add  --------------------------------
    private static final Logger l = LoggerFactory.getLogger(CommSendService.class);

    @Autowired
    private CommReceiveService commReceiveService;
    @Autowired
    private StudentService studentService;
    @Autowired
    private SimService simService;
    @Autowired
    private SeatService seatService;
    @Autowired
    private RealExamCollectionService realExamCollectionService;
    @Autowired
    private RealExamFaultService realExamFaultService;
    @Autowired
    private CommSendService commSendService;


    public List<RealExamVo> list(RealExam q) {
        List<RealExamVo> list = new ArrayList<>();
        realExamMapper.selectRealExamList(q).forEach(re -> {
            RealExamVo v = new RealExamVo();
            RealExamCollection rec = realExamCollectionService.selectRealExamCollectionByExamCollectionId(re.getExamCollectionId());
            v.setRealExam(re);
            v.setRealExamCollection(rec);
            list.add(v);
        });
        return list;
    }

    public List<RealExam> listAllByStatus(String state) {
        RealExam q = new RealExam();
        q.setExamStatus(state);
        return selectRealExamList(q);
    }

    /**
     * 交卷自动修改关联状态
     *
     * @param examId
     * @param state
     * @return
     */
    @Transactional
    public int updateOneState(long examId, final String state) {
        RealExam q = selectRealExamByExamId(examId);
        // todo:屏蔽
        if (false && RealExam.State.SUBMITTED.equals(state)) {
            // 关联故障list同步锁死。
            realExamFaultService.listAllType2LoopReadStateByExamId(q.getExamId())
                    .forEach(ref -> {
                        ref.setRefState(RealExamFault.State.FINISH);
                        realExamFaultService.updateRealExamFault(ref);
                    });
        }
        q.setExamStatus(state);
        return updateRealExam(q);
    }

    /**
     * [学生]进入考试。
     *
     * @return
     */
    public AjaxResult studentEnterRealExam(Long examId) {
        RealExam re = selectRealExamByExamId(examId);
        if (re == null) {
            AjaxResult.error("realExamId error!");
        }
        // todo:应该在登录位置实现
        // todo: temp
        updateOneState(examId, RealExam.State.LOGGED_IN);
        // todo: temp
        realExamFaultService.resetAllType2(examId);
        return AjaxResult.success(re);
    }

    /**
     * [轮询][学生]准备考试界面。
     *
     * @param realExamId
     * @return
     */
    public AjaxResult studentLoopPrepareRealExam(Long realExamId) {
        l.info("studentLoopPrepareRealExam");
        // check
        if (realExamId == null || realExamId == 0) {
            // todo:
        }
        //
        RealExam re = selectRealExamByExamId(realExamId);
        if (re == null) {
            // todo:
        }
        // todo: 日期，不可进入考试。
        //
        // todo: 验证学生登录身份
        Objects.requireNonNull(re);
        RealExamCollection collection =
                realExamCollectionService.selectRealExamCollectionByExamCollectionId(re.getExamCollectionId());
        // check collection
        Sim sim = simService.selectSimBySimId(re.getSimId());
        // check sim
        Student student = studentService.selectStudentByUserId(re.getUserId());
        // check student
        Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
        // check seat
        StudentRealExamPreVo vo = new StudentRealExamPreVo();
        vo.setRealExam(re);
        vo.setRealExamCollection(collection);
        vo.setSim(sim);
        vo.setStudent(student);
        vo.setSeat(seat);
        // todo:多人请求同时进入的问题
        boolean next = studentPrepareRealExamCheck(vo);
        vo.setNext(next);
        if (!next) {
            // 执行模拟器通信，让模拟器准备好。
            // 异步执行
            commSendService.clearListFaultByRealExamAsync(re);
        }
        l.info("vo = {}", vo);

        return AjaxResult.success(vo);
    }

    public boolean studentPrepareRealExamCheck(StudentRealExamPreVo v) {
        if (v == null ||
                v.getRealExam() == null ||
                v.getRealExamCollection() == null ||
                v.getSim() == null ||
                v.getStudent() == null ||
                v.getSeat() == null) {
            return false;
        }
        // todo:在考试日期内。
        // check 一个模拟器的所有选中故障点位都下发成功，准备是否可以
        //
        // todo:??
        // 学生答题中可以再次进入。
        String examStatus = v.getRealExam().getExamStatus();
        String simStatus = v.getSim().getSimState();

        // 兜底
        {
            if (realExamFaultService.isAllType2StateXiaFa(v.getRealExam().getExamId())) {
                updateOneState(v.getRealExam().getExamId(), RealExam.State.SIM_PREPARE_OK);
            }
        }
        if ((RealExam.State.SIM_PREPARE_OK.equals(examStatus) ||
                RealExam.State.ANSWERING.equals(examStatus)) &&
                Sim.State.ONLINE.equals(simStatus)
        ) {
            return true;
        }
        return false;
    }

    /**
     * [学生]开始考试
     *
     * @param examId
     * @return
     */
    @Transactional
    public AjaxResult studentStartRealExam(Long examId) {
        l.info("studentStartRealExam");
        RealExam re = selectRealExamByExamId(examId);
        l.info("re = {}", re);
        re.setExamStatus(RealExam.State.ANSWERING);
        re.setStartTime(DateUtils.getNowDate());
        updateRealExam(re);
        return AjaxResult.success(re);
    }

    /**
     * [轮询][学生]正在考试界面。
     *
     * @param realExamId
     * @return
     */
    public AjaxResult studentLoopAnsweringRealExam(Long realExamId) {
        RealExam re = selectRealExamByExamId(realExamId);
        RealExamCollection rec = realExamCollectionService.selectRealExamCollectionByExamCollectionId(re.getExamCollectionId());
        StudentRealExamIngVo vo = new StudentRealExamIngVo();
        vo.setRealExam(re);
        long remaining = (re.getStartTime().getTime() + rec.getLimitDuration() * 60 * 1000) - DateUtils.getNowDate().getTime();
        vo.setRemainingMilliseconds(remaining);
        vo.setCompulsiveSubmit(remaining >= RealExam.EXAM_TIMEOUT_LIMIT);
        l.info("studentLoopAnsweringRealExam vo = {}", vo);

        return AjaxResult.success(vo);
    }

    /**
     * [学生]交卷
     *
     * @param examId
     * @return
     */
    public AjaxResult studentSubmitRealExam(Long examId) {
        // 最后检查一下模拟器状态。

        // 最后读取一下模拟器电阻值。
        // todo:
        RealExam re1 = selectRealExamByExamId(examId);
        commSendService.readOneExamAtLastAsync(re1);

        submit(examId);

        return AjaxResult.success(re1);
    }

    /**
     * @param examId
     */
    @Transactional
    public void submit(long examId) {
        l.info("submit");
        RealExam re2 = selectRealExamByExamId(examId);
        re2.setExamStatus(RealExam.State.SUBMITTED);
        re2.setEndTime(DateUtils.getNowDate());
        updateRealExam(re2);
    }

    /**
     * [轮询][学生]结束考试界面。
     *
     * @param examId
     * @return
     */
    public AjaxResult studentLoopPostRealExam(Long examId) {
        RealExam re = selectRealExamByExamId(examId);
        StudentRealExamPostVo vo = new StudentRealExamPostVo();
        {

        }
        vo.setRealExam(re);
        vo.setListPart1(realExamFaultService.getReportListPart1(examId));
        return AjaxResult.success(vo);
    }
}
