using StandardScene.Magnetic.Protocol;
using StandardScene.Magnetic.Tasking;
using System.Collections.Generic;
using Xunit;

namespace StandardScene.Magnetic.Tests.Tasking
{
    public class Fass2ControlAreaGateTests
    {
        [Fact]
        public void Resolve_WithoutArea_UsesCurrentSite()
        {
            var info = Fass2ControlAreaGate.Resolve(new Fass2SiteActionData(), 17);

            Assert.Equal(new[] { 17 }, info.AreaSiteIds);
            Assert.Equal("17", info.AreaId);
            Assert.Equal(1, info.Capacity);
            Assert.Equal(Fass2TaskBuilder.StartStopControlStart, info.ReleaseStartStop);
        }

        [Fact]
        public void Resolve_ParsesSiteArray_AndReleasePass()
        {
            var info = Fass2ControlAreaGate.Resolve(new Fass2SiteActionData
            {
                ControlArea = "[10, 11, 12]",
                ControlCapacity = 2,
                ControlReleaseStartStop = Fass2TaskBuilder.StartStopPass
            }, 8);

            Assert.Equal(new[] { 10, 11, 12 }, info.AreaSiteIds);
            Assert.Equal("10,11,12", info.AreaId);
            Assert.Equal(2, info.Capacity);
            Assert.Equal(Fass2TaskBuilder.StartStopPass, info.ReleaseStartStop);
        }

        [Fact]
        public void ParseSiteIds_CommaAndSemicolon()
        {
            var ids = Fass2ControlAreaGate.ParseSiteIds("10;11|12", 8);
            Assert.Equal(new[] { 10, 11, 12 }, ids);
        }

        [Fact]
        public void LimitWindow_StopsAtFirstUnreleasedControlStop()
        {
            var nodes = new[]
            {
                Node(1, Fass2TaskBuilder.StartStopPass),
                Node(2, Fass2TaskBuilder.StartStopControlStop),
                Node(3, Fass2TaskBuilder.StartStopPass),
                Node(4, Fass2TaskBuilder.StartStopPass)
            };

            var count = Fass2ControlAreaGate.LimitWindowCount(nodes, 0, 4, releasedIndex: -1);

            Assert.Equal(2, count);
        }

        [Fact]
        public void LimitWindow_AfterRelease_IncludesFollowingNodes()
        {
            var nodes = new[]
            {
                Node(1, Fass2TaskBuilder.StartStopPass),
                Node(2, Fass2TaskBuilder.StartStopControlStart),
                Node(3, Fass2TaskBuilder.StartStopPass)
            };

            var count = Fass2ControlAreaGate.LimitWindowCount(nodes, 1, 3, releasedIndex: 1);

            Assert.Equal(2, count);
        }

        [Fact]
        public void Evaluate_ExcludesSelf_AllowsFirstCar()
        {
            var area = Area(1, 10, 11);
            var occupancies = new List<Fass2ControlOccupancy>
            {
                new Fass2ControlOccupancy(1, new[] { 10, 11 })
            };

            var check = Fass2ControlAreaGate.Evaluate(1, area, occupancies);

            Assert.True(check.CanRelease);
            Assert.Equal(0, check.OthersInArea);
        }

        [Fact]
        public void Evaluate_OtherCarOnListedSite_EqualsCapacity_Blocks()
        {
            var area = Area(1, 10, 11);
            var occupancies = new List<Fass2ControlOccupancy>
            {
                new Fass2ControlOccupancy(1, new[] { 8 }),
                new Fass2ControlOccupancy(2, new[] { 11 })
            };

            var check = Fass2ControlAreaGate.Evaluate(1, area, occupancies);

            Assert.False(check.CanRelease);
            Assert.Equal(1, check.OthersInArea);
            Assert.Contains("capacity=1", check.Reason);
        }

        [Fact]
        public void Evaluate_OtherCarOutsideList_DoesNotBlock()
        {
            var area = Area(1, 10, 11);
            var occupancies = new List<Fass2ControlOccupancy>
            {
                new Fass2ControlOccupancy(2, new[] { 99 })
            };

            var check = Fass2ControlAreaGate.Evaluate(1, area, occupancies);

            Assert.True(check.CanRelease);
            Assert.Equal(0, check.OthersInArea);
        }

        [Fact]
        public void Evaluate_CapacityTwo_AllowsSecondCar()
        {
            var area = Area(2, 10, 11);
            var occupancies = new List<Fass2ControlOccupancy>
            {
                new Fass2ControlOccupancy(1, new[] { 10 }),
                new Fass2ControlOccupancy(2, new[] { 11 })
            };

            var check = Fass2ControlAreaGate.Evaluate(2, area, occupancies);

            Assert.True(check.CanRelease);
            Assert.Equal(1, check.OthersInArea);
        }

        [Fact]
        public void ActionResolver_ControlStop_IsNeverComplete()
        {
            var expected = Node(5, Fass2TaskBuilder.StartStopControlStop);
            var actual = Node(5, Fass2TaskBuilder.StartStopControlStop);

            Assert.True(Fass2ActionResolver.RequiresActionWait(expected));
            Assert.False(Fass2ActionResolver.IsStationActionComplete(expected, actual, vehicleState: 2));
        }

        private static Fass2ControlAreaInfo Area(int capacity, params int[] siteIds)
        {
            return new Fass2ControlAreaInfo
            {
                AreaSiteIds = siteIds,
                AreaId = string.Join(",", siteIds),
                Capacity = capacity,
                ReleaseStartStop = 11
            };
        }

        private static Fass2NodeMessage Node(ushort id, byte startStop)
        {
            return new Fass2NodeMessage { Node = id, StartStop = startStop };
        }
    }
}
