using ColaFlow.Modules.Identity.Application.Services;
using ColaFlow.Modules.Identity.Domain.Aggregates.Users.Events;
using ColaFlow.Modules.Identity.Domain.Repositories;
using MediatR;
using Microsoft.Extensions.Logging;

namespace ColaFlow.Modules.Identity.Application.Commands.ResetPassword;

public class ResetPasswordCommandHandler(
    IPasswordResetTokenRepository tokenRepository,
    IUserRepository userRepository,
    IRefreshTokenRepository refreshTokenRepository,
    ISecurityTokenService tokenService,
    IPasswordHasher passwordHasher,
    ILogger<ResetPasswordCommandHandler> logger,
    IPublisher publisher)
    : IRequestHandler<ResetPasswordCommand, bool>
{
    public async Task<bool> Handle(ResetPasswordCommand request, CancellationToken cancellationToken)
    {
        // Validate new password
        if (string.IsNullOrWhiteSpace(request.NewPassword) || request.NewPassword.Length < 8)
        {
            logger.LogWarning("Invalid password provided for reset");
            return false;
        }

        // Hash the token to look it up
        var tokenHash = tokenService.HashToken(request.Token);
        var resetToken = await tokenRepository.GetByTokenHashAsync(tokenHash, cancellationToken);

        if (resetToken == null)
        {
            logger.LogWarning("Password reset token not found");
            return false;
        }

        if (!resetToken.IsValid)
        {
            logger.LogWarning(
                "Password reset token is invalid. IsExpired: {IsExpired}, IsUsed: {IsUsed}",
                resetToken.IsExpired,
                resetToken.IsUsed);
            return false;
        }

        // Get user
        var user = await userRepository.GetByIdAsync(resetToken.UserId, cancellationToken);
        if (user == null)
        {
            logger.LogError("User {UserId} not found for password reset", resetToken.UserId);
            return false;
        }

        // Hash the new password
        var newPasswordHash = passwordHasher.HashPassword(request.NewPassword);

        // Update user password (will emit UserPasswordChangedEvent)
        user.UpdatePassword(newPasswordHash);
        await userRepository.UpdateAsync(user, cancellationToken);

        // Mark token as used
        resetToken.MarkAsUsed();
        await tokenRepository.UpdateAsync(resetToken, cancellationToken);

        // Revoke all refresh tokens for security (force re-login on all devices)
        await refreshTokenRepository.RevokeAllUserTokensAsync(
            (Guid)user.Id,
            "Password reset",
            cancellationToken);

        // Publish domain event for audit logging
        await publisher.Publish(
            new PasswordResetCompletedEvent((Guid)user.Id, resetToken.IpAddress),
            cancellationToken);

        logger.LogInformation(
            "Password reset successfully completed for user {UserId}. All refresh tokens revoked.",
            (Guid)user.Id);

        return true;
    }
}
