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

namespace StorageService.UnitTests.Domain;
/// <summary>
/// EN: Tests for Folder aggregate root.
/// VI: Kiểm thử cho aggregate root Folder.
/// </summary>
public class FolderTests
{
    private const string ValidUserId = "user-123";
    private const string ValidFolderName = "Documents";

    #region CreateRoot Tests

    [Fact]
    public void CreateRoot_ValidParams_CreatesRootFolder()
    {
        // Arrange & Act
        var folder = Folder.CreateRoot(ValidUserId, ValidFolderName);

        // Assert
        folder.Should().NotBeNull();
        folder.Id.Should().NotBeEmpty();
        folder.UserId.Should().Be(ValidUserId);
        folder.Name.Should().Be(ValidFolderName);
        folder.IsDeleted.Should().BeFalse();
    }

    [Fact]
    public void CreateRoot_SetsLevelToZero()
    {
        // Arrange & Act
        var folder = Folder.CreateRoot(ValidUserId, ValidFolderName);

        // Assert
        folder.Level.Should().Be(0);
    }

    [Fact]
    public void CreateRoot_SetsPathCorrectly()
    {
        // Arrange & Act
        var folder = Folder.CreateRoot(ValidUserId, ValidFolderName);

        // Assert
        folder.Path.Should().Be($"/{ValidFolderName}/");
    }

    [Fact]
    public void CreateRoot_SetsParentIdToNull()
    {
        // Arrange & Act
        var folder = Folder.CreateRoot(ValidUserId, ValidFolderName);

        // Assert
        folder.ParentId.Should().BeNull();
    }

    [Fact]
    public void CreateRoot_SetsCreatedAtAndUpdatedAt()
    {
        // Arrange & Act
        var beforeCreate = DateTime.UtcNow;
        var folder = Folder.CreateRoot(ValidUserId, ValidFolderName);

        // Assert
        folder.CreatedAt.Should().BeOnOrAfter(beforeCreate);
        folder.UpdatedAt.Should().BeOnOrAfter(beforeCreate);
        folder.CreatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
    }

    #endregion

    #region CreateChild Tests

    [Fact]
    public void CreateChild_InheritsUserId()
    {
        // Arrange
        var parentFolder = Folder.CreateRoot(ValidUserId, "Parent");

        // Act
        var childFolder = parentFolder.CreateChild("Child");

        // Assert
        childFolder.UserId.Should().Be(ValidUserId);
    }

    [Fact]
    public void CreateChild_SetsParentId()
    {
        // Arrange
        var parentFolder = Folder.CreateRoot(ValidUserId, "Parent");

        // Act
        var childFolder = parentFolder.CreateChild("Child");

        // Assert
        childFolder.ParentId.Should().Be(parentFolder.Id);
    }

    [Fact]
    public void CreateChild_IncrementsLevel()
    {
        // Arrange
        var rootFolder = Folder.CreateRoot(ValidUserId, "Root");

        // Act
        var level1 = rootFolder.CreateChild("Level1");
        var level2 = level1.CreateChild("Level2");
        var level3 = level2.CreateChild("Level3");

        // Assert
        rootFolder.Level.Should().Be(0);
        level1.Level.Should().Be(1);
        level2.Level.Should().Be(2);
        level3.Level.Should().Be(3);
    }

    [Fact]
    public void CreateChild_AppendsToPath()
    {
        // Arrange
        var rootFolder = Folder.CreateRoot(ValidUserId, "Root");

        // Act
        var childFolder = rootFolder.CreateChild("Child");
        var grandchildFolder = childFolder.CreateChild("Grandchild");

        // Assert
        rootFolder.Path.Should().Be("/Root/");
        childFolder.Path.Should().Be("/Root/Child/");
        grandchildFolder.Path.Should().Be("/Root/Child/Grandchild/");
    }

    [Fact]
    public void CreateChild_GeneratesUniqueId()
    {
        // Arrange
        var parentFolder = Folder.CreateRoot(ValidUserId, "Parent");

        // Act
        var child1 = parentFolder.CreateChild("Child1");
        var child2 = parentFolder.CreateChild("Child2");

        // Assert
        child1.Id.Should().NotBe(child2.Id);
        child1.Id.Should().NotBe(parentFolder.Id);
    }

    #endregion

    #region Rename Tests

    [Fact]
    public void Rename_ValidName_UpdatesNameAndPath()
    {
        // Arrange
        var folder = Folder.CreateRoot(ValidUserId, "OldName");
        var beforeRename = folder.UpdatedAt;

        // Act
        Thread.Sleep(10); // Ensure time difference
        folder.Rename("NewName");

        // Assert
        folder.Name.Should().Be("NewName");
        folder.Path.Should().Be("/NewName/");
        folder.UpdatedAt.Should().BeAfter(beforeRename);
    }

