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 Task<IReadOnlyList<UserRole>> GetByUserIdsAsync(long tenantId, IEnumerable<long> userIds, CancellationToken cancellationToken = default)
        => dbContext.UserRoles.AsNoTracking()
            .Where(x => x.TenantId == tenantId && userIds.Contains(x.UserId))
            .ToListAsync(cancellationToken)
            .ContinueWith(t => (IReadOnlyList<UserRole>)t.Result, cancellationToken);

    /// <summary>
    /// 获取指定用户的角色集合。
    /// </summary>
    /// <param name="tenantId">租户 ID。</param>
    /// <param name="userId">用户 ID。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>用户角色列表。</returns>
    public Task<IReadOnlyList<UserRole>> GetByUserIdAsync(long tenantId, long userId, CancellationToken cancellationToken = default)
        => dbContext.UserRoles.AsNoTracking()
            .Where(x => x.TenantId == tenantId && x.UserId == userId)
            .ToListAsync(cancellationToken)
            .ContinueWith(t => (IReadOnlyList<UserRole>)t.Result, cancellationToken);

    /// <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. 读取当前角色映射
            var existing = await dbContext.UserRoles
                .Where(x => x.TenantId == tenantId && x.UserId == userId)
                .ToListAsync(cancellationToken);

            // 3. 清空并保存
            dbContext.UserRoles.RemoveRange(existing);
            await dbContext.SaveChangesAsync(cancellationToken);

            // 4. 构建新映射
            var toAdd = roleIds.Distinct().Select(roleId => new UserRole
            {
                TenantId = tenantId,
                UserId = userId,
                RoleId = roleId
            });

            // 5. 批量新增并保存
            await dbContext.UserRoles.AddRangeAsync(toAdd, cancellationToken);
            await dbContext.SaveChangesAsync(cancellationToken);

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

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