package cn.teammodel.controller.admin.service.impl;

import cn.teammodel.common.ErrorCode;
import cn.teammodel.common.PK;
import cn.teammodel.config.exception.ServiceException;
import cn.teammodel.controller.admin.service.AdminIndexDutyService;
import cn.teammodel.model.dto.admin.appraise.TimeRangeDto;
import cn.teammodel.model.dto.weekDuty.LessonRecordDto;
import cn.teammodel.model.entity.User;
import cn.teammodel.model.entity.school.ClassInfo;
import cn.teammodel.model.entity.school.School;
import cn.teammodel.model.entity.school.Teacher;
import cn.teammodel.model.entity.weekDuty.WeekDuty;
import cn.teammodel.model.vo.admin.DutyIndexData;
import cn.teammodel.model.vo.admin.DutyIndexPo;
import cn.teammodel.model.vo.admin.DutyNodeRankVo;
import cn.teammodel.model.vo.admin.DutyRankPo;
import cn.teammodel.repository.ClassRepository;
import cn.teammodel.repository.DutyRecordRepository;
import cn.teammodel.repository.SchoolRepository;
import cn.teammodel.repository.TeacherRepository;
import cn.teammodel.security.utils.SecurityUtil;
import cn.teammodel.utils.GroupUtil;
import cn.teammodel.utils.SchoolDateUtil;
import com.azure.spring.data.cosmos.core.query.CosmosPageRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.data.domain.Slice;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

import static cn.teammodel.utils.SchoolDateUtil.calculateWeekNum;

/**
 * @author winter
 * &#064;create  2024-02-28 15:07
 */
@Service
@Slf4j
public class AdminIndexDutyServiceImpl implements AdminIndexDutyService {

    @Resource
    private SchoolRepository schoolRepository;

    @Resource
    private TeacherRepository teacherRepository;

    @Resource
    private ClassRepository classRepository;
    @Resource
    private DutyRecordRepository dutyRecordRepository;
    @Autowired
    private Environment env;

   /* @Autowired
    public AdminIndexDutyServiceImpl(Environment env) {
        this.env = env;
    }*/

    @Override
    public DutyIndexData getIndexData(TimeRangeDto timeRangeDto) {
        User loginUser = SecurityUtil.getLoginUser();
        String schoolId = loginUser.getSchoolId();

        final int SLICE_SIZE = 100;
        if (StringUtils.isBlank(timeRangeDto.getPeriodId())) {
            throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "不能为空");
        }
        final DutyIndexData DutyIndexData = new DutyIndexData();
        int totalCount = 0;
        int negetiveCount = 0;
        Set<String> creatorIdSet = new HashSet<>();
        // todo
        Set<String> classIdSet = new HashSet<>();

        // 获取学期起止时间
        List<School.Semester> semesters = schoolRepository.findSemestersById(schoolId, timeRangeDto.getPeriodId());
        SchoolDateUtil.semesterModel semesterModel = SchoolDateUtil.getSemesterByNow(semesters, LocalDate.now());
        LocalDateTime startDatetime = null;
        LocalDateTime endDatetime = null;
        String academicYearId = null;
        if(timeRangeDto.getStartTime() != null) {
            startDatetime = LocalDateTime.ofInstant(Instant.ofEpochMilli(timeRangeDto.getStartTime()), ZoneId.systemDefault());
            endDatetime = LocalDateTime.ofInstant(Instant.ofEpochMilli(timeRangeDto.getEndTime()), ZoneId.systemDefault());
            academicYearId = timeRangeDto.getAcademicYearId();
        }else {
            academicYearId = semesterModel.getAcademicYearId();
            startDatetime = semesterModel.getStartDatetime();
            endDatetime = semesterModel.getEndDatetime();

        }
        if (startDatetime == null || endDatetime == null) throw new ServiceException(ErrorCode.PARAMS_ERROR);
        long totalWeek = calculateWeekNum(startDatetime, endDatetime, null);

        // slice 分段读取
        CosmosPageRequest pageRequest = new CosmosPageRequest(0, SLICE_SIZE, null);
        Slice<DutyIndexPo> slice;
        Map<Long, Integer> countByWeek = SchoolDateUtil.createEmptyWeekMap(totalWeek);

        do {
            slice = dutyRecordRepository.findAllByAcademicYearId(String.format(PK.WEEK_DUTY_RECORD, schoolId), academicYearId, pageRequest);
            List<DutyIndexPo> content = slice.getContent();
            if (ObjectUtils.isEmpty(content)) {
                break;
            }

            // 分批次计算
            for (DutyIndexPo item : content) {
                // 处理每周的评价数
                long weekNum = calculateWeekNum(startDatetime, endDatetime, item.getCreateTime());
                countByWeek.put(weekNum, countByWeek.getOrDefault(weekNum, 0) + 1);
                // 处理总评价数
                totalCount++;
                // 处理批评数
                if (item.getDutyTreeNodeScore() < 0) {
                    negetiveCount++;
                }
                // 处理已评价老师总数
                creatorIdSet.add(item.getCreatorId());
                // 处理被评价的学生数
                 classIdSet.add(item.getClassId());
            }

            if (slice.hasNext()) {
                pageRequest = (CosmosPageRequest) slice.nextPageable();
            }
        } while (slice.hasNext());

