package cn.nianxx.thhotel.platform.access.repository;

import cn.nianxx.thhotel.platform.access.common.enums.PlatformPermissionStatus;
import cn.nianxx.thhotel.platform.access.common.enums.PlatformRoleStatus;
import cn.nianxx.thhotel.platform.access.domain.PlatformPermissionEntity;
import cn.nianxx.thhotel.platform.access.domain.PlatformRoleEntity;
import cn.nianxx.thhotel.platform.access.domain.PlatformRolePermissionEntity;
import cn.nianxx.thhotel.platform.access.domain.PlatformUserRoleEntity;
import cn.nianxx.thhotel.platform.access.mapper.PlatformPermissionMapper;
import cn.nianxx.thhotel.platform.access.mapper.PlatformRoleMapper;
import cn.nianxx.thhotel.platform.access.mapper.PlatformRolePermissionMapper;
import cn.nianxx.thhotel.platform.access.mapper.PlatformUserRoleMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.springframework.stereotype.Repository;

/**
 * 基于 MyBatis-Plus 的平台角色权限仓储实现。
 */
@Repository
public class MybatisPlatformAccessRepository implements PlatformAccessRepository {

    private final PlatformRoleMapper roleMapper;
    private final PlatformPermissionMapper permissionMapper;
    private final PlatformUserRoleMapper userRoleMapper;
    private final PlatformRolePermissionMapper rolePermissionMapper;

    public MybatisPlatformAccessRepository(
            PlatformRoleMapper roleMapper,
            PlatformPermissionMapper permissionMapper,
            PlatformUserRoleMapper userRoleMapper,
            PlatformRolePermissionMapper rolePermissionMapper) {
        this.roleMapper = roleMapper;
        this.permissionMapper = permissionMapper;
        this.userRoleMapper = userRoleMapper;
        this.rolePermissionMapper = rolePermissionMapper;
    }

    @Override
    public Optional<PlatformRoleEntity> findRoleByCode(String roleCode) {
        return Optional.ofNullable(roleMapper.selectOne(Wrappers.<PlatformRoleEntity>lambdaQuery()
                .eq(PlatformRoleEntity::getRoleCode, roleCode)
                .last("LIMIT 1")));
    }

    @Override
    public Optional<PlatformPermissionEntity> findPermissionByCode(String permissionCode) {
        return Optional.ofNullable(permissionMapper.selectOne(Wrappers.<PlatformPermissionEntity>lambdaQuery()
                .eq(PlatformPermissionEntity::getPermissionCode, permissionCode)
                .last("LIMIT 1")));
    }

    @Override
    public void insertRole(PlatformRoleEntity role) {
        roleMapper.insert(role);
    }

    @Override
    public void updateRole(PlatformRoleEntity role) {
        roleMapper.updateById(role);
    }

    @Override
    public void insertPermission(PlatformPermissionEntity permission) {
        permissionMapper.insert(permission);
    }

    @Override
    public void updatePermission(PlatformPermissionEntity permission) {
        permissionMapper.updateById(permission);
    }

    @Override
    public void ensureRolePermission(Long roleId, Long permissionId) {
        Long existing = rolePermissionMapper.selectCount(Wrappers.<PlatformRolePermissionEntity>lambdaQuery()
                .eq(PlatformRolePermissionEntity::getRoleId, roleId)
                .eq(PlatformRolePermissionEntity::getPermissionId, permissionId));
        if (existing != null && existing > 0) {
            return;
        }
        PlatformRolePermissionEntity relation = new PlatformRolePermissionEntity();
        relation.setRoleId(roleId);
        relation.setPermissionId(permissionId);
        relation.setCreatedAt(nowUtc());
        rolePermissionMapper.insert(relation);
    }

    @Override
    public void ensureUserRole(Long userId, Long roleId) {
        Long existing = userRoleMapper.selectCount(Wrappers.<PlatformUserRoleEntity>lambdaQuery()
                .eq(PlatformUserRoleEntity::getUserId, userId)
                .eq(PlatformUserRoleEntity::getRoleId, roleId));
        if (existing != null && existing > 0) {
            return;
        }
        PlatformUserRoleEntity relation = new PlatformUserRoleEntity();
        relation.setUserId(userId);
        relation.setRoleId(roleId);
        relation.setCreatedAt(nowUtc());
        userRoleMapper.insert(relation);
    }

    @Override
    public List<String> listActivePermissionCodesByUserId(Long userId) {
        List<PlatformUserRoleEntity> userRoles = userRoleMapper.selectList(Wrappers.<PlatformUserRoleEntity>lambdaQuery()
                .eq(PlatformUserRoleEntity::getUserId, userId));
        if (userRoles.isEmpty()) {
            return Collections.emptyList();
        }
        Set<Long> roleIds = new LinkedHashSet<>(userRoles.stream()
                .map(PlatformUserRoleEntity::getRoleId)
                .toList());
        List<PlatformRoleEntity> roles = roleMapper.selectList(Wrappers.<PlatformRoleEntity>lambdaQuery()
                .in(PlatformRoleEntity::getId, roleIds)
                .eq(PlatformRoleEntity::getRoleStatus, PlatformRoleStatus.ACTIVE.name()));
        if (roles.isEmpty()) {
            return Collections.emptyList();
        }
        Set<Long> activeRoleIds = new LinkedHashSet<>(roles.stream()
                .map(PlatformRoleEntity::getId)
                .toList());
        List<PlatformRolePermissionEntity> rolePermissions = rolePermissionMapper.selectList(
                Wrappers.<PlatformRolePermissionEntity>lambdaQuery()
                        .in(PlatformRolePermissionEntity::getRoleId, activeRoleIds));
        if (rolePermissions.isEmpty()) {
            return Collections.emptyList();
        }
        Set<Long> permissionIds = new LinkedHashSet<>(rolePermissions.stream()
                .map(PlatformRolePermissionEntity::getPermissionId)
                .toList());
        return permissionMapper.selectList(Wrappers.<PlatformPermissionEntity>lambdaQuery()
                        .in(PlatformPermissionEntity::getId, permissionIds)
                        .eq(PlatformPermissionEntity::getPermissionStatus, PlatformPermissionStatus.ACTIVE.name()))
                .stream()
                .map(PlatformPermissionEntity::getPermissionCode)
                .toList();
    }

    private LocalDateTime nowUtc() {
        return LocalDateTime.now(ZoneOffset.UTC);
    }
}