    [Fact]
    public void Rename_ChildFolder_UpdatesPathCorrectly()
    {
        // Arrange
        var rootFolder = Folder.CreateRoot(ValidUserId, "Root");
        var childFolder = rootFolder.CreateChild("OldChild");

        // Act
        childFolder.Rename("NewChild");

        // Assert
        childFolder.Name.Should().Be("NewChild");
        childFolder.Path.Should().Be("/Root/NewChild/");
    }

    [Fact]
    public void Rename_DeletedFolder_ThrowsException()
    {
        // Arrange
        var folder = Folder.CreateRoot(ValidUserId, "Folder");
        folder.Delete();

        // Act
        var act = () => folder.Rename("NewName");

        // Assert
        act.Should().Throw<InvalidOperationException>()
            .WithMessage("*deleted*");
    }

    #endregion

    #region MoveTo Tests

    [Fact]
    public void MoveTo_NullParent_BecomesRoot()
    {
        // Arrange
        var rootFolder = Folder.CreateRoot(ValidUserId, "Root");
        var childFolder = rootFolder.CreateChild("Child");

        // Act
        childFolder.MoveTo(null);

        // Assert
        childFolder.ParentId.Should().BeNull();
        childFolder.Level.Should().Be(0);
        childFolder.Path.Should().Be("/Child/");
    }

    [Fact]
    public void MoveTo_ValidParent_UpdatesHierarchy()
    {
        // Arrange
        var folder1 = Folder.CreateRoot(ValidUserId, "Folder1");
        var folder2 = Folder.CreateRoot(ValidUserId, "Folder2");
        var childFolder = folder1.CreateChild("Child");

        // Act
        childFolder.MoveTo(folder2);

        // Assert
        childFolder.ParentId.Should().Be(folder2.Id);
        childFolder.Level.Should().Be(1);
        childFolder.Path.Should().Be("/Folder2/Child/");
    }

    [Fact]
    public void MoveTo_DifferentUser_ThrowsException()
    {
        // Arrange
        var folder1 = Folder.CreateRoot("user-1", "Folder1");
        var folder2 = Folder.CreateRoot("user-2", "Folder2");

        // Act
        var act = () => folder1.MoveTo(folder2);

        // Assert
        act.Should().Throw<InvalidOperationException>()
            .WithMessage("*different user*");
    }

    [Fact]
    public void MoveTo_DeletedFolder_ThrowsException()
    {
        // Arrange
        var folder = Folder.CreateRoot(ValidUserId, "Folder");
        var targetFolder = Folder.CreateRoot(ValidUserId, "Target");
        folder.Delete();

        // Act
        var act = () => folder.MoveTo(targetFolder);

        // Assert
        act.Should().Throw<InvalidOperationException>()
            .WithMessage("*deleted*");
    }

    [Fact]
    public void MoveTo_UpdatesUpdatedAt()
    {
        // Arrange
        var folder1 = Folder.CreateRoot(ValidUserId, "Folder1");
        var folder2 = Folder.CreateRoot(ValidUserId, "Folder2");
        var beforeMove = folder1.UpdatedAt;

        // Act
        Thread.Sleep(10);
        folder1.MoveTo(folder2);

        // Assert
        folder1.UpdatedAt.Should().BeAfter(beforeMove);
    }

    #endregion

    #region Delete Tests

    [Fact]
    public void Delete_SetsIsDeletedAndDeletedAt()
    {
        // Arrange
        var folder = Folder.CreateRoot(ValidUserId, ValidFolderName);

        // Act
        folder.Delete();

        // Assert
        folder.IsDeleted.Should().BeTrue();
        folder.DeletedAt.Should().NotBeNull();
        folder.DeletedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
    }

    [Fact]
    public void Delete_AlreadyDeleted_DoesNotUpdateDeletedAt()
    {
        // Arrange
        var folder = Folder.CreateRoot(ValidUserId, ValidFolderName);
        folder.Delete();
        var firstDeletedAt = folder.DeletedAt;

        // Act
        Thread.Sleep(10);
        folder.Delete();

        // Assert
        folder.DeletedAt.Should().Be(firstDeletedAt);
    }

    [Fact]
    public void Delete_RootFolder_SetsDeleted()
    {
        // Arrange
        var folder = Folder.CreateRoot(ValidUserId, "Root");

        // Act
        folder.Delete();

        // Assert
        folder.IsDeleted.Should().BeTrue();
    }

    [Fact]
    public void Delete_ChildFolder_SetsDeleted()
    {
        // Arrange
        var rootFolder = Folder.CreateRoot(ValidUserId, "Root");
        var childFolder = rootFolder.CreateChild("Child");

        // Act
        childFolder.Delete();

        // Assert
        childFolder.IsDeleted.Should().BeTrue();
        rootFolder.IsDeleted.Should().BeFalse(); // Parent should not be affected
    }

    #endregion
}
