package com.czg.service.account.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.czg.account.dto.PageDTO;
import com.czg.account.dto.menu.MenuApiInfoItemDTO;
import com.czg.account.dto.role.RoleAddDTO;
import com.czg.account.dto.role.RoleEditDTO;
import com.czg.account.dto.role.RolePermissionDTO;
import com.czg.account.dto.role.RoleSaveTemplateDTO;
import com.czg.account.entity.*;
import com.czg.account.service.CashMenuService;
import com.czg.account.service.SysMenuService;
import com.czg.account.service.SysRoleService;
import com.czg.account.service.SysRolesMenusService;
import com.czg.exception.CzgException;
import com.czg.market.entity.SysRoleTemplate;
import com.czg.market.service.SysRoleTemplateService;
import com.czg.sa.StpKit;
import com.czg.service.account.mapper.SysRoleMapper;
import com.czg.service.account.mapper.SysRolesMenusMapper;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

import static com.mybatisflex.core.query.QueryMethods.column;

/**
 * 角色表 服务层实现。
 *
 * @author Administrator
 * @since 2025-02-08
 */
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {
    @Resource
    private SysMenuService sysMenuService;
    @Resource
    private SysRolesMenusService sysRolesMenusService;
    @Resource
    private SysRolesMenusMapper sysRolesMenusMapper;
    @Resource
    private CashMenuService cashMenuService;
    @Resource
    @Lazy
    private SysRoleTemplateService roleTemplateService;

    @Override
    public List<SysRole> getByUserId(Long id) {
        return mapper.selectByUserId(id);
    }

    /**
     * 将单个路径通配符转为 SQL LIKE 模式
     */
    public static String toSqlLikePattern(String pathPattern) {
        if (pathPattern == null) return null;

        // 转义 SQL 特殊字符
        String escaped = pathPattern
                .replace("\\", "\\\\")
                .replace("_", "\\_")
                .replace("%", "\\%");

        // 替换 ** 为占位，避免被 * 干扰
        String placeholder = "@@DOUBLE_STAR@@";
        escaped = escaped.replace("**", placeholder);

        // 替换 * 为单层路径匹配
        escaped = escaped.replace("*", "%");

        // 替换 ? 为单字符匹配
        escaped = escaped.replace("?", "_");

        // 替换回 **
        escaped = escaped.replace(placeholder, "%");

        return escaped;
    }

    /**
     * 拼接多个路径通配符为 SQL OR 条件（适用于 MyBatis-Plus apply）
     */
    public static String buildLikeSql(List<MenuApiInfoItemDTO> apiInfoItemDTOS, String columnName) {
        // 永不成立
        if (apiInfoItemDTOS == null || apiInfoItemDTOS.isEmpty()) {
            return "1 = 0";
        }

        return apiInfoItemDTOS.stream()
                .map(pattern -> StrUtil.format("({} LIKE '{}' ESCAPE '\\\\' and method = '{}')", columnName, toSqlLikePattern(pattern.getUrl()), pattern.getMethod()))
                .collect(Collectors.joining(" OR "));
    }

    @Override
    public Page<SysRole> getList(Long shopId, PageDTO pageDTO, String key, String startTime, String endTime) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq(SysRole::getShopId, shopId);

        if (StrUtil.isNotBlank(key)) {
            queryWrapper.and(column(SysRole::getName).like(key).or(column(SysRole::getDescription).like(key)));
        }

        if (StrUtil.isNotBlank(startTime)) {
            queryWrapper.ge(SysRole::getCreateTime, DateUtil.parse(startTime));
        }

        if (StrUtil.isNotBlank(endTime)) {
            queryWrapper.le(SysRole::getCreateTime, DateUtil.parse(endTime));
        }

        queryWrapper.orderBy(SysRole::getLevel, true);

        return page(new Page<>(pageDTO.page(), pageDTO.size()), queryWrapper);
    }

    @Override
    public List<Long> getRoleMenu(long shopId, Integer id, Integer type) {
        SysRole role = queryChain().eq(SysRole::getId, id).eq(SysRole::getCreateUserId, shopId).one();
        if (role == null) {
            throw new CzgException("角色不存在");
        }

        return sysRolesMenusService.queryChain().eq(SysRolesMenus::getRoleId, id).eq(SysRolesMenus::getType, type).list().stream().map(SysRolesMenus::getMenuId).toList();
    }

    public boolean addMenu(Long roleId, List<Long> menuIds, boolean isAdmin) {
        List<MenuApiInfoItemDTO> apiPathList = new ArrayList<>();
        Set<SysRolesMenus> rolesMenus = new HashSet<>();

        // Step 1: 获取菜单列表并校验
        List<? extends BaseMenu> menuList;
        if (isAdmin) {
            List<SysMenu> sysMenuList = sysMenuService.queryChain().in(SysMenu::getMenuId, menuIds).list();
            menuList = sysMenuList;
        } else {
            List<CashMenu> cashMenuList = cashMenuService.queryChain().in(CashMenu::getMenuId, menuIds).list();
            menuList = cashMenuList;
        }

        int type = isAdmin ? 0 : 1;

        // Step 2: 解析 apiInfo 并判断菜单是否已绑定
        for (BaseMenu menu : menuList) {
            // 解析 apiInfo
            if (StrUtil.isNotBlank(menu.getApiInfo())) {
                List<MenuApiInfoItemDTO> itemDTOS = JSONArray.parseArray(menu.getApiInfo())
                        .toJavaList(MenuApiInfoItemDTO.class);
                apiPathList.addAll(itemDTOS);
            }

            // 判断是否已绑定
            long count = sysRolesMenusService.count(new QueryWrapper()
                    .eq("menu_id", menu.getMenuId())
                    .eq("role_id", roleId)
                    .eq("type", type));
            if (count == 0) {
                rolesMenus.add(new SysRolesMenus(menu.getMenuId(), roleId, type));
            }
        }

        // Step 3: 匹配接口路径补充菜单（如 API 匹配到其他菜单）
        if (!apiPathList.isEmpty()) {
            String likeSql = buildLikeSql(apiPathList, "url");
            QueryWrapper wrapper = new QueryWrapper();
            wrapper.where(likeSql);

            List<SysMenu> matchedMenus = sysMenuService.list(wrapper);
            for (SysMenu matched : matchedMenus) {
                long count = sysRolesMenusService.count(new QueryWrapper()
                        .eq("menu_id", matched.getMenuId())
                        .eq("role_id", roleId)
                        .eq("type", type));
                if (count == 0) {
                    rolesMenus.add(new SysRolesMenus(matched.getMenuId(), roleId, type));
                }
            }
        }


        // Step 4: 批量保存（如有数据）
        return rolesMenus.isEmpty() || sysRolesMenusService.getMapper().insertBatchSelective(rolesMenus, 50) > 0;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean editPermission(long shopId, RolePermissionDTO rolePermissionDTO) {
        long count = count(new QueryWrapper().eq(SysRole::getId, rolePermissionDTO.getRoleId()).eq(SysRole::getCreateUserId, shopId));
        if (count == 0) {
            throw new CzgException("角色不存在");
        }
        sysRolesMenusService.updateChain().eq(SysRolesMenus::getRoleId, rolePermissionDTO.getRoleId()).remove();

        if (rolePermissionDTO.getAdminMenuIdList() != null && !rolePermissionDTO.getAdminMenuIdList().isEmpty()) {
            addMenu(rolePermissionDTO.getRoleId(), rolePermissionDTO.getAdminMenuIdList(), true);
        }

        if (rolePermissionDTO.getCashMenuIdList() != null && !rolePermissionDTO.getCashMenuIdList().isEmpty()) {
            addMenu(rolePermissionDTO.getRoleId(), rolePermissionDTO.getCashMenuIdList(), false);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(RoleAddDTO roleAddDTO) {
        long roleCount = queryChain().eq(SysRole::getName, roleAddDTO.name())
                .eq(SysRole::getShopId, StpKit.USER.getShopId())
                .count();
        if (roleCount > 0) {
            throw new CzgException("此角色名称已存在");
        }

        SysRole sysRole = new SysRole();
        sysRole.setName(roleAddDTO.name());
        sysRole.setLevel(roleAddDTO.level());
        sysRole.setDescription(roleAddDTO.description());
        sysRole.setShopId(StpKit.USER.getShopId());
        sysRole.setCreateUserId(StpKit.USER.getShopId());
        sysRole.setCreateTime(DateUtil.date().toLocalDateTime());
        boolean save = save(sysRole);
        if (save) {
            if (roleAddDTO.adminMenuIdList() != null && !roleAddDTO.adminMenuIdList().isEmpty()) {
                addMenu(sysRole.getId(), roleAddDTO.adminMenuIdList(), true);
            }

            if (roleAddDTO.cashMenuIdList() != null && !roleAddDTO.cashMenuIdList().isEmpty()) {
                addMenu(sysRole.getId(), roleAddDTO.cashMenuIdList(), false);
            }


            return save;
        }

        throw new CzgException("保存失败");
    }

    @Override
    @Transactional
    public Boolean saveByTemplate(long shopId, RoleSaveTemplateDTO dto) {
        roleTemplateService.list(new QueryWrapper().eq(SysRoleTemplate::getPid, dto.getId()).eq(SysRoleTemplate::getIsEnable, 1)).forEach(item -> {
            SysRole role = getById(item.getRoleId());
            role.setShopId(shopId);
            role.setCreateUserId(shopId);
            role.setCreateTime(null);
            role.setId(null);
            save(role);
            List<SysRolesMenus> list = sysRolesMenusService.list(query().select(SysRolesMenus::getMenuId, SysRolesMenus::getType).eq(SysRolesMenus::getRoleId, item.getRoleId()));
            if (CollUtil.isNotEmpty(list)) {
                sysRolesMenusMapper.saveByTemplate(role.getId(), list);
            }
        });
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean edit(RoleEditDTO roleEditDTO) {
        SysRole role = queryChain().eq(SysRole::getId, roleEditDTO.getId()).eq(SysRole::getCreateUserId, StpKit.USER.getShopId()).one();
        if (role == null) {
            throw new CzgException("角色不存在");
        }
        long roleCount;
        if (role.getShopId() == null) {
            roleCount = 0;
        } else {
            roleCount = queryChain().eq(SysRole::getName, roleEditDTO.getName()).ne(SysRole::getId, roleEditDTO.getId())
                    .eq(SysRole::getShopId, StpKit.USER.getShopId()).count();
        }
        if (roleCount > 0) {
            throw new CzgException("此角色名称已存在");
        }

        BeanUtil.copyProperties(roleEditDTO, role);
        boolean b = updateById(role);
        if (b) {
            if (roleEditDTO.getAdminMenuIdList() != null && !roleEditDTO.getAdminMenuIdList().isEmpty()) {
                sysRolesMenusService.updateChain().eq(SysRolesMenus::getRoleId, role.getId()).eq(SysRolesMenus::getType, 0).remove();

                addMenu(role.getId(), roleEditDTO.getAdminMenuIdList().stream().distinct().toList(), true);
            }

            if (roleEditDTO.getCashMenuIdList() != null && !roleEditDTO.getCashMenuIdList().isEmpty()) {
                sysRolesMenusService.updateChain().eq(SysRolesMenus::getRoleId, role.getId()).eq(SysRolesMenus::getType, 1).remove();
                addMenu(role.getId(), roleEditDTO.getCashMenuIdList().stream().distinct().toList(), false);
            }
            return true;
        }
        throw new CzgException("保存失败");
    }
}
