using System.Reflection;
using Shouldly;
using Xunit;

namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests;

/// <summary>
///     Asserts that <c>Core.Abstractions</c> stays a true contract project — it must not depend on
///     any implementation type, any other OtOpcUa project, or anything beyond BCL + System types.
///     Per <c>docs/v2/plan.md</c> decision #59 (Core.Abstractions internal-only for now; design as
///     if public to minimize churn later).
/// </summary>
public sealed class InterfaceIndependenceTests
{
    private static readonly Assembly Assembly = typeof(IDriver).Assembly;

    /// <summary>Verifies that the assembly has no references outside the BCL.</summary>
    [Fact]
    public void Assembly_HasNoReferencesOutsideBcl()
    {
        // Allowed reference assembly name prefixes — BCL + the assembly itself.
        var allowed = new[]
        {
            "System",
            "Microsoft.Win32",
            "netstandard",
            "mscorlib",
            "ZB.MOM.WW.OtOpcUa.Core.Abstractions",
        };

        var referenced = Assembly.GetReferencedAssemblies();
        var disallowed = referenced
            .Where(r => !allowed.Any(a => r.Name!.StartsWith(a, StringComparison.Ordinal)))
            .ToList();

        disallowed.ShouldBeEmpty(
            $"Core.Abstractions must reference only BCL/System assemblies. " +
            $"Found disallowed references: {string.Join(", ", disallowed.Select(a => a.Name))}");
    }

    /// <summary>Verifies that all public types live in the root namespace.</summary>
    [Fact]
    public void AllPublicTypes_LiveInRootNamespace()
    {
        // Per the decision-#59 "design as if public" rule — no nested sub-namespaces; one flat surface.
        var publicTypes = Assembly.GetExportedTypes();
        var nonRoot = publicTypes
            .Where(t => t.Namespace != "ZB.MOM.WW.OtOpcUa.Core.Abstractions")
            .ToList();

        nonRoot.ShouldBeEmpty(
            $"Core.Abstractions should expose all public types in the root namespace. " +
            $"Found types in other namespaces: {string.Join(", ", nonRoot.Select(t => $"{t.FullName}"))}");
    }

    /// <summary>Verifies that every capability interface is public.</summary>
    /// <param name="type">The interface type to check.</param>
    [Theory]
    [InlineData(typeof(IDriver))]
    [InlineData(typeof(ITagDiscovery))]
    [InlineData(typeof(IReadable))]
    [InlineData(typeof(IWritable))]
    [InlineData(typeof(ISubscribable))]
    [InlineData(typeof(IAlarmSource))]
    [InlineData(typeof(IHistoryProvider))]
    [InlineData(typeof(IRediscoverable))]
    [InlineData(typeof(IHostConnectivityProbe))]
    [InlineData(typeof(IDriverConfigEditor))]
    [InlineData(typeof(IAddressSpaceBuilder))]
    public void EveryCapabilityInterface_IsPublic(Type type)
    {
        type.IsPublic.ShouldBeTrue($"{type.Name} must be public — drivers in separate assemblies implement it.");
        type.IsInterface.ShouldBeTrue($"{type.Name} must be an interface, not a class.");
    }
}
