package com.yingpaipay.business.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yingpaipay.business.constant.DocumentTypeConst;
import com.yingpaipay.business.constant.FolderTypeConst;
import com.yingpaipay.business.domain.Folder;
import com.yingpaipay.business.domain.bo.FolderBo;
import com.yingpaipay.business.domain.bo.ProjectCenterListBo;
import com.yingpaipay.business.domain.bo.ProjectCenterMemberListBo;
import com.yingpaipay.business.domain.vo.FolderListVo;
import com.yingpaipay.business.domain.vo.FolderVo;
import com.yingpaipay.business.domain.vo.ProjectCenterListVo;
import com.yingpaipay.business.domain.vo.ProjectCenterMemberListVo;
import com.yingpaipay.system.domain.SysUserFolders;
import com.yingpaipay.system.mapper.SysUserFoldersMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.BusinessException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.helper.DataBaseHelper;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysDeptService;
import org.dromara.system.service.ISysUserService;
import org.springframework.stereotype.Service;
import com.yingpaipay.business.mapper.FolderMapper;
import com.yingpaipay.business.service.IFolderService;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 文件夹管理Service业务层处理
 *
 * @author Huanyi
 * @date 2025-12-08
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class FolderServiceImpl implements IFolderService {

    private final FolderMapper baseMapper;
    private final SysUserFoldersMapper userFoldersMapper;

    private final ISysUserService userService;
    private final ISysDeptService deptService;

    @Override
    public List<FolderListVo> selectList(Long projectId) {
        List<Folder> allFolders = baseMapper.selectList(
            Wrappers.lambdaQuery(Folder.class).eq(Folder::getProjectId, projectId)
        );

        if (allFolders.isEmpty()) {
            return Collections.emptyList();
        }

        SysUserFolders userFolderRecord = userFoldersMapper.selectOne(
            Wrappers.lambdaQuery(SysUserFolders.class)
                .eq(SysUserFolders::getProjectId, projectId)
                .eq(SysUserFolders::getUserId, LoginHelper.getUserId())
        );

        List<Folder> retainedFolders;

        if (userFolderRecord != null && "*".equals(userFolderRecord.getFolders())) {
            retainedFolders = allFolders;
        } else {
            if (userFolderRecord == null || StringUtils.isBlank(userFolderRecord.getFolders())) {
                return Collections.emptyList();
            }

            Set<Long> directPermissionIds = Arrays.stream(userFolderRecord.getFolders().split(","))
                .map(String::trim)
                .filter(StringUtils::isNotBlank)
                .map(Long::valueOf)
                .collect(Collectors.toSet());

            Map<Long, Folder> folderMap = allFolders.stream()
                .collect(Collectors.toMap(Folder::getId, f -> f));

            Map<Long, List<Folder>> fullChildrenMap = allFolders.stream()
                .filter(f -> f.getParentId() != null)
                .collect(Collectors.groupingBy(Folder::getParentId));

            Set<Long> retainedIds = new HashSet<>();

            for (Long seedId : directPermissionIds) {
                Long current = seedId;
                while (current != null) {
                    if (!folderMap.containsKey(current)) break;
                    retainedIds.add(current);
                    current = folderMap.get(current).getParentId();
                }

                collectAllDescendants(seedId, fullChildrenMap, retainedIds);
            }

            retainedFolders = allFolders.stream()
                .filter(f -> retainedIds.contains(f.getId()))
                .collect(Collectors.toList());
        }

        Map<Long, List<Folder>> childrenMap = retainedFolders.stream()
            .filter(f -> f.getParentId() != null)
            .collect(Collectors.groupingBy(Folder::getParentId));

        List<Folder> roots = retainedFolders.stream()
            .filter(f -> f.getParentId() == null)
            .toList();

        return roots.stream()
            .map(root -> buildVoTree(root, childrenMap))
            .collect(Collectors.toList());
    }

    private void collectAllDescendants(Long parentId, Map<Long, List<Folder>> childrenMap, Set<Long> result) {
        List<Folder> children = childrenMap.get(parentId);
        if (children != null) {
            for (Folder child : children) {
                result.add(child.getId());
                collectAllDescendants(child.getId(), childrenMap, result);
            }
        }
    }

    private FolderListVo buildVoTree(Folder folder, Map<Long, List<Folder>> childrenMap) {
        FolderListVo vo = new FolderListVo();
        vo.setId(folder.getId());
        vo.setName(folder.getName());
        vo.setType(folder.getType());

        List<Folder> children = childrenMap.get(folder.getId());
        if (children != null && !children.isEmpty()) {
            vo.setChildren(children.stream()
                .map(child -> buildVoTree(child, childrenMap))
                .collect(Collectors.toList()));
        } else {
            vo.setChildren(Collections.emptyList());
        }
        return vo;
    }

    @Override
    public List<FolderListVo> selectAllList(Long projectId) {
        List<Folder> folders = baseMapper.selectList(Wrappers.lambdaQuery(Folder.class).eq(Folder::getProjectId, projectId));
        Map<Long, List<Folder>> childrenMap = folders.stream()
            .filter(this::isNotRoot)
            .collect(Collectors.groupingBy(Folder::getParentId));
        List<Folder> roots = folders.stream().filter(this::isRoot).toList();
        List<FolderListVo> vos = new ArrayList<>();
        roots.forEach(e -> {
            FolderListVo vo = new FolderListVo();
            vo.setId(e.getId());
            vo.setName(e.getName());
            vo.setType(e.getType());
            vo.setChildren(buildChildren(e, childrenMap));
            vos.add(vo);
        });
        return vos;
    }

    private List<FolderListVo> buildChildren(Folder folder, Map<Long, List<Folder>> childrenMap) {
        List<Folder> children = childrenMap.get(folder.getId());
        if (children == null || children.isEmpty()) {
            return Collections.emptyList();
        }

        return children.stream().map(childFolder -> {
            FolderListVo vo = new FolderListVo();
            vo.setId(childFolder.getId());
            vo.setName(childFolder.getName());
            vo.setType(childFolder.getType());
            vo.setChildren(buildChildren(childFolder, childrenMap));
            return vo;
        }).toList();
    }

    private boolean isRoot(Folder folder) {
        return folder.getParentId() == null;
    }

    private boolean isNotRoot(Folder folder) {
        return !isRoot(folder);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertByBo(FolderBo bo) {
        Folder entity = MapstructUtils.convert(bo, Folder.class);
        boolean flag = baseMapper.insert(entity) == 0;
        if (flag) {
            throw new RuntimeException("插入失败");
        }
        checkInsertIsValid(bo);
        return true;
    }

    private void checkInsertIsValid(FolderBo bo) {
        if (bo.getParentId() == null) {
            return;
        }
        Folder parent = baseMapper.selectById(bo.getParentId());
        if (parent.getRestrictionLevel().equals(-1)) {
            return;
        }
        if (bo.getRestrictionLevel().equals(-1)) {
            throw new BusinessException(MessageUtils.message("document.folder.restrictionerror"));
        }
        checkRestriction(parent.getId(), parent.getRestrictionLevel());
    }

    @Override
    public boolean deleteWithValidIds(Collection<Long> ids, Boolean isValid) {
        return baseMapper.deleteByIds(ids) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateByBo(FolderBo bo) {
        Folder entity = MapstructUtils.convert(bo, Folder.class);
        boolean flag = baseMapper.updateById(entity) == 0;
        if (flag) {
            throw new RuntimeException("更新操作失败");
        }
        checkInsertIsValid(bo);
        checkUpdateIsValid(bo);
        return true;
    }

    private void checkUpdateIsValid(FolderBo bo) {
        checkRestriction(bo.getId(), bo.getRestrictionLevel());
    }

    private void checkRestriction(Long parentId, Integer restrictionLevel) {
        if (restrictionLevel.equals(-1)) {
            return;
        }
        AtomicInteger total = new AtomicInteger();
        baseMapper.selectList(Wrappers.lambdaQuery(Folder.class).eq(Folder::getParentId, parentId))
            .forEach(e -> {
                log.debug("当前层次文件夹名称 : {}; 限制个数 : {}", e.getName(), e.getRestrictionLevel());
                if (e.getRestrictionLevel().equals(-1)) {
                    throw new BusinessException(MessageUtils.message("document.folder.restrictionerror"));
                }
                total.addAndGet(e.getRestrictionLevel());
            });
        if (total.get() > restrictionLevel) {
            throw new BusinessException(MessageUtils.message("document.folder.restrictionerror"));
        }
    }

    @Override
    public FolderVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    @Override
    public TableDataInfo<ProjectCenterListVo> queryCenterInfo(ProjectCenterListBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<Folder> wrapper = buildCenterQueryWrapper(bo);
        IPage<Folder> page = baseMapper.selectPage(pageQuery.build(), wrapper);
        List<Long> userIds = new ArrayList<>();
        Map<Long, String> userMap = new HashMap<>();
        page.getRecords().forEach(e -> {
            userIds.add(e.getCreateBy());
            userIds.add(e.getUpdateBy());
        });
        userService.selectUserByIds(userIds)
            .forEach(e -> userMap.put(e.getUserId(), e.getNickName()));
        return TableDataInfo.build(page.convert(e -> {
            ProjectCenterListVo vo = new ProjectCenterListVo();
            vo.setId(e.getId());
            vo.setName(e.getName());
            vo.setStatus(e.getStatus());
            vo.setCreateTime(e.getCreateTime());
            vo.setUpdateTime(e.getUpdateTime());
            vo.setCreateBy(userMap.get(e.getCreateBy()));
            vo.setUpdateBy(userMap.get(e.getUpdateBy()));
            return vo;
        }));
    }

    @Override
    public TableDataInfo<ProjectCenterMemberListVo> queryCenterMember(ProjectCenterMemberListBo bo, PageQuery pageQuery) {
        List<Long> centerIds = new ArrayList<>();
        Map<Long, Folder> folderMap = new HashMap<>();
        List<Folder> folderList = new ArrayList<>();
        baseMapper.selectList(Wrappers.lambdaQuery(Folder.class).eq(Folder::getProjectId, bo.getProjectId()))
            .forEach(e -> {
                folderList.add(e);
                folderMap.put(e.getId(), e);
            });
        Map<Long, List<Folder>> childerMap = folderList.stream().filter(e -> e.getParentId() != null).collect(Collectors.groupingBy(Folder::getParentId));
        for (Folder folder : folderList) {
            if (folder.getType().equals(FolderTypeConst.CENTER)) {
                centerIds.add(folder.getId());
            } else if (folder.getType().equals(FolderTypeConst.COUNTRY)) {
                List<Folder> childrens = childerMap.getOrDefault(folder.getId(), Collections.emptyList());
                if (childrens.stream().anyMatch(e -> e.getType().equals(FolderTypeConst.CENTER))) {
                    centerIds.add(folder.getId());
                }
            }
        }

        IPage<SysUserFolders> page = userFoldersMapper.selectPage(
            pageQuery.build(),
            Wrappers.lambdaQuery(SysUserFolders.class)
                .eq(SysUserFolders::getProjectId, bo.getProjectId())
                .and(wrapper -> {
                        wrapper.eq(SysUserFolders::getFolders, "*");
                        for (Long centerId : centerIds) {
                            wrapper.or().apply(DataBaseHelper.findInSet(centerId, "folders"));
                        }
                    }
                )
                .orderByDesc(SysUserFolders::getUserId)
        );
        List<Long> userIds = new ArrayList<>();
        List<Long> deptIds = new ArrayList<>();
        Map<Long, SysUserVo> userMap = new HashMap<>();
        Map<Long, String> deptMap = new HashMap<>();
        page.getRecords().forEach(e -> userIds.add(e.getUserId()));
        userService.selectUserVoByIds(userIds).forEach(e -> {
            userMap.put(e.getUserId(), e);
            deptIds.add(e.getDeptId());
        });
        deptService.selectDeptByIds(deptIds).forEach(e -> deptMap.put(e.getDeptId(), e.getDeptName()));
        List<Folder> roots = folderList.stream().filter(folder -> folder.getParentId() == null).toList();
        return TableDataInfo.build(page.convert(e -> {

            SysUserVo user = userMap.get(e.getUserId());
            ProjectCenterMemberListVo vo = new ProjectCenterMemberListVo();

            Set<Folder> centers = new HashSet<>();
            if (e.getFolders().equals("*")) {
                for (Folder root : roots) {
                    if (root.getType().equals(FolderTypeConst.CENTER)) {
                        centers.add(root);
                        continue;
                    }
                    List<Folder> childrens = childerMap.getOrDefault(root.getId(), Collections.emptyList());
                    for (Folder children : childrens) {
                        if (children.getType().equals(FolderTypeConst.CENTER)) {
                            centers.add(children);
                        }
                    }
                }
            } else {
                Long[] folderIds = Arrays.stream(e.getFolders().split(",")).map(Long::valueOf).toArray(Long[]::new);
                for (Long folderId : folderIds) {
                    Folder current = folderMap.get(folderId);
                    if (current.getType().equals(FolderTypeConst.CENTER)) {
                        centers.add(current);
                    } else if (current.getType().equals(FolderTypeConst.COUNTRY)) {
                        // 国家就看有没有子中心
                        List<Folder> folders = childerMap.getOrDefault(current.getId(), Collections.emptyList());
                        for (Folder folder : folders) {
                            if (folder.getType().equals(FolderTypeConst.CENTER)) {
                                centers.add(folder);
                            }
                        }
                    } else {
                        // 如果该成员拥有某一中心的某个文件夹的任意权限,那么他也属于该中心
                        if (current.getParentId() == null) {
                            continue;
                        }
                        Folder parent = folderMap.get(current.getParentId());
                        while (parent.getType().equals(FolderTypeConst.NORMAL)) {
                            if (parent.getType().equals(FolderTypeConst.CENTER) || parent.getParentId() == null) {
                                break;
                            }
                            parent = folderMap.get(parent.getParentId());
                        }
                        if (parent.getType().equals(FolderTypeConst.CENTER)) {
                            centers.add(parent);
                        }
                    }
                }
            }
            vo.setCenters(String.join(",", new TreeSet<>(centers.stream().map(Folder::getName).collect(Collectors.toSet()))));

            if (user != null) {
                vo.setId(user.getUserId());
                vo.setName(user.getNickName());
                vo.setPhoneNumber(user.getPhonenumber());
                vo.setDept(deptMap.get(user.getDeptId()));
                vo.setTime(user.getCreateTime());
            }
            return vo;
        }));
    }

    private LambdaQueryWrapper<Folder> buildCenterQueryWrapper(ProjectCenterListBo bo) {
        return Wrappers.lambdaQuery(Folder.class)
            .eq(Folder::getType, FolderTypeConst.CENTER)
            .eq(Folder::getProjectId, bo.getProjectId())
            .like(StringUtils.isNotBlank(bo.getName()), Folder::getName, bo.getName())
            .orderByDesc(Folder::getId);
    }

}
