using ColaFlow.Modules.Identity.Domain.Aggregates.Users;
using ColaFlow.Modules.Identity.Domain.Entities;
using FluentAssertions;
using Xunit;

namespace ColaFlow.Modules.Identity.Domain.Tests.Entities;

public sealed class EmailVerificationTokenTests
{
    private readonly UserId _userId = UserId.CreateUnique();
    private const string TestTokenHash = "hashed_token_value";

    [Fact]
    public void Create_WithValidData_ShouldSucceed()
    {
        // Arrange
        var expiresAt = DateTime.UtcNow.AddHours(24);

        // Act
        var token = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);

        // Assert
        token.Should().NotBeNull();
        token.Id.Should().NotBe(Guid.Empty);
        token.UserId.Should().Be(_userId);
        token.TokenHash.Should().Be(TestTokenHash);
        token.ExpiresAt.Should().Be(expiresAt);
        token.VerifiedAt.Should().BeNull();
        token.CreatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
        token.IsExpired.Should().BeFalse();
        token.IsVerified.Should().BeFalse();
        token.IsValid.Should().BeTrue();
    }

    [Fact]
    public void IsExpired_WithFutureExpiration_ShouldReturnFalse()
    {
        // Arrange
        var expiresAt = DateTime.UtcNow.AddHours(24);
        var token = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);

        // Act & Assert
        token.IsExpired.Should().BeFalse();
    }

    [Fact]
    public void IsExpired_WithPastExpiration_ShouldReturnTrue()
    {
        // Arrange
        var expiresAt = DateTime.UtcNow.AddHours(-1);
        var token = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);

        // Act & Assert
        token.IsExpired.Should().BeTrue();
    }

    [Fact]
    public void IsVerified_WhenNotVerified_ShouldReturnFalse()
    {
        // Arrange
        var expiresAt = DateTime.UtcNow.AddHours(24);
        var token = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);

        // Act & Assert
        token.IsVerified.Should().BeFalse();
    }

    [Fact]
    public void IsVerified_WhenVerified_ShouldReturnTrue()
    {
        // Arrange
        var expiresAt = DateTime.UtcNow.AddHours(24);
        var token = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);
        token.MarkAsVerified();

        // Act & Assert
        token.IsVerified.Should().BeTrue();
    }

    [Fact]
    public void IsValid_WithValidToken_ShouldReturnTrue()
    {
        // Arrange
        var expiresAt = DateTime.UtcNow.AddHours(24);
        var token = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);

        // Act & Assert
        token.IsValid.Should().BeTrue();
    }

    [Fact]
    public void IsValid_WithExpiredToken_ShouldReturnFalse()
    {
        // Arrange
        var expiresAt = DateTime.UtcNow.AddHours(-1);
        var token = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);

        // Act & Assert
        token.IsValid.Should().BeFalse();
    }

    [Fact]
    public void IsValid_WithVerifiedToken_ShouldReturnFalse()
    {
        // Arrange
        var expiresAt = DateTime.UtcNow.AddHours(24);
        var token = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);
        token.MarkAsVerified();

        // Act & Assert
        token.IsValid.Should().BeFalse();
    }

    [Fact]
    public void MarkAsVerified_WithValidToken_ShouldSetVerifiedAt()
    {
        // Arrange
        var expiresAt = DateTime.UtcNow.AddHours(24);
        var token = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);

        // Act
        token.MarkAsVerified();

        // Assert
        token.VerifiedAt.Should().NotBeNull();
        token.VerifiedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
        token.IsVerified.Should().BeTrue();
        token.IsValid.Should().BeFalse();
    }

    [Fact]
    public void MarkAsVerified_WithExpiredToken_ShouldThrowException()
    {
        // Arrange
        var expiresAt = DateTime.UtcNow.AddHours(-1);
        var token = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);

        // Act
        var act = () => token.MarkAsVerified();

        // Assert
        act.Should().Throw<InvalidOperationException>()
            .WithMessage("*Token is not valid for verification*");
    }

    [Fact]
    public void MarkAsVerified_WithAlreadyVerifiedToken_ShouldThrowException()
    {
        // Arrange
        var expiresAt = DateTime.UtcNow.AddHours(24);
        var token = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);
        token.MarkAsVerified();

        // Act
        var act = () => token.MarkAsVerified();

        // Assert
        act.Should().Throw<InvalidOperationException>()
            .WithMessage("*Token is not valid for verification*");
    }
}
