using NetArchTest.Rules;
using ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate;
using ColaFlow.Shared.Kernel.Common;

namespace ColaFlow.ArchitectureTests;

/// <summary>
/// Architecture tests to enforce module boundaries and dependencies.
/// These tests ensure the modular monolith architecture is maintained.
/// </summary>
public class ModuleBoundaryTests
{
    private const string PM_DOMAIN = "ColaFlow.Modules.ProjectManagement.Domain";
    private const string PM_APPLICATION = "ColaFlow.Modules.ProjectManagement.Application";
    private const string PM_INFRASTRUCTURE = "ColaFlow.Modules.ProjectManagement.Infrastructure";
    private const string SHARED_KERNEL = "ColaFlow.Shared.Kernel";

    [Fact]
    public void Domain_Should_Not_Depend_On_Application()
    {
        // Arrange
        var assembly = typeof(Project).Assembly;

        // Act
        var result = Types.InAssembly(assembly)
            .That()
            .ResideInNamespace(PM_DOMAIN)
            .ShouldNot()
            .HaveDependencyOn(PM_APPLICATION)
            .GetResult();

        // Assert
        Assert.True(result.IsSuccessful,
            $"Domain layer should not depend on Application layer. Violations: {string.Join(", ", result.FailingTypeNames ?? Array.Empty<string>())}");
    }

    [Fact]
    public void Domain_Should_Not_Depend_On_Infrastructure()
    {
        // Arrange
        var assembly = typeof(Project).Assembly;

        // Act
        var result = Types.InAssembly(assembly)
            .That()
            .ResideInNamespace(PM_DOMAIN)
            .ShouldNot()
            .HaveDependencyOn(PM_INFRASTRUCTURE)
            .GetResult();

        // Assert
        Assert.True(result.IsSuccessful,
            $"Domain layer should not depend on Infrastructure layer. Violations: {string.Join(", ", result.FailingTypeNames ?? Array.Empty<string>())}");
    }

    [Fact]
    public void Domain_Can_Only_Depend_On_Shared_Kernel()
    {
        // Arrange
        var assembly = typeof(Project).Assembly;

        // Act
        var result = Types.InAssembly(assembly)
            .That()
            .ResideInNamespace(PM_DOMAIN)
            .And()
            .HaveDependencyOnAny("ColaFlow")
            .Should()
            .HaveDependencyOn(SHARED_KERNEL)
            .Or()
            .ResideInNamespace(PM_DOMAIN)
            .GetResult();

        // Assert
        Assert.True(result.IsSuccessful,
            $"Domain layer should only depend on Shared.Kernel. Violations: {string.Join(", ", result.FailingTypeNames ?? Array.Empty<string>())}");
    }

    [Fact]
    public void Application_Should_Not_Depend_On_Infrastructure()
    {
        // Arrange
        // Note: Application assembly might not have types yet, so we'll skip this test for now
        // This test will be enabled when Application layer is implemented

        Assert.True(true, "Skipped - Application layer not yet implemented");
    }

    [Fact]
    public void Project_Should_Be_AggregateRoot()
    {
        // Arrange
        var assembly = typeof(Project).Assembly;

        // Act
        var result = Types.InAssembly(assembly)
            .That()
            .HaveName("Project")
            .Should()
            .Inherit(typeof(AggregateRoot))
            .GetResult();

        // Assert
        Assert.True(result.IsSuccessful,
            $"Project should inherit from AggregateRoot. Violations: {string.Join(", ", result.FailingTypeNames ?? Array.Empty<string>())}");
    }

    [Fact]
    public void Entities_Should_Inherit_From_Entity()
    {
        // Arrange
        var assembly = typeof(Project).Assembly;

        // Act
        var result = Types.InAssembly(assembly)
            .That()
            .ResideInNamespace($"{PM_DOMAIN}.Aggregates")
            .And()
            .AreClasses()
            .And()
            .AreNotAbstract()
            .Should()
            .Inherit(typeof(Entity))
            .GetResult();

        // Assert
        Assert.True(result.IsSuccessful,
            $"All entity classes should inherit from Entity. Violations: {string.Join(", ", result.FailingTypeNames ?? Array.Empty<string>())}");
    }

    [Fact]
    public void Domain_Events_Should_Be_Records()
    {
        // Arrange
        var assembly = typeof(Project).Assembly;

        // Act
        var types = Types.InAssembly(assembly)
            .That()
            .ResideInNamespace($"{PM_DOMAIN}.Events")
            .And()
            .DoNotHaveName("DomainEvent") // Exclude base class
            .GetTypes();

        // Assert - Check if types are records (records are sealed and inherit from a specific base)
        foreach (var type in types)
        {
            Assert.True(type.IsClass && type.IsSealed,
                $"Event {type.Name} should be a record (sealed class)");
        }
    }

    [Fact]
    public void ValueObjects_Should_Be_Immutable()
    {
        // Arrange
        var assembly = typeof(Project).Assembly;

        // Act
        var result = Types.InAssembly(assembly)
            .That()
            .ResideInNamespace($"{PM_DOMAIN}.ValueObjects")
            .And()
            .AreClasses()
            .And()
            .AreNotAbstract()
            .Should()
            .BeSealed()
            .GetResult();

        // Assert
        Assert.True(result.IsSuccessful,
            $"All value objects should be sealed (immutable). Violations: {string.Join(", ", result.FailingTypeNames ?? Array.Empty<string>())}");
    }
}
