using Microsoft.EntityFrameworkCore;
using Modules.User.Application.Repositories;
using Modules.User.Database.Database;
using Modules.User.Domain.Entities.Access;
using Modules.User.Domain.Factories.Access;

namespace Modules.User.Database.Repositories;

public class RoleRepository(UserDbContext context) : IRoleRepository
{
    public async Task<Role?> GetAsync(int roleId, CancellationToken cancellationToken)
    {
        var efRole = await context.Roles
            .Include(r => r.Permissions)
            .FirstOrDefaultAsync(r => r.Id == roleId, cancellationToken);

        return efRole is null
            ? null
            : RoleFactory.Load(efRole.Id, efRole.Code, efRole.Name, efRole.Description, efRole.CreationDate,
                efRole.Permissions.Select(p => p.Id), efRole.Deleted);
    }

    public async Task<Role?> GetByNameAsync(string name, CancellationToken cancellationToken)
    {
        var efRole = await context.Roles
            .Include(r => r.Permissions)
            .FirstOrDefaultAsync(r => r.Name.ToLower() == name.Trim().ToLower(), cancellationToken);

        return efRole is null
            ? null
            : RoleFactory.Load(efRole.Id, efRole.Code, efRole.Name, efRole.Description,
                efRole.CreationDate, efRole.Permissions.Select(p => p.Id), efRole.Deleted);
    }

    public async Task<bool> ExistsWithPermissionAsync(int permissionId, CancellationToken cancellationToken)
    {
        return await context.Roles
            .SelectMany(q => q.Permissions)
            .AnyAsync(q => q.Id == permissionId, cancellationToken);
    }

    public async Task AddAsync(Role role, CancellationToken cancellationToken = default)
    {
        var permissions = await context.Permissions
            .Where(p => role.Permissions.Contains(p.Id))
            .ToListAsync(cancellationToken);
        var ef = new Database.Entities.Role
        {
            Id = role.Id,
            Code = role.Code,
            Name = role.Name,
            Description = role.Description,
            CreationDate = role.CreationDate,
            // Permissions = role.Permissions.Select(p => new Database.Entities.Permission
            // {
            //     Id = p.Id,
            //     Code = p.Code,
            //     Name = p.Name,
            //     Description = p.Description,
            //     CreationDate = p.CreationDate
            // }).ToList()
            Permissions = permissions
        };
        context.Roles.Add(ef);
    }

    public async Task SaveAsync(Role role, CancellationToken ct = default)
    {
        var ef = await context.Roles
            .Include(r => r.Permissions)
            .FirstAsync(r => r.Id == role.Id, ct);

        ef.Name = role.Name;
        ef.Description = role.Description;
        ef.Deleted = role.Deleted;

        // Синхронизация пермишенов
        var currentIds = ef.Permissions.Select(p => p.Id).ToHashSet();
        var desiredIds = role.Permissions.ToHashSet();

        foreach (var remove in currentIds.Except(desiredIds))
        {
            var p = ef.Permissions.First(x => x.Id == remove);
            ef.Permissions.Remove(p);
        }
        foreach (var add in desiredIds.Except(currentIds))
        {
            ef.Permissions.Add(await context.Permissions.FirstAsync(p => p.Id == add, ct));
        }
    }
}