package rd.service.user;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yitter.idgen.YitIdHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import rd.dao.mapper.user.StudentInfoMapper;
import rd.data.Constants;
import rd.data.code.ServiceCode;
import rd.data.dto.user.UserBasicInfoDTO;
import rd.data.dto.user.student.BasicStudentInfoDto;
import rd.data.dto.user.student.BasicStudentInfoWithMajorNameDto;
import rd.data.dto.user.student.NewStudentDTO;
import rd.data.dto.user.student.StudentUserInfoDTO;
import rd.data.db.table.StudentInfo;
import rd.data.enums.UserType;
import rd.data.model.response.PaginationWrapper;
import rd.exception.ServiceException;

import rd.rpc.WusthelperApi;
import rd.service.major.MajorService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
public class StudentUserService {
    private final StudentInfoMapper studentInfoMapper;
    private final UserBasicService userBasicService;
    private final UserInfoService userInfoService;
    private final MajorService majorService;

    private final WusthelperApi wusthelperApi;
    private final StringRedisTemplate stringRedisTemplate;

    public StudentUserService(StudentInfoMapper studentInfoMapper,
                              UserBasicService userBasicService,
                              UserInfoService userInfoService,
                              MajorService majorService,
                              WusthelperApi wusthelperApi, StringRedisTemplate stringRedisTemplate) {

        this.studentInfoMapper = studentInfoMapper;
        this.userBasicService = userBasicService;
        this.userInfoService = userInfoService;
        this.majorService = majorService;

        this.wusthelperApi = wusthelperApi;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    public PaginationWrapper<List<StudentInfo>> list(int page, int pageSize) {
        if (pageSize == 0) {
            pageSize = 1024;
        }

        Page<StudentInfo> queryPage = new Page<>(page, pageSize);
        var result = studentInfoMapper.selectPage(queryPage, new QueryWrapper<StudentInfo>()
                .eq("deleted", false)
        );

        return new PaginationWrapper<>(
                (int) result.getCurrent(),
                (int) result.getSize(),
                (int) result.getTotal(),
                result.getRecords()
        );
    }

    public StudentInfo getById(Long id) {
        var result = studentInfoMapper.selectOne(new QueryWrapper<StudentInfo>()
                .eq("id", id)
                .eq("deleted", false)
        );

        if (result == null) {
            throw ServiceException.error(ServiceCode.UserNotExists);
        }

        return result;
    }

    public List<StudentInfo> getByIds(List<Long> idList) {
        if (idList.isEmpty()) {
            return new ArrayList<>();
        }

        var result = studentInfoMapper.selectList(new QueryWrapper<StudentInfo>()
                .in("id", idList)
                .eq("deleted", false)
        );

        if (result.isEmpty()) {
            throw ServiceException.error(ServiceCode.UserNotExists);
        }

        return result;
    }

    public StudentUserInfoDTO getByStudentNumber(String studentNumber) {
        var result = studentInfoMapper.selectOne(new QueryWrapper<StudentInfo>()
                .eq("student_number", studentNumber)
                .eq("deleted", false)
        );

        if (result == null) {
            throw ServiceException.error(ServiceCode.UserNotExists);
        }

        var userBasic = userBasicService.getUserBasicById(result.getId());
        if (userBasic == null) {
            throw ServiceException.error(ServiceCode.UserNotExists);
        }

        return StudentUserInfoDTO.builder()
                .id(result.getId())
                .username(userBasic.getUsername())
                .number(result.getStudentNumber())
                .name(result.getName())
                .major(result.getMajor())
                .grade(result.getGrade())
                .clazz(result.getClazz())
                .build();
    }

    public StudentUserInfoDTO getInfoById(Long id) {
        var result = studentInfoMapper.selectOne(new QueryWrapper<StudentInfo>()
                .eq("id", id)
                .eq("deleted", false)
        );

        var userBasic = userBasicService.getUserBasicById(id);

        if (result == null || userBasic == null) {
            throw ServiceException.error(ServiceCode.UserNotExists);
        }

        return StudentUserInfoDTO.builder()
                .id(id)
                .username(userBasic.getUsername())
                .number(result.getStudentNumber())
                .name(result.getName())
                .major(result.getMajor())
                .grade(result.getGrade())
                .clazz(result.getClazz())
                .build();
    }

    @Transactional
    public StudentInfo add(BasicStudentInfoDto dto) {
        Long id = YitIdHelper.nextId();

        StudentInfo studentInfo = new StudentInfo();
        studentInfo.setId(id);
        studentInfo.setStudentNumber(dto.studentNumber());
        studentInfo.setName(dto.studentName());
        studentInfo.setMajor(dto.majorId());
        studentInfo.setGrade(Integer.parseInt(dto.studentNumber().substring(0, 4)));
        studentInfo.setClazz("");
        studentInfo.setCreateTime(LocalDateTime.now());
        studentInfo.setEditTime(LocalDateTime.now());
        studentInfo.setDeleted(false);

        studentInfoMapper.insert(studentInfo);

        String username = dto.studentNumber();
        String password = dto.studentNumber(); // 默认密码与学号相同
        userBasicService.addUser(id, username, password, (short) UserType.STUDENT.code);

        return studentInfo;
    }
//
//    @Transactional
//    public StudentInfo addBatch(List<BasicStudentInfoDto> basicStudentInfoDtoList) {
//        String id = Long.toString(YitIdHelper.nextId());
//
//        StudentInfo studentInfo = new StudentInfo();
//        studentInfo.setId(id);
//        studentInfo.setStudentNumber(dto.studentNumber());
//        studentInfo.setName(dto.studentName());
//        studentInfo.setMajor(dto.major());
//        studentInfo.setGrade(Integer.parseInt(dto.studentNumber().substring(0, 4)));
//        studentInfo.setClazz("");
//        studentInfo.setCreateTime(LocalDateTime.now());
//        studentInfo.setEditTime(LocalDateTime.now());
//        studentInfo.setDeleted(false);
//
//        studentInfoMapper.insert(studentInfo);
//
//        String username = dto.studentNumber();
//        String password = "";
//        userBasicService.addUser(id, username, password, (short) UserType.STUDENT.code);
//
//        return studentInfo;
//    }

    @Transactional
    public StudentInfo addWithAutoMajor(BasicStudentInfoWithMajorNameDto dto) {
        var userBasic = userBasicService.getUserBasicByUsername(dto.studentNumber());
        if (userBasic != null) {
            return userInfoService.getAsStudent(userBasic.getId());
        }

        Long id = YitIdHelper.nextId();

        StudentInfo studentInfo = new StudentInfo();
        studentInfo.setId(id);
        studentInfo.setStudentNumber(dto.studentNumber());
        studentInfo.setName(dto.studentName());

        var major = majorService.addMajorIfNotExists(dto.majorName());
        studentInfo.setMajor(major.getId());

        studentInfo.setGrade(Integer.parseInt(dto.studentNumber().substring(0, 4)));
        studentInfo.setClazz("unknown");
        studentInfo.setCreateTime(LocalDateTime.now());
        studentInfo.setEditTime(LocalDateTime.now());
        studentInfo.setDeleted(false);

        studentInfoMapper.insert(studentInfo);

        String username = dto.studentNumber();
        String password = "";
        userBasicService.addUser(id, username, password, (short) UserType.STUDENT.code);

        return studentInfo;
    }

    @Transactional
    public void delete(Long id) {
        StudentInfo studentInfo = new StudentInfo();
        studentInfo.setDeleted(true);

        studentInfoMapper.update(studentInfo, new QueryWrapper<StudentInfo>()
                .eq("id", id)
        );

        userBasicService.deleteUser(id);
        userInfoService.invalidateCache(id);
    }

    @Transactional
    public StudentInfo update(Long id, StudentUserInfoDTO dto) {
        StudentInfo studentInfo = this.getById(id);
        studentInfo.setStudentNumber(dto.number());
        studentInfo.setName(dto.name());
        studentInfo.setMajor(dto.major());
        studentInfo.setGrade(dto.grade());
        studentInfo.setClazz(dto.clazz());
        studentInfo.setEditTime(LocalDateTime.now());

        studentInfoMapper.update(studentInfo, new QueryWrapper<StudentInfo>()
                .eq("id", id)
                .eq("deleted", false)
        );

        UserBasicInfoDTO userBasicInfoDTO = new UserBasicInfoDTO(id, dto.username(), dto.password(), (short) UserType.STUDENT.code);
        userBasicService.updateUser(id, userBasicInfoDTO);

        return userInfoService.getAsStudent(id, false);
    }

    @Transactional
    public NewStudentDTO tryAddStudentFromOfficial(String studentNumber, String password) throws IOException {
        String cacheToken = null;
        try {
            cacheToken = stringRedisTemplate.opsForValue().get(Constants.Redis.WusthelperTokenCacheKey.formatted(studentNumber));
        } catch (Exception e) {
            log.error("err reading redis cache token", e);
        }

        var token = cacheToken == null ? wusthelperApi.wusthelperLogin(studentNumber, password) : cacheToken;
        try {
            stringRedisTemplate.opsForValue().set(Constants.Redis.WusthelperTokenCacheKey.formatted(studentNumber), token);
        } catch (Exception e) {
            log.error("err writing redis cache token", e);
        }

        var originInfo = wusthelperApi.getStudentInfo(token);

        String name = originInfo.getStuName();
        String majorName = originInfo.getMajor();
        String clazz = originInfo.getClasses();

        var studentInfo = studentInfoMapper.selectOne(new QueryWrapper<StudentInfo>()
                .eq("student_number", studentNumber)
                .eq("deleted", false)
        );

        Long id;
        if (studentInfo == null) {
            id = YitIdHelper.nextId();
            studentInfo = StudentInfo.builder()
                    .id(id)
                    .studentNumber(studentNumber)
                    .name(name)
                    .grade(Integer.parseInt(studentNumber.substring(0, 4)))
                    .clazz(clazz)
                    .build();
        } else {
            id = studentInfo.getId();
        }

        var major = majorService.addMajorIfNotExists(majorName);
        studentInfo.setMajor(major.getId());
        studentInfoMapper.insertOrUpdate(studentInfo);

        var newUserBasic = userBasicService.addUser(id, studentNumber, password, (short) UserType.STUDENT.code);

        var newStudentInfo = userInfoService.getAsStudent(id, false);

        return new NewStudentDTO(newUserBasic, newStudentInfo);
    }
}
