using NUnit.Framework;
using VMdemo.Core;
using VMdemo.Simulation;

namespace VMdemo.Tests.EditMode
{
    public class Step1CoreModelTests
    {
        [Test]
        public void TryValidateAndBuildDerived_ValidConfig_ReturnsExpectedDerivedValues()
        {
            var config = new SimulationConfig
            {
                vaBits = 48,
                pageSizeKB = 4,
                physicalMemoryMB = 64,
                tlbEntries = 16,
                accessCount = 1000,
                pageFaultPenalty = 100
            };

            var ok = ConfigValidator.TryValidateAndBuildDerived(config, out var derived, out var error);

            Assert.IsTrue(ok, error);
            Assert.That(error, Is.Empty);
            Assert.That(derived.PageSizeBytes, Is.EqualTo(4096UL));
            Assert.That(derived.PhysicalMemoryBytes, Is.EqualTo(67108864UL));
            Assert.That(derived.OffsetBits, Is.EqualTo(12));
            Assert.That(derived.VpnBits, Is.EqualTo(36));
            Assert.That(derived.FrameCount, Is.EqualTo(16384UL));
        }

        [Test]
        public void TryValidate_InvalidVaBits_FailsWithReadableMessage()
        {
            var config = new SimulationConfig
            {
                vaBits = 40,
                pageSizeKB = 4,
                physicalMemoryMB = 64,
                tlbEntries = 16,
                accessCount = 10,
                pageFaultPenalty = 100
            };

            var ok = ConfigValidator.TryValidate(config, out var error);

            Assert.IsFalse(ok);
            Assert.That(error, Does.Contain("vaBits"));
        }

        [Test]
        public void TryValidate_PageSizeNotPowerOfTwo_FailsWithReadableMessage()
        {
            var config = new SimulationConfig
            {
                vaBits = 32,
                pageSizeKB = 6,
                physicalMemoryMB = 64,
                tlbEntries = 16,
                accessCount = 10,
                pageFaultPenalty = 100
            };

            var ok = ConfigValidator.TryValidate(config, out var error);

            Assert.IsFalse(ok);
            Assert.That(error, Does.Contain("pageSizeKB"));
        }

        [Test]
        public void TryValidate_OffsetBitsGreaterOrEqualVaBits_Fails()
        {
            var config = new SimulationConfig
            {
                vaBits = 32,
                pageSizeKB = 4 * 1024 * 1024,
                physicalMemoryMB = 8192,
                tlbEntries = 16,
                accessCount = 10,
                pageFaultPenalty = 100
            };

            var ok = ConfigValidator.TryValidate(config, out var error);

            Assert.IsFalse(ok);
            Assert.That(error, Does.Contain("offsetBits"));
        }

        [Test]
        public void TryValidate_FrameCountLessThanOne_Fails()
        {
            var config = new SimulationConfig
            {
                vaBits = 32,
                pageSizeKB = 2048,
                physicalMemoryMB = 1,
                tlbEntries = 16,
                accessCount = 10,
                pageFaultPenalty = 100
            };

            var ok = ConfigValidator.TryValidate(config, out var error);

            Assert.IsFalse(ok);
            Assert.That(error, Does.Contain("frameCount"));
        }

        [Test]
        public void SimulationState_Reset_ClearsAllStepState()
        {
            var state = new SimulationState
            {
                CurrentRound = 7,
                CurrentVirtualAddress = 123456UL,
                CurrentAddressParts = new AddressParts(10UL, 20UL, 3UL, 7UL),
                IsPageFault = true,
                IsTlbHit = true,
                IsPageTableHit = true,
                CurrentCost = 999
            };

            state.Reset();

            Assert.That(state.CurrentRound, Is.EqualTo(0));
            Assert.That(state.CurrentVirtualAddress, Is.EqualTo(0UL));
            Assert.That(state.CurrentAddressParts.Vpn, Is.EqualTo(0UL));
            Assert.That(state.CurrentAddressParts.Offset, Is.EqualTo(0UL));
            Assert.That(state.CurrentAddressParts.L1Index, Is.EqualTo(0UL));
            Assert.That(state.CurrentAddressParts.L2Index, Is.EqualTo(0UL));
            Assert.IsFalse(state.IsPageFault);
            Assert.IsFalse(state.IsTlbHit);
            Assert.IsFalse(state.IsPageTableHit);
            Assert.That(state.CurrentCost, Is.EqualTo(0));
        }
    }
}
