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 cn.nianxx.thhotel.platform.common.dto.PlatformPageSnapshot;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.dao.DuplicateKeyException;
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<PlatformRoleEntity> findRoleById(Long roleId) {
        if (roleId == null) {
            return Optional.empty();
        }
        return Optional.ofNullable(roleMapper.selectById(roleId));
    }

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

    @Override
    public Optional<PlatformPermissionEntity> findPermissionById(Long permissionId) {
        if (permissionId == null) {
            return Optional.empty();
        }
        return Optional.ofNullable(permissionMapper.selectById(permissionId));
    }

    @Override
    public PlatformPageSnapshot<PlatformRoleEntity> queryRoles(String keyword, String roleStatus, int pageNum, int pageSize) {
        Page<PlatformRoleEntity> page = roleMapper.selectPage(Page.of(pageNum, pageSize),
                Wrappers.<PlatformRoleEntity>lambdaQuery()
                        .eq(roleStatus != null && !roleStatus.isBlank(), PlatformRoleEntity::getRoleStatus, roleStatus)
                        .and(keyword != null && !keyword.isBlank(), wrapper -> wrapper
                                .like(PlatformRoleEntity::getRoleCode, keyword)
                                .or()
                                .like(PlatformRoleEntity::getRoleName, keyword))
                        .orderByDesc(PlatformRoleEntity::getSystemBuiltin)
                        .orderByAsc(PlatformRoleEntity::getRoleCode));
        return new PlatformPageSnapshot<>(page.getRecords(), page.getTotal(), pageNum, pageSize);
    }

    @Override
    public List<PlatformRoleEntity> listAllRoles() {
        return roleMapper.selectList(Wrappers.<PlatformRoleEntity>lambdaQuery()
                .orderByDesc(PlatformRoleEntity::getSystemBuiltin)
                .orderByAsc(PlatformRoleEntity::getRoleCode));
    }

    @Override
    public List<PlatformRoleEntity> listRolesByIds(List<Long> roleIds) {
        if (roleIds == null || roleIds.isEmpty()) {
            return Collections.emptyList();
        }
        return roleMapper.selectList(Wrappers.<PlatformRoleEntity>lambdaQuery()
                .in(PlatformRoleEntity::getId, roleIds));
    }

    @Override
    public List<PlatformPermissionEntity> listAllPermissions() {
        return permissionMapper.selectList(Wrappers.<PlatformPermissionEntity>lambdaQuery()
                .orderByAsc(PlatformPermissionEntity::getPermissionGroup)
                .orderByAsc(PlatformPermissionEntity::getPermissionCode));
    }

    @Override
    public List<PlatformUserRoleEntity> listUserRolesByUserIds(List<Long> userIds) {
        if (userIds == null || userIds.isEmpty()) {
            return Collections.emptyList();
        }
        return userRoleMapper.selectList(Wrappers.<PlatformUserRoleEntity>lambdaQuery()
                .in(PlatformUserRoleEntity::getUserId, userIds));
    }

    @Override
    public List<PlatformRolePermissionEntity> listRolePermissionsByRoleIds(List<Long> roleIds) {
        if (roleIds == null || roleIds.isEmpty()) {
            return Collections.emptyList();
        }
        return rolePermissionMapper.selectList(Wrappers.<PlatformRolePermissionEntity>lambdaQuery()
                .in(PlatformRolePermissionEntity::getRoleId, roleIds));
    }

    @Override
    public List<PlatformRolePermissionEntity> listRolePermissionsByRoleId(Long roleId) {
        if (roleId == null) {
            return Collections.emptyList();
        }
        return rolePermissionMapper.selectList(Wrappers.<PlatformRolePermissionEntity>lambdaQuery()
                .eq(PlatformRolePermissionEntity::getRoleId, roleId));
    }

    @Override
    public long countUsersByRoleId(Long roleId) {
        if (roleId == null) {
            return 0L;
        }
        return userRoleMapper.selectCount(Wrappers.<PlatformUserRoleEntity>lambdaQuery()
                .eq(PlatformUserRoleEntity::getRoleId, roleId));
    }

    @Override
    public long countPermissionsByRoleId(Long roleId) {
        if (roleId == null) {
            return 0L;
        }
        return rolePermissionMapper.selectCount(Wrappers.<PlatformRolePermissionEntity>lambdaQuery()
                .eq(PlatformRolePermissionEntity::getRoleId, roleId));
    }

    @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());
        try {
            rolePermissionMapper.insert(relation);
        } catch (DuplicateKeyException ignored) {
            // 多实例同时启动时唯一索引已经保证关系存在，重复插入可安全忽略。
        }
    }

    @Override
    public void syncRolePermissions(Long roleId, List<Long> permissionIds) {
        Set<Long> expectedPermissionIds = new LinkedHashSet<>(permissionIds == null
                ? Collections.emptyList()
                : permissionIds);
        expectedPermissionIds.forEach(permissionId -> ensureRolePermission(roleId, permissionId));
        if (expectedPermissionIds.isEmpty()) {
            rolePermissionMapper.delete(Wrappers.<PlatformRolePermissionEntity>lambdaQuery()
                    .eq(PlatformRolePermissionEntity::getRoleId, roleId));
            return;
        }
        rolePermissionMapper.delete(Wrappers.<PlatformRolePermissionEntity>lambdaQuery()
                .eq(PlatformRolePermissionEntity::getRoleId, roleId)
                .notIn(PlatformRolePermissionEntity::getPermissionId, expectedPermissionIds));
    }

    @Override
    public void replaceUserRoles(Long userId, List<Long> roleIds) {
        userRoleMapper.delete(Wrappers.<PlatformUserRoleEntity>lambdaQuery()
                .eq(PlatformUserRoleEntity::getUserId, userId));
        if (roleIds == null || roleIds.isEmpty()) {
            return;
        }
        new LinkedHashSet<>(roleIds).forEach(roleId -> ensureUserRole(userId, roleId));
    }

    @Override
    public void replaceRolePermissions(Long roleId, List<Long> permissionIds) {
        rolePermissionMapper.delete(Wrappers.<PlatformRolePermissionEntity>lambdaQuery()
                .eq(PlatformRolePermissionEntity::getRoleId, roleId));
        if (permissionIds == null || permissionIds.isEmpty()) {
            return;
        }
        new LinkedHashSet<>(permissionIds).forEach(permissionId -> ensureRolePermission(roleId, permissionId));
    }

    @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());
        try {
            userRoleMapper.insert(relation);
        } catch (DuplicateKeyException ignored) {
            // 多实例同时启动时唯一索引已经保证关系存在，重复插入可安全忽略。
        }
    }

    @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);
    }
}
