using Microsoft.EntityFrameworkCore;
using TakeoutSaaS.Domain.Identity.Entities;
using TakeoutSaaS.Domain.Identity.Repositories;

namespace TakeoutSaaS.Infrastructure.Identity.Persistence;

/// <summary>
/// EF 用户-角色仓储。
/// </summary>
public sealed class EfUserRoleRepository(IdentityDbContext dbContext) : IUserRoleRepository
{
    /// <summary>
    /// 根据用户 ID 集合获取用户角色映射。
    /// </summary>
    /// <param name="tenantId">租户 ID。</param>
    /// <param name="userIds">用户 ID 集合。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>用户角色映射列表。</returns>
    public async Task<IReadOnlyList<UserRole>> GetByUserIdsAsync(long tenantId, IEnumerable<long> userIds, CancellationToken cancellationToken = default)
    {
        // 1. 查询用户角色映射
        var mappings = await dbContext.UserRoles
            .AsNoTracking()
            .Where(x => x.TenantId == tenantId && x.DeletedAt == null && userIds.Contains(x.UserId))
            .ToListAsync(cancellationToken);

        // 2. (空行后) 返回只读列表
        return mappings;
    }

    /// <summary>
    /// 获取指定用户的角色集合。
    /// </summary>
    /// <param name="tenantId">租户 ID。</param>
    /// <param name="userId">用户 ID。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>用户角色列表。</returns>
    public async Task<IReadOnlyList<UserRole>> GetByUserIdAsync(long tenantId, long userId, CancellationToken cancellationToken = default)
    {
        // 1. 查询用户角色映射
        var mappings = await dbContext.UserRoles
            .AsNoTracking()
            .Where(x => x.TenantId == tenantId && x.DeletedAt == null && x.UserId == userId)
            .ToListAsync(cancellationToken);

        // 2. (空行后) 返回只读列表
        return mappings;
    }

    /// <summary>
    /// 替换指定用户的角色集合。
    /// </summary>
    /// <param name="tenantId">租户 ID。</param>
    /// <param name="userId">用户 ID。</param>
    /// <param name="roleIds">角色 ID 集合。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>异步任务。</returns>
    public async Task ReplaceUserRolesAsync(long tenantId, long userId, IEnumerable<long> roleIds, CancellationToken cancellationToken = default)
    {
        // 1. 使用执行策略保障一致性
        var strategy = dbContext.Database.CreateExecutionStrategy();
        await strategy.ExecuteAsync(async () =>
        {
            await using var trx = await dbContext.Database.BeginTransactionAsync(cancellationToken);

            // 2. 读取当前角色映射
            using var disableSoftDeleteScope = dbContext.DisableSoftDeleteFilter();
            var existing = await dbContext.UserRoles
                .Where(x => x.TenantId == tenantId && x.UserId == userId)
                .ToListAsync(cancellationToken);

            // 3. 去重并构建目标集合
            var targetRoleIds = roleIds.Distinct().ToArray();
            var targetRoleSet = targetRoleIds.ToHashSet();
            var existingRoleMap = existing.ToDictionary(x => x.RoleId);

            // 4. 同步现有映射状态（软删除或恢复）
            foreach (var mapping in existing)
            {
                if (targetRoleSet.Contains(mapping.RoleId))
                {
                    if (mapping.DeletedAt.HasValue)
                    {
                        mapping.DeletedAt = null;
                        mapping.DeletedBy = null;
                    }
                    continue;
                }

                if (!mapping.DeletedAt.HasValue)
                {
                    dbContext.UserRoles.Remove(mapping);
                }
            }

            // 5. 补齐新增角色映射
            var toAdd = targetRoleIds
                .Where(roleId => !existingRoleMap.ContainsKey(roleId))
                .Select(roleId => new UserRole
                {
                    TenantId = tenantId,
                    UserId = userId,
                    RoleId = roleId
                });

            await dbContext.UserRoles.AddRangeAsync(toAdd, cancellationToken);
            await dbContext.SaveChangesAsync(cancellationToken);

            await trx.CommitAsync(cancellationToken);
        });
    }

    /// <summary>
    /// 统计指定角色下的用户数量。
    /// </summary>
    /// <param name="tenantId">租户 ID。</param>
    /// <param name="roleId">角色 ID。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>用户数量。</returns>
    public Task<int> CountUsersByRoleAsync(long tenantId, long roleId, CancellationToken cancellationToken = default)
        => dbContext.UserRoles
            .AsNoTracking()
            .Where(x => x.TenantId == tenantId && x.DeletedAt == null && x.RoleId == roleId)
            .CountAsync(cancellationToken);

    /// <summary>
    /// 保存仓储变更。
    /// </summary>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>保存任务。</returns>
    public Task SaveChangesAsync(CancellationToken cancellationToken = default)
        => dbContext.SaveChangesAsync(cancellationToken);
}
