using Knot.Contracts.Auth.Domain;
using Knot.Modules.Auth.Domain;
using Microsoft.EntityFrameworkCore;

namespace Knot.Modules.Auth.Infrastructure.Persistence;

internal sealed class UserRepository : IUserRepository
{
    private readonly AuthDbContext _context;

    public UserRepository(AuthDbContext context)
    {
        _context = context;
    }

    public async Task<UserContract?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var user = await _context.Users.FirstOrDefaultAsync(u => u.Id == id, cancellationToken);
        return user?.ToContract();
    }

    public async Task<List<UserContract>> GetByIdsAsync(IEnumerable<Guid> ids, CancellationToken cancellationToken = default)
    {
        var users = await _context.Users.Where(u => ids.Contains(u.Id)).ToListAsync(cancellationToken);
        return users.Select(u => u.ToContract()).ToList();
    }

    public async Task<UserContract?> GetByUsernameAsync(string username, CancellationToken cancellationToken = default)
    {
        var user = await _context.Users.FirstOrDefaultAsync(u => u.Username == username, cancellationToken);
        return user?.ToContract();
    }

    public async Task<bool> IsUsernameUniqueAsync(string username, CancellationToken cancellationToken = default)
    {
        return !await _context.Users.AnyAsync(u => u.Username == username, cancellationToken);
    }

    public async Task<List<UserContract>> SearchUsersAsync(string query, CancellationToken cancellationToken = default)
    {
        var users = await _context.Users
            .Where(u => u.Username.ToLower().Contains(query.ToLower()) ||
                        (u.DisplayName != null && u.DisplayName.ToLower().Contains(query.ToLower())))
            .Take(20)
            .ToListAsync(cancellationToken);
        return users.Select(u => u.ToContract()).ToList();
    }

    public async Task<bool> IsBannedAsync(Guid userId, CancellationToken cancellationToken = default)
    {
        var user = await _context.Users.FirstOrDefaultAsync(u => u.Id == userId, cancellationToken);
        return user?.IsBanned ?? false;
    }

    public async Task<UserContract?> GetByRefreshTokenAsync(string refreshToken, CancellationToken cancellationToken = default)
    {
        var user = await _context.Users.FirstOrDefaultAsync(u => u.RefreshToken == refreshToken, cancellationToken);
        return user?.ToContract();
    }

    public async Task UpdateAsync(UserContract user, CancellationToken cancellationToken = default)
    {
        var domainUser = await _context.Users.FirstOrDefaultAsync(u => u.Id == user.Id, cancellationToken);
        if (domainUser != null)
        {
            domainUser.UpdateFromContract(user);
            _context.Users.Update(domainUser);
            await _context.SaveChangesAsync(cancellationToken);
        }
    }

    public void Add(UserContract user)
    {
        var domainUser = User.Create(user.Username, user.PasswordHash, user.DisplayName, user.Email, user.Bio);
        _context.Users.Add(domainUser);
    }

    public void Add(User user)
    {
        _context.Users.Add(user);
    }

    public void Update(User user)
    {
        _context.Users.Update(user);
    }

    public void Remove(User user)
    {
        _context.Users.Remove(user);
    }

    public async Task RemoveAsync(UserContract user, CancellationToken cancellationToken = default)
    {
        var domainUser = await _context.Users.FirstOrDefaultAsync(u => u.Id == user.Id, cancellationToken);
        if (domainUser != null)
        {
            _context.Users.Remove(domainUser);
        }
    }

    public void Update(UserContract user)
    {
        var domainUser = _context.Users.Find(user.Id);
        if (domainUser != null)
        {
            domainUser.UpdateFromContract(user);
            _context.Users.Update(domainUser);
        }
    }
}
