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

namespace TakeoutSaaS.Infrastructure.Identity.Persistence;

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

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

    /// <summary>
    /// 批量新增角色权限。
    /// </summary>
    /// <param name="rolePermissions">角色权限集合。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>异步任务。</returns>
    public async Task AddRangeAsync(IEnumerable<RolePermission> rolePermissions, CancellationToken cancellationToken = default)
    {
        // 1. 转为数组便于计数
        var toAdd = rolePermissions as RolePermission[] ?? rolePermissions.ToArray();
        if (toAdd.Length == 0)
        {
            return;
        }

        // 2. 批量插入
        await dbContext.RolePermissions.AddRangeAsync(toAdd, cancellationToken);
    }

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

            // 1. 删除旧记录（原生 SQL，避免跟踪干扰）
            await dbContext.Database.ExecuteSqlRawAsync(
                "DELETE FROM \"role_permissions\" WHERE \"TenantId\" = {0} AND \"RoleId\" = {1};",
                parameters: new object[] { tenantId, roleId },
                cancellationToken: cancellationToken);

            // 2. 插入新记录（防重复）
            foreach (var permissionId in permissionIds.Distinct())
            {
                await dbContext.Database.ExecuteSqlRawAsync(
                    "INSERT INTO \"role_permissions\" (\"TenantId\",\"RoleId\",\"PermissionId\",\"CreatedAt\",\"CreatedBy\",\"UpdatedAt\",\"UpdatedBy\",\"DeletedAt\",\"DeletedBy\") VALUES ({0},{1},{2},NOW(),NULL,NULL,NULL,NULL,NULL) ON CONFLICT DO NOTHING;",
                    parameters: new object[] { tenantId, roleId, permissionId },
                    cancellationToken: cancellationToken);
            }

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

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