using System;
using Xunit;

namespace TeslaConsole.DiffTests
{
    /// <summary>
    /// For every input below, the original and the recovered build must return the
    /// exact same result. Each [Theory] case runs the same method in both assemblies
    /// (in their own AppDomains) and asserts byte-for-byte string equality.
    /// </summary>
    public class BehavioralEquivalenceTests : IClassFixture<DifferentialFixture>
    {
        private readonly DifferentialFixture _fx;

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

        private void AssertSame(string caseName, params string[] args)
        {
            var (original, recovered) = _fx.RunBoth(caseName, args);
            Assert.Equal(original, recovered);
            // Guard against both silently throwing the same way for a case that should succeed.
            Assert.False(original == null,
                $"{caseName}({string.Join(",", args)}) returned null from both assemblies");
        }

        [Fact]
        public void Harness_Distinguishes_Different_Outputs()
        {
            // Negative control: a passing suite must be capable of seeing a difference.
            // Same method, different inputs, must yield different results in each assembly.
            string ninetySec = (90L * TimeSpan.TicksPerSecond).ToString();
            var zero = _fx.Original.Run("GetTimeString", new[] { "0" });
            var ninety = _fx.Original.Run("GetTimeString", new[] { ninetySec });
            Assert.NotEqual(zero, ninety);

            var rZero = _fx.Recovered.Run("GetTimeString", new[] { "0" });
            var rNinety = _fx.Recovered.Run("GetTimeString", new[] { ninetySec });
            Assert.NotEqual(rZero, rNinety);
        }

        [Fact]
        public void Loaded_Assemblies_Are_The_Expected_Builds()
        {
            // The original is the untouched 4.11.3.37076 baseline; the recovered build is
            // the modernized line, intentionally bumped to 4.11.4.x (same assembly name,
            // new version). Both must be TeslaConsole — guards against testing the wrong files.
            Assert.Contains("TeslaConsole", _fx.Original.AssemblyFullName);
            Assert.Contains("TeslaConsole", _fx.Recovered.AssemblyFullName);
            Assert.Contains("4.11.3.37076", _fx.Original.AssemblyFullName);
            Assert.Contains("4.11.4.1", _fx.Recovered.AssemblyFullName);
        }

        // ---- RPStrings.GetTimeString: mm:ss formatting with 0.5s rounding ----
        [Theory]
        [InlineData(0L)]                  // 0s -> 00:00
        [InlineData(4_000_000L)]          // 0.4s -> rounds down
        [InlineData(5_000_000L)]          // 0.5s -> rounds up to 1s
        [InlineData(6_000_000L)]          // 0.6s
        [InlineData(595_000_000L)]        // 59.5s -> rolls to 01:00
        [InlineData(600_000_000L)]        // 60s -> 01:00
        [InlineData(900_000_000L)]        // 90s -> 01:30
        [InlineData(5_990_000_000L)]      // 599s
        [InlineData(6_000_000_000L)]      // 600s -> 10:00
        [InlineData(36_000_000_000L)]     // 3600s -> 60:00
        [InlineData(-50_000_000L)]        // negative
        [InlineData(864_000_000_000L)]    // 1 day
        public void GetTimeString_Matches(long ticks)
            => AssertSame("GetTimeString", ticks.ToString());

        // ---- HostTypeHelper.Parse(...).ToString() including invalid input ----
        [Theory]
        [InlineData("Game Machine")]
        [InlineData("Mission Review")]
        [InlineData("Console")]
        [InlineData("GameMachineHostType")]
        [InlineData("MissionReviewHostType")]
        [InlineData("ConsoleHostType")]
        [InlineData("console")]           // case-sensitive: should throw in both
        [InlineData("Nonsense")]          // invalid: ArgumentException in both
        [InlineData("")]                  // empty
        public void HostTypeParse_Matches(string input)
            => AssertSame("HostTypeParse", input);

        // ---- PlasmaBitmaps.ConvertBitmap: 1bpp packing of a known pixel pattern ----
        [Theory]
        [InlineData(8, 8, 0)]
        [InlineData(8, 8, 1)]
        [InlineData(8, 8, 2)]
        [InlineData(16, 16, 0)]
        [InlineData(16, 16, 3)]
        [InlineData(32, 8, 5)]
        [InlineData(7, 8, 0)]             // width not multiple of 8 -> throws in both
        [InlineData(8, 7, 0)]             // height not multiple of 8 -> throws in both
        public void ConvertBitmap_Matches(int width, int height, int seed)
            => AssertSame("ConvertBitmap", width.ToString(), height.ToString(), seed.ToString());

        // ---- PlasmaBitmaps.GenerateString: full GDI text -> 1bpp pipeline ----
        [Theory]
        [InlineData(128, 32, "Microsoft Sans Serif", "RED")]
        [InlineData(64, 16, "Microsoft Sans Serif", "RED")]
        [InlineData(128, 32, "Microsoft Sans Serif", "1")]
        [InlineData(128, 32, "Microsoft Sans Serif", "ABCDEFGH")]
        [InlineData(128, 32, "Microsoft Sans Serif", "")]
        [InlineData(128, 32, "Arial", "GO")]
        public void GenerateString_Matches(int width, int height, string font, string text)
            => AssertSame("GenerateString", width.ToString(), height.ToString(), font, text);

        // ---- RedPlanet RPMap / RPVehicle XML parsing ----
        [Theory]
        [InlineData("<map key=\"m1\" name=\"Blade's Edge\" image=\"images/x.bmp\" />")]
        [InlineData("<map key=\"m2\" name=\"Lyz's Lane\" />")]                 // no image attr
        [InlineData("<map key=\"\" name=\"\" />")]                              // empty values
        public void RPMapParse_Matches(string xml)
            => AssertSame("RPMapParse", xml);

        [Theory]
        [InlineData("<vehicle key=\"v1\" name=\"Armadillo\" image=\"images/a.bmp\" />")]
        [InlineData("<vehicle key=\"v2\" name=\"Skeeter\" />")]
        public void RPVehicleParse_Matches(string xml)
            => AssertSame("RPVehicleParse", xml);

        // ---- SiteManagement well-known application GUIDs (constants) ----
        [Theory]
        [InlineData("RPAppGuid")]
        [InlineData("RPMRAppGuid")]
        [InlineData("RPLCAppGuid")]
        [InlineData("MW4AppGuid")]
        [InlineData("MW4LCAppGuid")]
        public void SiteManagementGuid_Matches(string fieldName)
            => AssertSame("SiteManagementGuid", fieldName);

        // ---- Tuple.Create<int,string> generic factory ----
        [Theory]
        [InlineData("1", "alpha")]
        [InlineData("-7", "")]
        public void TupleCreate_Matches(string a, string b)
            => AssertSame("TupleCreate", a, b);
    }
}