        // 组装数据
        DutyIndexData.setCountByWeek(countByWeek);
        DutyIndexData.setTotalCount(totalCount);
        DutyIndexData.setClassCount(classIdSet.size());
        DutyIndexData.setPositiveCount(totalCount - negetiveCount);
        DutyIndexData.setTeacherCount(creatorIdSet.size());
        return DutyIndexData;
    }

    @Override
    public List<DutyRankPo> classRank(TimeRangeDto timeRangeDto) {
        Long startTime = timeRangeDto.getStartTime();
        Long endTime = timeRangeDto.getEndTime();
        String academicYearId = timeRangeDto.getAcademicYearId();
        String schoolId = SecurityUtil.getLoginUser().getSchoolId();
        List<DutyRankPo> res = dutyRecordRepository.classRank(String.format(PK.WEEK_DUTY_RECORD, schoolId), academicYearId, startTime, endTime);
        if (ObjectUtils.isEmpty(res)) return null;
        List<String> classIds = res.stream().map(DutyRankPo::getId).collect(Collectors.toList());
        if (ObjectUtils.isEmpty(classIds)) {
            return null;
        }
        List<ClassInfo> classes = classRepository.findAllByCodeAndIdIn(String.format(PK.CLASS, schoolId), classIds);
        Map<String, String> idNameMap = classes.stream().collect(Collectors.toMap(ClassInfo::getId, ClassInfo::getName));
        // reversed sort by score
        res = res.stream().peek(s -> s.setName(idNameMap.get(s.getId()))).sorted((a, b) -> b.getScore() - a.getScore()).collect(Collectors.toList());
        return res;
    }

    @Override
    public List<DutyRankPo> teacherRank(TimeRangeDto timeRangeDto) {
        Long startTime = timeRangeDto.getStartTime();
        Long endTime = timeRangeDto.getEndTime();
        String academicYearId = timeRangeDto.getAcademicYearId();
        String schoolId = SecurityUtil.getLoginUser().getSchoolId();
        List<DutyRankPo> res = dutyRecordRepository.teacherRank(String.format(PK.WEEK_DUTY_RECORD, schoolId), academicYearId, startTime, endTime);
        // set teacher name
        if (ObjectUtils.isEmpty(res)) return null;
        Set<String> teacherIdSet = res.stream().map(DutyRankPo::getId).collect(Collectors.toSet());
        if (ObjectUtils.isEmpty(teacherIdSet)) return res;
        List<Teacher> teachers = teacherRepository.findAllByCodeAndIdIn(PK.COMMON_BASE, teacherIdSet);
        Map<String, String> idNameMap = teachers.stream().collect(Collectors.toMap(Teacher::getId, Teacher::getName));
        res = res.stream().peek(s -> s.setName(idNameMap.get(s.getId()))).sorted((a, b) -> b.getScore() - a.getScore()).collect(Collectors.toList());
        return res;
    }

    @Override
    public List<DutyNodeRankVo> appraiseNodeRank(TimeRangeDto timeRangeDto) {
        Long startTime = timeRangeDto.getStartTime();
        Long endTime = timeRangeDto.getEndTime();
        String academicYearId = timeRangeDto.getAcademicYearId();
        String schoolId = SecurityUtil.getLoginUser().getSchoolId();

        List<DutyNodeRankVo> res = dutyRecordRepository.dutyNodeRank(String.format(PK.WEEK_DUTY_RECORD, schoolId), academicYearId, startTime, endTime);
        if (ObjectUtils.isEmpty(res)) return null;
        List<String> names = res.stream().map(DutyNodeRankVo::getName).collect(Collectors.toList());
        if (ObjectUtils.isEmpty(names)) return null;
        // 去重后的 nodes
        List<WeekDuty.DutyTreeNode> nodesByName = dutyRecordRepository.findDutyRecordInNames(String.format(PK.WEEK_DUTY_RECORD, schoolId), academicYearId, names);
        // 正常情况下 name 一一对应 // TODO: 2024/2/29
        Map<String, WeekDuty.DutyTreeNode> nameNodeMap = nodesByName.stream()
                .collect(Collectors.toMap(WeekDuty.DutyTreeNode::getName, item -> item, (existing, replacement) -> {
                    if (replacement.getPath() != null) {
                        return replacement;
                    }
                    return existing;
                }));

        res = res.stream()
                .sorted(Comparator.comparing(DutyNodeRankVo::getCount).reversed())
                // 流中对元素操作但不改变流
                .peek(s -> {
                    WeekDuty.DutyTreeNode node = nameNodeMap.get(s.getName());
                    if (node != null) {
                        s.setPath(node.getPath());
                        s.setScore(node.getScore());
                    }
                })
                .collect(Collectors.toList());
        return res;


    }

    @Override
    public Map<String, Object> getLessonRecord(LessonRecordDto lessonRecordDto, HttpServletRequest request) {

        Map<String, Object> mapper;
        String url = env.getProperty("ies.server-url");
        if(lessonRecordDto.isClassMeeting()) {
            List<School.Subject> subjects = schoolRepository.findSubjectById(lessonRecordDto.getSchool(), lessonRecordDto.getPeriodId());
            List<String> subjectIds = subjects.stream().filter(subject -> "班会".equals(subject.getName()))
                    .map(School.Subject::getId).collect(Collectors.toList());
            lessonRecordDto.setSubjectId(subjectIds);
        }
        mapper =  GroupUtil.getGroupId(lessonRecordDto,new GroupUtil(env), request,url);
        return mapper;
    }
}
