package cn.teammodel.service.impl;

import cn.hutool.core.lang.Pair;
import cn.hutool.core.lang.UUID;
import cn.teammodel.common.ErrorCode;
import cn.teammodel.common.PK;
import cn.teammodel.config.exception.ServiceException;
import cn.teammodel.model.dto.admin.weekduty.AdminFindDutyRecordDto;
import cn.teammodel.model.dto.weekDuty.*;
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.weekDuty.WeekDuty;
import cn.teammodel.model.entity.weekDuty.WeekDutyRecord;
import cn.teammodel.model.vo.weekDuty.DutyRecordVo;
import cn.teammodel.repository.ClassRepository;
import cn.teammodel.repository.DutyRecordRepository;
import cn.teammodel.repository.DutyRepository;
import cn.teammodel.repository.SchoolRepository;
import cn.teammodel.security.utils.SecurityUtil;
import cn.teammodel.service.DutyService;
import cn.teammodel.utils.SchoolDateUtil;
import com.azure.cosmos.models.CosmosPatchOperations;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.Instant;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author winter
 * @create 2024-01-03 10:08
 */
@Service
public class DutyServiceImpl implements DutyService {
    @Resource
    private ClassRepository classRepository;
    @Resource
    private SchoolRepository schoolRepository;
    @Resource
    private DutyRecordRepository dutyRecordRepository;
    @Resource
    private DutyRepository dutyRepository;

    @Override
    public WeekDuty getTree() {
        User user = SecurityUtil.getLoginUser();
        String schoolId = user.getSchoolId();

        WeekDuty duty = dutyRepository.findBySchoolIdAndCode(schoolId , PK.WEEK_DUTY);
        if (duty == null) {
            // copy from template
            duty = dutyRepository.findBySchoolIdAndCode("template", PK.WEEK_DUTY);
            if (duty == null) {
                throw new RuntimeException("值周模板不存在");
            }
            duty.setId(null);
            duty.setSchoolId(schoolId);
            // 刷新值周树和 spots 的 id
            refreshDuty(duty);
            duty = dutyRepository.save(duty);
        }

        return this.buildTree(duty);
    }

    @Override
    public WeekDuty insertNode(InsertDutyNodeDto insertDutyNodeDto) {
        String pid = insertDutyNodeDto.getPid();
        String name = insertDutyNodeDto.getName();
        Integer order = insertDutyNodeDto.getOrder();
        Boolean positive = insertDutyNodeDto.getPositive();
        Integer score = positive ? insertDutyNodeDto.getScore() : -insertDutyNodeDto.getScore();
        String desc = insertDutyNodeDto.getDesc();

        User user = SecurityUtil.getLoginUser();
        String schoolId = user.getSchoolId();
        WeekDuty duty = dutyRepository.findBySchoolIdAndCode(schoolId, PK.WEEK_DUTY);
        if (duty == null) {
            throw new ServiceException("值周树不存在");
        }
        WeekDuty.DutyTreeNode newNode = new WeekDuty.DutyTreeNode();

        List<WeekDuty.DutyTreeNode> nodes = duty.getNodes();
        // 只有三级节点需要 positive
        newNode.setPositive(null);
        // 非根节点(需考虑填充 path)
        if (StringUtils.isNotEmpty(pid)) {
            WeekDuty.DutyTreeNode parent = nodes.stream().filter(x -> pid.equals(x.getId()))
                    .findFirst()
                    .orElseThrow(() -> new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "pid 无效"));
            String level2 = parent.getName();

            // 新节点是三级节点(二级节点不用特殊处理
            String ppid = parent.getPid();
            if (StringUtils.isNotEmpty(ppid)) {
                String[] path = new String[3];
                WeekDuty.DutyTreeNode root = nodes.stream().filter(x -> ppid.equals(x.getId()))
                        .findFirst()
                        .orElseThrow(() -> new ServiceException(ErrorCode.SYSTEM_ERROR.getCode(), "值周树结构错误"));
                // 层数不为三级,抛出异常
                if (StringUtils.isNotEmpty(root.getPid())) {
                    throw new ServiceException(ErrorCode.SYSTEM_ERROR.getCode(), "值周树结构错误");
                }
                String level1 = root.getName();
                path[0] = level1;
                path[1] = level2;
                path[2] = name;
                newNode.setDesc(desc);
                newNode.setPath(path);
                newNode.setScore(score);
                newNode.setPositive(positive);
            }
        }

