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

namespace StandardScene.Magnetic.Tests.Tasking
{
    public class Fass2ControlStopStateMachineTests
    {
        [Fact]
        public void ControlStop_TruncatesWindow_WaitsOccupancy_ThenReleases()
        {
            Fass2NodeMessage[] lastWindow = null;
            Fass2NodeMessage lastPatch = null;
            var canRelease = false;
            var nodes = new List<Fass2NodeMessage>
            {
                new Fass2NodeMessage { Node = 1, StartStop = Fass2TaskBuilder.StartStopPass, Distance = 100 },
                new Fass2NodeMessage { Node = 2, StartStop = Fass2TaskBuilder.StartStopControlStop },
                new Fass2NodeMessage { Node = 3, StartStop = Fass2TaskBuilder.StartStopPass }
            };
            var plan = new Fass2TaskPlan
            {
                StartSiteId = 10,
                GoalSiteId = 12,
                SiteIds = new List<int>(),
                Nodes = nodes,
                FieldsSignature = "keep"
            };

            var machine = new Fass2TaskStateMachine(new Fass2TaskCallbacks
            {
                BuildPlan = (_, __, ___) => plan,
                SendNodes = (window, _) => lastWindow = window,
                SendAction = (patch, _) => lastPatch = patch,
                SendControl = (_, __) => { },
                ResolveNodeId = id => (ushort)id,
                AllocateTaskId = () => 1UL,
                AllocateActionId = () => 9UL,
                CheckControlRelease = _ => new Fass2ControlReleaseCheck
                {
                    CanRelease = canRelease,
                    AreaId = "DockA",
                    Capacity = 1,
                    OthersInArea = canRelease ? 0 : 1,
                    ReleaseStartStop = Fass2TaskBuilder.StartStopControlStart,
                    Reason = canRelease ? "area clear" : "area occupied"
                }
            })
            {
                StartBeforeMove = false,
                LockCount = 4,
                ResendIntervalMs = 60_000,
                ActionRetryIntervalMs = 0
            };

            machine.Begin(10, 12);
            machine.Tick(Report(1, moving: true, startStop: 1));

            Assert.Equal(Fass2TaskPhase.Moving, machine.Context.Phase);
            Assert.NotNull(lastWindow);
            Assert.Equal(2, lastWindow.Length);
            Assert.Equal(Fass2TaskBuilder.StartStopControlStop, lastWindow[1].StartStop);

            machine.Tick(Report(2, moving: false, startStop: 12));
            Assert.Equal(Fass2TaskPhase.AtStation, machine.Context.Phase);
            Assert.Equal(1, machine.Context.CurrentIndex);

            lastPatch = null;
            machine.Tick(Report(2, moving: false, startStop: 12));
            Assert.True(machine.Context.WaitingForRelease);
            Assert.Null(lastPatch);
            Assert.Equal(-1, machine.Context.ControlReleasedIndex);

            canRelease = true;
            machine.Tick(Report(2, moving: false, startStop: 12));
            Assert.NotNull(lastPatch);
            Assert.Equal(Fass2TaskBuilder.StartStopControlStart, lastPatch.StartStop);
            Assert.Equal(1, machine.Context.ControlReleasedIndex);
            Assert.Equal(Fass2TaskBuilder.StartStopControlStart, nodes[1].StartStop);
            Assert.True(lastWindow.Length >= 2);
            Assert.Equal((ushort)3, lastWindow[lastWindow.Length - 1].Node);

            machine.Tick(Report(2, moving: false, startStop: 11));
            Assert.Equal(Fass2TaskPhase.AtStation, machine.Context.Phase);
            Assert.Equal(1, machine.Context.CurrentIndex);
        }

        private static Fass2StateReport Report(ushort node, bool moving, byte startStop)
        {
            return new Fass2StateReport
            {
                State = (byte)(moving ? 1 : 2),
                Node = new Fass2NodeMessage
                {
                    Node = node,
                    StartStop = startStop
                }
            };
        }
    }
}
