using System.Linq;
using System.Text.RegularExpressions;
using Xunit;

namespace TeslaConsole.DiffTests
{
    /// <summary>
    /// Structural equivalence: every public type and public member exposed by the
    /// original TeslaConsole.exe must also be exposed, with the same signature, by the
    /// recovered build. This is a strong "same program" contract that catches any
    /// dropped, renamed, or re-signatured member introduced during reconstruction.
    ///
    /// The enumeration runs inside each assembly's own AppDomain (see DifferentialFixture)
    /// because the two files share identical assembly identity and cannot coexist in one
    /// domain. Compiler-generated members are excluded — the README notes those legitimately
    /// differ between a decompilation and the lost original sources.
    /// </summary>
    public class PublicApiSurfaceTests : IClassFixture<DifferentialFixture>
    {
        private readonly DifferentialFixture _fx;

        public PublicApiSurfaceTests(DifferentialFixture fx) => _fx = fx;

        [Fact]
        public void Recovered_Exposes_All_Original_Public_Types()
        {
            var original = _fx.Original.GetPublicTypeNames();
            var recovered = _fx.Recovered.GetPublicTypeNames();

            // Sanity: the original actually yielded a non-trivial surface to compare.
            Assert.True(original.Length > 20,
                "Only " + original.Length + " public types enumerated from the original; " +
                "dependency resolution likely failed.");

            var missing = original.Except(recovered).OrderBy(s => s).ToArray();
            Assert.True(missing.Length == 0,
                "Public types present in the original but missing from the recovered build:\n  " +
                string.Join("\n  ", missing));
        }

        // The recovered build is intentionally a newer assembly version than the
        // original baseline. A member signature that references a TeslaConsole type as a
        // generic argument embeds that type's assembly-qualified name — including the
        // version — so strip Version= stamps before comparing. We compare type NAMES,
        // not versions.
        private static string StripVersion(string sig)
            => Regex.Replace(sig, @", Version=\d+\.\d+\.\d+\.\d+", "");

        [Fact]
        public void Recovered_Exposes_All_Original_Public_Members()
        {
            var original = _fx.Original.GetPublicMemberSignatures().Select(StripVersion).ToArray();
            var recovered = _fx.Recovered.GetPublicMemberSignatures().Select(StripVersion).ToArray();

            Assert.True(original.Length > 100,
                "Only " + original.Length + " public members enumerated from the original; " +
                "dependency resolution likely failed.");

            var missing = original.Except(recovered).OrderBy(s => s).ToArray();
            Assert.True(missing.Length == 0,
                missing.Length + " public member(s) present in the original but missing from the recovered build:\n  " +
                string.Join("\n  ", missing.Take(80)));
        }
    }
}
