using BCrypt.Net;
using BCrypt.Net;
using Knot.Contracts.Auth.Application.Abstractions;
using Knot.Contracts.Auth.Application.Auth.DTOs;
using Knot.Contracts.Settings.Application.Abstractions;
using Knot.Contracts.Settings.Application.DTOs;
using Knot.Modules.Auth.Domain;
using Knot.Shared.Kernel;

namespace Knot.Modules.Auth.Application.Users.Register;

/// <summary>
/// ������� ��� ����������� ������ ������������.
/// </summary>
public sealed record RegisterUserCommand(
    string Username,
    string Password,
    string DisplayName,
    string? Email,
    string? Bio) : ICommand<AuthResponseDto>;

/// <summary>
/// ���������� ������� �����������.
/// </summary>
internal sealed class RegisterUserCommandHandler : ICommandHandler<RegisterUserCommand, AuthResponseDto>
{
    private readonly IUserRepository _userRepository;
    private readonly IAuthUnitOfWork _unitOfWork;
    private readonly ISystemSettings _settings;
    private readonly IJwtTokenProvider _tokenProvider;

    public RegisterUserCommandHandler(
        IUserRepository userRepository,
        IAuthUnitOfWork unitOfWork,
        ISystemSettings settings,
        IJwtTokenProvider tokenProvider)
    {
        _userRepository = userRepository;
        _unitOfWork = unitOfWork;
        _settings = settings;
        _tokenProvider = tokenProvider;
    }

    public async Task<Result<AuthResponseDto>> Handle(RegisterUserCommand request, CancellationToken cancellationToken)
    {
        if (!_settings.Current.EnableRegistration)
        {
            return Result.Failure<AuthResponseDto>(AuthErrors.IdentityRegistrationDisabled);
        }

        // 1. �������� ������������ username
        if (!await _userRepository.IsUsernameUniqueAsync(request.Username, cancellationToken))
        {
            return Result.Failure<AuthResponseDto>(AuthErrors.IdentityUsernameNotUnique);
        }

        // 2. ����������� ������
        string passwordHash = BCrypt.Net.BCrypt.HashPassword(request.Password);

        // 3. �������� ��������
        var user = User.Create(
            request.Username,
            passwordHash,
            request.DisplayName,
            request.Email,
            request.Bio);

        // 4. ���������� - ���������� ����� � Domain User
        var repoWithDomainUserAdd = _userRepository as Infrastructure.Persistence.UserRepository;
        repoWithDomainUserAdd?.Add(user);

        await _unitOfWork.SaveChangesAsync(cancellationToken);

        string token = _tokenProvider.Generate(user.Id, user.Username, user.DisplayName, user.Avatar);

        return Result.Success(new AuthResponseDto
        {
            AccessToken = token,
            RefreshToken = string.Empty,
            UserId = user.Id,
            Username = user.Username,
            DisplayName = user.DisplayName
        });
    }
}
