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.admin.common.GroupDto;
import cn.teammodel.model.dto.admin.common.RGroupList;
import cn.teammodel.model.dto.admin.common.RMember;
import cn.teammodel.model.dto.admin.teacher.TeacherDto;
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.LessonRecord;
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.*;
import cn.teammodel.security.utils.SecurityUtil;
import cn.teammodel.test.LessonRecordQueryService;
import cn.teammodel.utils.GroupUtil;
import cn.teammodel.utils.SchoolDateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
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.concurrent.ExecutionException;
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;
    @Resource
    private LessonRecordRepository lessonRecordRepository;
    @Resource
    private StudentRepository studentRepository;

   /* @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;
        LocalDateTime endDatetime;
        String academicYearId;
        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");
        // 修复：使用 setOk 方法来设置 ok 属性
        //lessonRecordDto.setOk(true);
       /* 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);

            if(subjectIds.isEmpty()) {
                return new HashMap<>();
            }
        }*/
        mapper =  GroupUtil.getGroupId(lessonRecordDto,new GroupUtil(env), request,url);
        return mapper;
    }

    @Override
    public List<Map<String, Object>> getTeachingQuality(TeacherDto teacherDto, HttpServletRequest request) {
        List<LessonRecord> records;
        LessonRecordQueryService queryService = new LessonRecordQueryService(lessonRecordRepository);
        LocalDateTime now = LocalDateTime.now();
        List<Map<String, Object>> list = new ArrayList<>();
        //Map<String, Object> mapAll = new HashMap<>();
        try {
            String lessonRecordKey = String.format(PK.PK_LESSON_RECORD, teacherDto.getCode());
            Long startTime = teacherDto.getStartTime();
            Long endTime = teacherDto.getEndTime();
            String subjectId = teacherDto.getSubjectId();
            String tmdId = teacherDto.getTmdId();
            String grade = teacherDto.getGrade();
            String periodId = teacherDto.getPeriodId();

            records = queryService.queryLessonsInParallel(
                    lessonRecordKey, startTime, endTime, subjectId, tmdId, grade, periodId
            );

            Set<String> classIds = records.stream().filter(lessonRecord -> lessonRecord.getTmdid().equalsIgnoreCase(teacherDto.getTmdId()))
                    .flatMap(record -> record.getGroupIds().stream())
                    .collect(Collectors.toSet());


            GroupDto groupDto = new GroupDto();
            groupDto.setIds(new ArrayList<>(classIds));
            groupDto.setSchoolId(teacherDto.getCode());
            String url = env.getProperty("ies.server-url-group");
            //List<ClassInfo> classes = classRepository.findAllByCodeAndIdIn("Class-"+artFindDto.getCode(),classIds);
            Map<String, Object> groupId =  GroupUtil.getGroupId(groupDto,new GroupUtil(env), request,url);
            List<RGroupList> rGroupList = new ArrayList<>();
            List<RMember> rMembers = new ArrayList<>();
            for (Map.Entry<String, Object> entry : groupId.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();
                if (key.equals("groups")) {
                    String jsonGroups = JSON.toJSONString(value);
                    rGroupList = JSON.parseObject(jsonGroups, new TypeReference<List<RGroupList>>() {});
                }
                if (key.equals("members")) {
                    String jsonGroups = JSON.toJSONString(value);
                    rMembers = JSON.parseObject(jsonGroups, new TypeReference<List<RMember>>() {});
                }
            }
            for(String classId : classIds) {
                Map<String, Object> mapper = new HashMap<>();

                // 班级内学生总数
                //int stuInClassCount = studentRepository.countByClassIdAndCode(classId, String.format(PK.STUDENT, teacherDto.getCode()));
                int stuInClassCount = 0;
                String className = "";
                for (RGroupList rGroup : rGroupList) {
                    if (rGroup.getId().equals(classId)) {
                        List<RMember> members  = rGroup.getMembers();
                        stuInClassCount =  members.size();
                        className = rGroup.getName();
                    }
                }
                mapper.put("stuInClassCount", stuInClassCount);
                mapper.put("className", className);
                // 计算本学期互动总数
                int totalClientInteractionCount = records.stream().filter(lessonRecord ->
                                lessonRecord.getGroupIds().contains(classId))
                        .mapToInt(LessonRecord::getClientInteractionCount)
                        .sum();
                mapper.put("totalClientInteractionCount", totalClientInteractionCount);
                int countRecord = (int) records.stream().filter(lessonRecord ->
                        lessonRecord.getGroupIds().contains(classId)).count();
                int engagementIndexAvergeSum = records.stream().filter(lessonRecord ->
                                lessonRecord.getGroupIds().contains(classId))
                        .mapToInt(LessonRecord::getEngagementIndexAverge)
                        .sum();
                if (countRecord == 0) {
                    mapper.put("engagementIndexAverge", 0);
                    continue;
                }else if (engagementIndexAvergeSum == 0) {
                    mapper.put("engagementIndexAverge", 0);
                }else {
                    mapper.put("engagementIndexAverge", engagementIndexAvergeSum / countRecord);
                }
                // 获取当前月和上个月的时间范围
                LocalDateTime startOfCurrentMonth = now.withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0);
                LocalDateTime startOfLastMonth = startOfCurrentMonth.minusMonths(1);
                LocalDateTime endOfLastMonth = startOfCurrentMonth.minusSeconds(1);
                long st = startOfCurrentMonth.atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli();
                long et = startOfLastMonth.atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli();
                long endMath = endOfLastMonth.atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli();

                // 过滤出当前月和上个月的记录
                List<LessonRecord> currentMonthRecords = records.stream()
                        .filter(record -> record.getStartTime() > st && record.getGroupIds().contains(classId))
                        .collect(Collectors.toList());

                List<LessonRecord> lastMonthRecords = records.stream()
                        .filter(record -> record.getStartTime() > et && record.getStartTime() < endMath && record.getGroupIds().contains(classId))
                        .collect(Collectors.toList());

                // 计算当前月和上个月的clientInteractionCount总数
                int currentMonthTotal = currentMonthRecords.stream()
                        .mapToInt(LessonRecord::getClientInteractionCount)
                        .sum();
                mapper.put("currentMonthTotal", currentMonthTotal);
                int lastMonthTotal = lastMonthRecords.stream()
                        .mapToInt(LessonRecord::getClientInteractionCount)
                        .sum();
                mapper.put("lastMonthTotal", lastMonthTotal);
                // 计算百分比变化
                double percentageChange = 0;
                if (lastMonthTotal != 0) {
                    percentageChange = ((double) (currentMonthTotal - lastMonthTotal) / lastMonthTotal) * 100;
                } else {
                    if (currentMonthTotal > 0) {
                        percentageChange = 100;
                    } else {
                        percentageChange = 0;
                    }

                }
                String formattedPercentageChange = String.format("%.2f", percentageChange);
                mapper.put("percentageChange", formattedPercentageChange);
                mapper.put("classId", classId);
                list.add(mapper);
            }

        //System.out.println("当前月上浮：" + percentageChange);
        } catch (InterruptedException | ExecutionException e) {
            throw new ServiceException(ErrorCode.SYSTEM_ERROR.getCode(), "数据查询异常");
        } finally {
            queryService.shutdown();
        }
        return list;
    }
}
