package tech.easyflow.system.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tech.easyflow.common.cache.RedisLockExecutor;
import tech.easyflow.common.constant.enums.EnumDataScope;
import tech.easyflow.system.entity.SysAccountRole;
import tech.easyflow.system.entity.SysRole;
import tech.easyflow.system.entity.SysRoleDept;
import tech.easyflow.system.entity.SysRoleMenu;
import tech.easyflow.system.mapper.SysRoleDeptMapper;
import tech.easyflow.system.mapper.SysRoleMapper;
import tech.easyflow.system.mapper.SysRoleMenuMapper;
import tech.easyflow.system.service.SysAccountRoleService;
import tech.easyflow.system.service.SysRoleService;

import javax.annotation.Resource;
import java.time.Duration;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 系统角色 服务层实现。
 *
 * @author ArkLight
 * @since 2025-03-14
 */
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {

    private static final String ROLE_LOCK_KEY_PREFIX = "easyflow:lock:sys:role:";
    private static final String ROLE_CREATE_LOCK_KEY_PREFIX = "easyflow:lock:sys:role:create:";
    private static final Duration LOCK_WAIT_TIMEOUT = Duration.ofSeconds(2);
    private static final Duration LOCK_LEASE_TIMEOUT = Duration.ofSeconds(10);

    @Resource
    private SysAccountRoleService sysAccountRoleService;
    @Resource
    private SysRoleMenuMapper sysRoleMenuMapper;
    @Resource
    private SysRoleDeptMapper sysRoleDeptMapper;
    @Resource
    private RedisLockExecutor redisLockExecutor;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveRoleMenu(BigInteger roleId, List<String> keys) {
        redisLockExecutor.executeWithLock(ROLE_LOCK_KEY_PREFIX + roleId, LOCK_WAIT_TIMEOUT, LOCK_LEASE_TIMEOUT, () -> {
            QueryWrapper delW = QueryWrapper.create();
            delW.eq(SysRoleMenu::getRoleId, roleId);
            sysRoleMenuMapper.deleteByQuery(delW);

            if (CollectionUtil.isEmpty(keys)) {
                return;
            }
            Set<BigInteger> uniqueMenuIds = new LinkedHashSet<>();
            for (String key : keys) {
                if (key != null && !key.isEmpty()) {
                    uniqueMenuIds.add(new BigInteger(key));
                }
            }
            if (uniqueMenuIds.isEmpty()) {
                return;
            }
            List<SysRoleMenu> rows = new ArrayList<>(uniqueMenuIds.size());
            for (BigInteger menuId : uniqueMenuIds) {
                SysRoleMenu row = new SysRoleMenu();
                row.setRoleId(roleId);
                row.setMenuId(menuId);
                rows.add(row);
            }
            sysRoleMenuMapper.insertBatch(rows);
        });
    }

    @Override
    public List<SysRole> getRolesByAccountId(BigInteger accountId) {
        // 查询用户对应角色id集合
        QueryWrapper am = QueryWrapper.create();
        am.eq(SysAccountRole::getAccountId, accountId);
        List<BigInteger> roleIds = sysAccountRoleService.list(am).stream().map(SysAccountRole::getRoleId).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(roleIds)) {
            return new ArrayList<>();
        }
        return listByIds(roleIds);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveRole(SysRole sysRole) {
        String lockKey = buildRoleLockKey(sysRole);
        redisLockExecutor.executeWithLock(lockKey, LOCK_WAIT_TIMEOUT, LOCK_LEASE_TIMEOUT, () -> {
            saveOrUpdate(sysRole);

            // 非自定义数据权限，则部门id集合为空
            if (!EnumDataScope.CUSTOM.getCode().equals(sysRole.getDataScope())) {
                sysRole.setDeptIds(new ArrayList<>());
            }

            Set<BigInteger> uniqueMenuIds = new LinkedHashSet<>();
            if (CollectionUtil.isNotEmpty(sysRole.getMenuIds())) {
                uniqueMenuIds.addAll(sysRole.getMenuIds());
            }
            Set<BigInteger> uniqueDeptIds = new LinkedHashSet<>();
            if (CollectionUtil.isNotEmpty(sysRole.getDeptIds())) {
                uniqueDeptIds.addAll(sysRole.getDeptIds());
            }

            QueryWrapper wrm = QueryWrapper.create();
            wrm.eq(SysRoleMenu::getRoleId, sysRole.getId());
            sysRoleMenuMapper.deleteByQuery(wrm);
            QueryWrapper wrd = QueryWrapper.create();
            wrd.eq(SysRoleDept::getRoleId, sysRole.getId());
            sysRoleDeptMapper.deleteByQuery(wrd);

            if (CollectionUtil.isNotEmpty(uniqueMenuIds)) {
                for (BigInteger menuId : uniqueMenuIds) {
                    if (menuId == null) {
                        continue;
                    }
                    SysRoleMenu roleMenu = new SysRoleMenu();
                    roleMenu.setRoleId(sysRole.getId());
                    roleMenu.setMenuId(menuId);
                    sysRoleMenuMapper.insert(roleMenu);
                }
            }

            if (CollectionUtil.isNotEmpty(uniqueDeptIds)) {
                for (BigInteger deptId : uniqueDeptIds) {
                    if (deptId == null) {
                        continue;
                    }
                    SysRoleDept roleDept = new SysRoleDept();
                    roleDept.setRoleId(sysRole.getId());
                    roleDept.setDeptId(deptId);
                    sysRoleDeptMapper.insert(roleDept);
                }
            }
        });
    }

    private String buildRoleLockKey(SysRole sysRole) {
        if (sysRole.getId() != null) {
            return ROLE_LOCK_KEY_PREFIX + sysRole.getId();
        }
        String tenantPart = sysRole.getTenantId() == null ? "0" : sysRole.getTenantId().toString();
        String roleKeyPart = sysRole.getRoleKey() == null ? "unknown" : sysRole.getRoleKey();
        return ROLE_CREATE_LOCK_KEY_PREFIX + tenantPart + ":" + roleKeyPart;
    }
}