        newNode.setId(UUID.randomUUID().toString());
        newNode.setPid(pid);
        newNode.setName(name);
        newNode.setOrder(order);
        newNode.setCreator(user.getName());
        newNode.setCreatorId(user.getId());
        newNode.setCreateTime(Instant.now().toEpochMilli());
        nodes.add(newNode);
        return buildTree(dutyRepository.save(duty));
    }

    @Override
    public WeekDuty deleteNode(DeleteDutyNodeDto deleteDutyNodeDto) {
        String schoolId = SecurityUtil.getLoginUser().getSchoolId();
        String nodeToDeleteId = deleteDutyNodeDto.getNodeId();

        WeekDuty duty = dutyRepository.findBySchoolIdAndCode(schoolId, PK.WEEK_DUTY);
        if (duty == null) {
            throw new ServiceException("值周树不存在");
        }
        List<WeekDuty.DutyTreeNode> nodes = duty.getNodes();
        List<WeekDuty.DutyTreeNode> nodesToDelete = new ArrayList<>();
        WeekDuty.DutyTreeNode node = nodes.stream()
                .filter(x -> nodeToDeleteId.equals(x.getId()))
                .findFirst()
                .orElseThrow(() -> new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "删除的节点不存在"));

        nodesToDelete.add(node);
        this.collectNodesToDelete(nodeToDeleteId, nodes, nodesToDelete);
        // 批量删除节点
        nodes.removeAll(nodesToDelete);

        return buildTree(dutyRepository.save(duty));
    }

    @Override
    public WeekDuty updateNode(UpdateDutyNodeDto updateDutyNodeDto) {
        String nodeId = updateDutyNodeDto.getId();
        String newNodeName = updateDutyNodeDto.getName();
        Integer newScore = updateDutyNodeDto.getScore();
        String desc = updateDutyNodeDto.getDesc();
        Boolean positive = updateDutyNodeDto.getPositive();

        String schoolId = SecurityUtil.getLoginUser().getSchoolId();
        WeekDuty duty = dutyRepository.findBySchoolIdAndCode(schoolId, PK.WEEK_DUTY);
        if (duty == null) {
            throw new ServiceException("值周树不存在");
        }
        List<WeekDuty.DutyTreeNode> nodes = duty.getNodes();
        WeekDuty.DutyTreeNode nodeToUpdate = nodes.stream().filter(x -> nodeId.equals(x.getId()))
                .findFirst()
                .orElseThrow(() -> new ServiceException("待更新的节点不存在"));
        String originNodeName = nodeToUpdate.getName();
        // 通用更新节点字段
        nodeToUpdate.setName(newNodeName);
        nodeToUpdate.setOrder(updateDutyNodeDto.getOrder());

        // 一级,二级需同步更新三级节点的 path
        if (nodeToUpdate.getPath() == null || nodeToUpdate.getPath().length == 0) {
            int index = StringUtils.isEmpty(nodeToUpdate.getPid()) ? 0 : 1;
            nodes.forEach(x -> {
                // 修改三级节点的 path
                if (x.getPath() == null) {
                    return;
                }
                String[] path = x.getPath();
                if (originNodeName.equals(path[index])) {
                    path[index] = newNodeName;
                    x.setPath(path);
                }
            });
        } else {
            // 三级节点
            nodeToUpdate.setScore(positive ? newScore : -newScore);
            nodeToUpdate.setDesc(desc);
            nodeToUpdate.setPositive(newScore.equals(0) || positive);
            nodeToUpdate.getPath()[2] = newNodeName;
        }

        return buildTree(dutyRepository.save(duty));
    }

    @Override
    public void vote(DutyVoteDto dutyVoteDto) {
        String classId = dutyVoteDto.getClassId();
        String spotId = dutyVoteDto.getSpotId();
        String dutyNodeId = dutyVoteDto.getDutyNodeId();
        String note = dutyVoteDto.getNote();
        boolean spread = dutyVoteDto.getSpread();
        List<String> attachments = dutyVoteDto.getAttachments();
        User user = SecurityUtil.getLoginUser();
        String schoolId = user.getSchoolId();

        ClassInfo classInfo = classRepository.findClassByIdAndCode(classId, String.format(PK.CLASS, schoolId));
        if (classInfo == null) {
            throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "目标班级不存在");
        }
        String className = classInfo.getName();
        String periodId = classInfo.getPeriodId();
        List<School.Semester> semesters = schoolRepository.findSemestersById(schoolId, periodId);
        if (ObjectUtils.isEmpty(semesters)) {
            throw new ServiceException(ErrorCode.SYSTEM_ERROR.getCode(), "内部结构异常");
        }
        String academicYearId = SchoolDateUtil.calculateAcademicYearId(semesters, LocalDate.now());
        WeekDuty duty = dutyRepository.findBySchoolIdAndCode(schoolId, PK.WEEK_DUTY);
        WeekDuty.DutyTreeNode dutyNode = duty.getNodes().stream()
                .filter(x -> dutyNodeId.equals(x.getId()))
                .findFirst()
                .orElseThrow(() -> new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "评价节点不存在"));

        if (dutyNode.getPath() == null || dutyNode.getPath().length == 0) {
            throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "只能评价三级节点");
        }
        // dutynode 剪枝
        dutyNode.setCreator(null);
        dutyNode.setCreatorId(null);
        dutyNode.setCreateTime(null);

