using FluentAssertions;
using StorageService.Domain.AggregatesModel.FileShareAggregate;
using Xunit;

// EN: Alias to avoid collision with System.IO.FileShare
// VI: Alias để tránh xung đột với System.IO.FileShare
using DomainFileShare = StorageService.Domain.AggregatesModel.FileShareAggregate.FileShare;

namespace StorageService.UnitTests.Domain;
/// <summary>
/// EN: Tests for FileShare aggregate root.
/// VI: Kiểm thử cho aggregate root FileShare.
/// </summary>
public class FileShareTests
{
    private static readonly Guid ValidFileId = Guid.NewGuid();
    private const string ValidSharedBy = "user-123";
    private const SharePermission ValidPermission = SharePermission.Download;

    #region Constructor Tests

    [Fact]
    public void Constructor_ValidParams_GeneratesUniqueShareToken()
    {
        // Arrange & Act
        var share1 = CreateValidFileShare();
        var share2 = CreateValidFileShare();

        // Assert
        share1.ShareToken.Should().NotBeNullOrEmpty();
        share2.ShareToken.Should().NotBeNullOrEmpty();
        share1.ShareToken.Should().NotBe(share2.ShareToken);
    }

    [Fact]
    public void Constructor_ValidParams_SetsCorrectProperties()
    {
        // Arrange & Act
        var expiresAt = DateTime.UtcNow.AddDays(7);
        var share = new DomainFileShare(
            ValidFileId,
            ValidSharedBy,
            SharePermission.Edit,
            sharedWith: "user-456",
            password: null,
            expiresAt: expiresAt,
            maxDownloads: 10);

        // Assert
        share.FileId.Should().Be(ValidFileId);
        share.SharedBy.Should().Be(ValidSharedBy);
        share.SharedWith.Should().Be("user-456");
        share.Permission.Should().Be(SharePermission.Edit);
        share.ExpiresAt.Should().Be(expiresAt);
        share.MaxDownloads.Should().Be(10);
        share.DownloadCount.Should().Be(0);
        share.Status.Should().Be(FileShareStatus.Active);
        share.CreatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
    }

    [Fact]
    public void Constructor_WithPassword_HashesPassword()
    {
        // Arrange
        var password = "SecureP@ssw0rd!";

        // Act
        var share = new DomainFileShare(
            ValidFileId,
            ValidSharedBy,
            ValidPermission,
            password: password);

        // Assert
        share.PasswordHash.Should().NotBeNullOrEmpty();
        share.PasswordHash.Should().NotBe(password); // Ensure hashed
        share.PasswordHash!.Length.Should().BeGreaterThan(password.Length); // Hash includes salt
    }

    [Fact]
    public void Constructor_WithoutPassword_PasswordHashIsNull()
    {
        // Arrange & Act
        var share = CreateValidFileShare();

        // Assert
        share.PasswordHash.Should().BeNull();
    }

    #endregion

    #region IsValid Tests

    [Fact]
    public void IsValid_ActiveShare_ReturnsTrue()
    {
        // Arrange
        var share = CreateValidFileShare();

        // Act
        var result = share.IsValid();

        // Assert
        result.Should().BeTrue();
        share.Status.Should().Be(FileShareStatus.Active);
    }

    [Fact]
    public void IsValid_RevokedShare_ReturnsFalse()
    {
        // Arrange
        var share = CreateValidFileShare();
        share.Revoke();

        // Act
        var result = share.IsValid();

        // Assert
        result.Should().BeFalse();
        share.Status.Should().Be(FileShareStatus.Revoked);
    }

    [Fact]
    public void IsValid_ExpiredShare_ReturnsFalseAndUpdatesStatus()
    {
        // Arrange
        var share = new DomainFileShare(
            ValidFileId,
            ValidSharedBy,
            ValidPermission,
            expiresAt: DateTime.UtcNow.AddMilliseconds(-100)); // Already expired

        // Act
        var result = share.IsValid();

        // Assert
        result.Should().BeFalse();
        share.Status.Should().Be(FileShareStatus.Expired);
    }

    [Fact]
    public void IsValid_LimitReached_ReturnsFalseAndUpdatesStatus()
    {
        // Arrange
        var share = new DomainFileShare(
            ValidFileId,
            ValidSharedBy,
            ValidPermission,
            maxDownloads: 2);
        
        // Simulate reaching limit
        share.IncrementDownloadCount();
        share.IncrementDownloadCount();

        // Act
        var result = share.IsValid();

        // Assert
        result.Should().BeFalse();
        share.Status.Should().Be(FileShareStatus.LimitReached);
    }

    [Fact]
    public void IsValid_WithFutureExpiration_ReturnsTrue()
    {
        // Arrange
        var share = new DomainFileShare(
            ValidFileId,
            ValidSharedBy,
            ValidPermission,
            expiresAt: DateTime.UtcNow.AddDays(7));

        // Act
        var result = share.IsValid();

        // Assert
        result.Should().BeTrue();
    }

