using Knot.Contracts.Auth.Application.Abstractions;
using Knot.Contracts.Auth.Application.Auth.DTOs;
using Knot.Contracts.Auth.Domain;
using Knot.Shared.Kernel;
using MediatR;

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

internal sealed class RefreshTokenCommandHandler : ICommandHandler<RefreshTokenCommand, AuthResponseDto>
{
    private readonly IUserRepository _userRepository;
    private readonly IJwtTokenProvider _tokenProvider;

    public RefreshTokenCommandHandler(
        IUserRepository userRepository,
        IJwtTokenProvider tokenProvider)
    {
        _userRepository = userRepository;
        _tokenProvider = tokenProvider;
    }

    public async Task<Result<AuthResponseDto>> Handle(RefreshTokenCommand request, CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(request.RefreshToken))
        {
            return Result.Failure<AuthResponseDto>(
                new Error("Auth.InvalidRefreshToken", "Refresh token is required"));
        }

        var user = await _userRepository.GetByRefreshTokenAsync(request.RefreshToken, cancellationToken);
        if (user == null)
        {
            return Result.Failure<AuthResponseDto>(
                new Error("Auth.InvalidRefreshToken", "Invalid or expired refresh token"));
        }

        // Check if refresh token has expired
        if (user.RefreshTokenExpiry.HasValue && user.RefreshTokenExpiry.Value < DateTime.UtcNow)
        {
            // Clear expired refresh token
            user.SetRefreshToken(null, null);
            await _userRepository.UpdateAsync(user, cancellationToken);
            
            return Result.Failure<AuthResponseDto>(
                new Error("Auth.RefreshTokenExpired", "Refresh token has expired. Please login again."));
        }

        var newAccessToken = _tokenProvider.Generate(user.Id, user.Username, user.DisplayName, user.Avatar);

        var newRefreshToken = _tokenProvider.GenerateRefreshToken();
        var newRefreshExpiry = _tokenProvider.GetRefreshTokenExpiry();
        user.SetRefreshToken(newRefreshToken, newRefreshExpiry);
        await _userRepository.UpdateAsync(user, cancellationToken);

        return Result.Success(new AuthResponseDto
        {
            AccessToken = newAccessToken,
            RefreshToken = newRefreshToken,
            UserId = user.Id,
            Username = user.Username,
            DisplayName = user.DisplayName,
            Avatar = user.Avatar
        });
    }
}