//        WeekDuty.DutySpot spot = duty.getSpots().stream()
//                .filter(x -> spotId.equals(x.getId()))
//                .findFirst()
//                .orElseThrow(() -> new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "值周评价地点不存在"));
        WeekDutyRecord weekDutyRecord = dutyRecordRepository.findByClassIdAndAcademicYearIdAndCode(
                classId,
                academicYearId,
                String.format(PK.WEEK_DUTY_RECORD, schoolId)
        );

        WeekDutyRecord.WeekDutyItem newItem = new WeekDutyRecord.WeekDutyItem();
        newItem.setId(UUID.randomUUID().toString());
        newItem.setDutyTreeNode(dutyNode);
//        newItem.setSpotId(spot.getId());
//        newItem.setSpotName(spot.getName());
        newItem.setSpread(spread);
        newItem.setAttachments(attachments);
        newItem.setNote(note);
        newItem.setCreateTime(Instant.now().toEpochMilli());
        newItem.setCreator(user.getName());
        newItem.setCreatorId(user.getId());
        // 不存在则创建
        if (weekDutyRecord == null) {
            List<WeekDutyRecord.WeekDutyItem> nodes = Collections.singletonList(newItem);
            weekDutyRecord = new WeekDutyRecord();
            weekDutyRecord.setClassId(classId);
            weekDutyRecord.setClassName(className);
            weekDutyRecord.setAcademicYearId(academicYearId);
            weekDutyRecord.setScore(dutyNode.getScore());
            weekDutyRecord.setNodes(nodes);
            weekDutyRecord.setCode(String.format(PK.WEEK_DUTY_RECORD, schoolId));
            weekDutyRecord.setCreateTime(Instant.now().toEpochMilli());
            dutyRecordRepository.save(weekDutyRecord);
        } else {
            CosmosPatchOperations operations = CosmosPatchOperations.create();
            operations.add("/nodes/-", newItem);
            operations.increment("/score", dutyNode.getScore());
            dutyRecordRepository.save(
                    weekDutyRecord.getId(),
                    PK.buildOf(PK.WEEK_DUTY_RECORD, schoolId),
                    WeekDutyRecord.class,
                    operations
            );
        }
    }

    @Override
    public List<WeekDuty.DutySpot> insertSpot(InsertSpotDto insertSpotDto) {
        String schoolId = SecurityUtil.getLoginUser().getSchoolId();
        WeekDuty duty = dutyRepository.findBySchoolIdAndCode(schoolId, PK.WEEK_DUTY);
        if (duty == null) {
            throw new ServiceException("值周树不存在");
        }
        String name = insertSpotDto.getName();
        CosmosPatchOperations operations = CosmosPatchOperations.create().add("/spots/-", new WeekDuty.DutySpot(UUID.randomUUID().toString(), name));
        duty = dutyRepository.save(
                duty.getId(),
                PK.of(PK.WEEK_DUTY),
                WeekDuty.class,
                operations
        );
        return duty.getSpots();
    }

    @Override
    public List<WeekDuty.DutySpot> deleteSpot(String id) {
        String schoolId = SecurityUtil.getLoginUser().getSchoolId();
        WeekDuty duty = dutyRepository.findBySchoolIdAndCode(schoolId, PK.WEEK_DUTY);
        if (duty == null) {
            throw new ServiceException("值周树不存在");
        }
        List<WeekDuty.DutySpot> spots = duty.getSpots();
        WeekDuty.DutySpot spot = spots.stream().filter(x -> id.equals(x.getId())).findFirst().orElseThrow(() -> new ServiceException("值周评价地点不存在"));
        spots.remove(spot);
        duty = dutyRepository.save(duty);
        return duty.getSpots();
    }

    @Override
    public List<DutyRecordVo> findRecords(FindDutyRecordDto findDutyRecordDto) {
        String classId = StringUtils.isEmpty(findDutyRecordDto.getClassId()) ? null : findDutyRecordDto.getClassId();
        String periodId = findDutyRecordDto.getPeriodId();
        Boolean positive = findDutyRecordDto.getPositive();
        Long startTime = findDutyRecordDto.getStartTime();
        Long endTime = findDutyRecordDto.getEndTime();
        User user = SecurityUtil.getLoginUser();
        String schoolId = user.getSchoolId();
        String userId = user.getId();

        List<School.Semester> semesters = schoolRepository.findSemestersById(schoolId, periodId);
        String academicYearId = SchoolDateUtil.calculateAcademicYearId(semesters, LocalDate.now());

        Pair<Long, Long> pair = SchoolDateUtil.adjustTimeRangeToCurrentWeekIfNull(startTime, endTime);
        startTime = pair.getKey();
        endTime = pair.getValue();
        List<DutyRecordVo> records = dutyRecordRepository.findRecordsByConditions(
                String.format(PK.WEEK_DUTY_RECORD, schoolId),
                academicYearId,
                userId,
                classId,
                positive,
                startTime,
                endTime);
        records = records.stream().sorted((o1, o2) -> o2.getInfo().getCreateTime().compareTo(o1.getInfo().getCreateTime())).collect(Collectors.toList());
        return records;
    }

    @Override
    public void recallVote(RecallDutyVoteDto recallDutyVoteDto) {
        String recordId = recallDutyVoteDto.getRecordId();
        String nodeId = recallDutyVoteDto.getNodeId();
        User user = SecurityUtil.getLoginUser();
        String schoolId = user.getSchoolId();
        String userId = user.getId();

        Optional<WeekDutyRecord> optional = dutyRecordRepository.findById(recordId, PK.of(String.format(PK.WEEK_DUTY_RECORD, schoolId)));
        WeekDutyRecord weekDutyRecord = optional.orElseThrow(() -> new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "评价记录不存在"));
        WeekDutyRecord.WeekDutyItem item = weekDutyRecord.getNodes().stream()
                .filter(x -> nodeId.equals(x.getId()))
                .findFirst().
                orElseThrow(() -> new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "评价记录不存在"));
        if (!userId.equals(item.getCreatorId())) {
            throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "只能撤回自己的评价");
        }
        weekDutyRecord.setScore(weekDutyRecord.getScore() - item.getDutyTreeNode().getScore());
        weekDutyRecord.getNodes().remove(item);
        dutyRecordRepository.save(weekDutyRecord);
    }

    @Override
    public List<DutyRecordVo> findAdminRecords(AdminFindDutyRecordDto adminFindDutyRecordDto) {
        String teacherId = adminFindDutyRecordDto.getTeacherId();
        String classId = adminFindDutyRecordDto.getClassId();
        String academicYearId = adminFindDutyRecordDto.getAcademicYearId();
        Boolean positive = adminFindDutyRecordDto.getPositive();
        Long startTime = adminFindDutyRecordDto.getStartTime();
        Long endTime = adminFindDutyRecordDto.getEndTime();
        User user = SecurityUtil.getLoginUser();
        String schoolId = user.getSchoolId();

        Pair<Long, Long> pair = SchoolDateUtil.adjustTimeRangeToCurrentWeekIfNull(startTime, endTime);
        startTime = pair.getKey();
        endTime = pair.getValue();
        List<DutyRecordVo> records = dutyRecordRepository.findRecordsByConditions(
                String.format(PK.WEEK_DUTY_RECORD, schoolId),
                academicYearId,
                teacherId,
                classId,
                positive,
                startTime,
                endTime);
        records = records.stream().sorted((o1, o2) -> o2.getInfo().getCreateTime().compareTo(o1.getInfo().getCreateTime())).collect(Collectors.toList());
        return records;
    }

    /**
     * 刷新值周树和 spots 的 id
     */
    private void refreshDuty(WeekDuty duty) {
        List<WeekDuty.DutyTreeNode> nodes = duty.getNodes();
        List<WeekDuty.DutySpot> spots = duty.getSpots();
        // refresh tree nodes
        nodes.forEach(x -> {
            String oldId = x.getId();
            String newId = UUID.randomUUID().toString();
            x.setId(newId);
            // 将孩子节点的 pid 改为新的 id
            nodes.stream().filter(y -> oldId.equals(y.getPid())).forEach(y -> y.setPid(newId));
        });

        // refresh spot id
        spots.forEach(x -> x.setId(UUID.randomUUID().toString()));
    }

    /**
     * 递归收集要删除的节点
     */
    private void collectNodesToDelete(String nodeToDeleteId, List<WeekDuty.DutyTreeNode> nodes, List<WeekDuty.DutyTreeNode> nodesToDelete) {
        List<WeekDuty.DutyTreeNode> children = nodes.stream()
                .filter(x -> nodeToDeleteId.equals(x.getPid()))
                .collect(Collectors.toList());
        if (children.size() == 0) return;
        nodesToDelete.addAll(children);
        for (WeekDuty.DutyTreeNode child : children) {
            this.collectNodesToDelete(child.getId(), nodes, nodesToDelete);
        }
    }

    public WeekDuty buildTree(WeekDuty weekDuty) {
        if (weekDuty == null) {
            return null;
        }

        List<WeekDuty.DutyTreeNode> nodes = weekDuty.getNodes();
        List<WeekDuty.DutyTreeNode> parents = new ArrayList<>();

        // pid 为 null 或者 "" 则为 parents
        for (WeekDuty.DutyTreeNode node : nodes) {
            if (StringUtils.isBlank(node.getPid())) {
                parents.add(node);
            }
        }
        // 迭代构建孩子节点
        for (WeekDuty.DutyTreeNode parent : parents) {
            buildChildren(parent, nodes);
        }
        // 根据 order 排序
        parents = parents.stream().sorted(Comparator.comparing(WeekDuty.DutyTreeNode::getOrder)).collect(Collectors.toList());
        weekDuty.setNodes(parents);
        return weekDuty;
    }

    private void buildChildren(WeekDuty.DutyTreeNode parent, List<WeekDuty.DutyTreeNode> nodes) {
        List<WeekDuty.DutyTreeNode> children = getChildren(parent, nodes);
        if (children.size() == 0) return;

        parent.setChildren(children);
        for (WeekDuty.DutyTreeNode child : children) {
            // 如果子节点还有孩子的话,就继续递归构建
            if (getChildren(child, nodes).size() > 0) {
                buildChildren(child, nodes);
            }
        }

    }

    private List<WeekDuty.DutyTreeNode> getChildren(WeekDuty.DutyTreeNode parent, List<WeekDuty.DutyTreeNode> nodes) {
        List<WeekDuty.DutyTreeNode> children = new ArrayList<>();
        for (WeekDuty.DutyTreeNode node : nodes) {
            if (StringUtils.isNotBlank(node.getPid()) && node.getPid().equals(parent.getId())) {
                children.add(node);
            }
        }
        return children;
    }
}