    [Fact]
    public void IsValid_UnderDownloadLimit_ReturnsTrue()
    {
        // Arrange
        var share = new DomainFileShare(
            ValidFileId,
            ValidSharedBy,
            ValidPermission,
            maxDownloads: 5);
        share.IncrementDownloadCount(); // 1/5

        // Act
        var result = share.IsValid();

        // Assert
        result.Should().BeTrue();
        share.DownloadCount.Should().Be(1);
    }

    #endregion

    #region ValidatePassword Tests

    [Fact]
    public void ValidatePassword_NoPasswordRequired_ReturnsTrue()
    {
        // Arrange
        var share = CreateValidFileShare(); // No password

        // Act
        var result = share.ValidatePassword(null);

        // Assert
        result.Should().BeTrue();
    }

    [Fact]
    public void ValidatePassword_CorrectPassword_ReturnsTrue()
    {
        // Arrange
        var password = "MySecretP@ss123";
        var share = new DomainFileShare(
            ValidFileId,
            ValidSharedBy,
            ValidPermission,
            password: password);

        // Act
        var result = share.ValidatePassword(password);

        // Assert
        result.Should().BeTrue();
    }

    [Fact]
    public void ValidatePassword_WrongPassword_ReturnsFalse()
    {
        // Arrange
        var share = new DomainFileShare(
            ValidFileId,
            ValidSharedBy,
            ValidPermission,
            password: "CorrectPassword");

        // Act
        var result = share.ValidatePassword("WrongPassword");

        // Assert
        result.Should().BeFalse();
    }

    [Fact]
    public void ValidatePassword_NullPasswordWhenRequired_ReturnsFalse()
    {
        // Arrange
        var share = new DomainFileShare(
            ValidFileId,
            ValidSharedBy,
            ValidPermission,
            password: "SomePassword");

        // Act
        var result = share.ValidatePassword(null);

        // Assert
        result.Should().BeFalse();
    }

    [Fact]
    public void ValidatePassword_EmptyPasswordWhenRequired_ReturnsFalse()
    {
        // Arrange
        var share = new DomainFileShare(
            ValidFileId,
            ValidSharedBy,
            ValidPermission,
            password: "SomePassword");

        // Act
        var result = share.ValidatePassword(string.Empty);

        // Assert
        result.Should().BeFalse();
    }

    #endregion

    #region IncrementDownloadCount Tests

    [Fact]
    public void IncrementDownloadCount_UnderLimit_IncreasesCount()
    {
        // Arrange
        var share = new DomainFileShare(
            ValidFileId,
            ValidSharedBy,
            ValidPermission,
            maxDownloads: 10);

        // Act
        share.IncrementDownloadCount();
        share.IncrementDownloadCount();
        share.IncrementDownloadCount();

        // Assert
        share.DownloadCount.Should().Be(3);
        share.Status.Should().Be(FileShareStatus.Active);
    }

    [Fact]
    public void IncrementDownloadCount_ReachesLimit_UpdatesStatus()
    {
        // Arrange
        var share = new DomainFileShare(
            ValidFileId,
            ValidSharedBy,
            ValidPermission,
            maxDownloads: 2);

        // Act
        share.IncrementDownloadCount(); // 1
        share.IncrementDownloadCount(); // 2 - limit reached

        // Assert
        share.DownloadCount.Should().Be(2);
        share.Status.Should().Be(FileShareStatus.LimitReached);
    }

    [Fact]
    public void IncrementDownloadCount_NoLimit_ContinuesIncrementing()
    {
        // Arrange
        var share = CreateValidFileShare(); // No max downloads

        // Act
        for (int i = 0; i < 100; i++)
        {
            share.IncrementDownloadCount();
        }

        // Assert
        share.DownloadCount.Should().Be(100);
        share.Status.Should().Be(FileShareStatus.Active);
    }

    #endregion

    #region Revoke Tests

    [Fact]
    public void Revoke_SetsStatusAndRevokedAt()
    {
        // Arrange
        var share = CreateValidFileShare();

        // Act
        share.Revoke();

        // Assert
        share.Status.Should().Be(FileShareStatus.Revoked);
        share.RevokedAt.Should().NotBeNull();
        share.RevokedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
    }

    [Fact]
    public void Revoke_AlreadyRevoked_UpdatesRevokedAt()
    {
        // Arrange
        var share = CreateValidFileShare();
        share.Revoke();
        var firstRevokedAt = share.RevokedAt;

        // Act
        Thread.Sleep(10);
        share.Revoke();

        // Assert
        share.Status.Should().Be(FileShareStatus.Revoked);
        // Note: Current implementation updates RevokedAt each time
    }

    #endregion

    #region Helper Methods

    private static DomainFileShare CreateValidFileShare()
    {
        return new DomainFileShare(
            ValidFileId,
            ValidSharedBy,
            ValidPermission);
    }

    #endregion
}
