using StandardScene.Magnetic.Protocol;
using System;
using System.Collections.Generic;
using System.Text;

namespace StandardScene.Magnetic.Tasking
{
    /// <summary>
    /// 比对期望节点与上报节点，判定到站与动作是否完成（对齐 backend <c>CarResponseService</c>）。
    /// 默认只闭环机构类动作；轨迹字段（Speed/Orientation 等）需显式开启。
    /// </summary>
    public static class Fass2ActionResolver
    {
        /// <summary>是否在停车站比对 Speed/Orientation/Direction/Byroad 等轨迹字段。</summary>
        public static bool VerifyTrajectoryFields { get; set; }

        private static readonly (string Name, Func<Fass2NodeMessage, byte> Get, Action<Fass2NodeMessage, byte> Set)[] MechanismFields =
        {
            ("Obstacle", n => n.Obstacle, (n, v) => n.Obstacle = v),
            ("Audio", n => n.Audio, (n, v) => n.Audio = v),
            ("Light", n => n.Light, (n, v) => n.Light = v),
            ("Charge", n => n.Charge, (n, v) => n.Charge = v),
            ("Rest", n => n.Rest, (n, v) => n.Rest = v),
            ("Lift", n => n.Lift, (n, v) => n.Lift = v),
            ("Clamp", n => n.Clamp, (n, v) => n.Clamp = v),
            ("Tray", n => n.Tray, (n, v) => n.Tray = v),
            ("Roll", n => n.Roll, (n, v) => n.Roll = v),
            ("Shutdown", n => n.Shutdown, (n, v) => n.Shutdown = v)
        };

        private static readonly (string Name, Func<Fass2NodeMessage, byte> Get, Action<Fass2NodeMessage, byte> Set)[] TrajectoryFields =
        {
            ("Direction", n => n.Direction, (n, v) => n.Direction = v),
            ("Orientation", n => n.Orientation, (n, v) => n.Orientation = v),
            ("Byroad", n => n.Byroad, (n, v) => n.Byroad = v)
        };

        public static bool IsAtNode(Fass2StateReport report, ushort targetNode)
        {
            return report?.Node != null && report.Node.Node == targetNode;
        }

        public static bool IsVehicleMoving(byte vehicleState)
        {
            return vehicleState is 1 or 5;
        }

        public static bool RequiresActionWait(Fass2NodeMessage expected)
        {
            if (expected == null)
            {
                return false;
            }

            if (expected.StartStop is Fass2TaskBuilder.StartStopStop
                or Fass2TaskBuilder.StartStopPrecision
                or Fass2TaskBuilder.StartStopControlStop
                or Fass2TaskBuilder.StartStopControlStart)
            {
                return true;
            }

            foreach (var field in MechanismFields)
            {
                if (field.Get(expected) != 0)
                {
                    return true;
                }
            }

            if (!VerifyTrajectoryFields)
            {
                return false;
            }

            foreach (var field in TrajectoryFields)
            {
                if (field.Get(expected) != 0)
                {
                    return true;
                }
            }

            return expected.Speed != 0;
        }

        public static bool IsStationActionComplete(Fass2NodeMessage expected, Fass2NodeMessage actual, byte vehicleState)
        {
            if (expected == null || actual == null)
            {
                return false;
            }

            if (actual.Node != expected.Node)
            {
                return false;
            }

            if (IsVehicleMoving(vehicleState))
            {
                return false;
            }

            if (!RequiresActionWait(expected))
            {
                return true;
            }

            if (expected.StartStop == Fass2TaskBuilder.StartStopPass)
            {
                return true;
            }

            if (expected.StartStop == Fass2TaskBuilder.StartStopControlStop)
            {
                return false;
            }

            return ListPendingFields(expected, actual).Count == 0;
        }

        public static IReadOnlyList<string> ListPendingFields(Fass2NodeMessage expected, Fass2NodeMessage actual)
        {
            var pending = new List<string>();
            if (expected == null || actual == null)
            {
                return pending;
            }

            if (expected.StartStop != 0 && actual.StartStop != expected.StartStop)
            {
                pending.Add("StartStop");
            }

            if (VerifyTrajectoryFields)
            {
                if (expected.Speed != 0 && actual.Speed != expected.Speed)
                {
                    pending.Add("Speed");
                }

                foreach (var field in TrajectoryFields)
                {
                    var expectedValue = field.Get(expected);
                    if (expectedValue != 0 && field.Get(actual) != expectedValue)
                    {
                        pending.Add(field.Name);
                    }
                }
            }

            foreach (var field in MechanismFields)
            {
                var expectedValue = field.Get(expected);
                if (expectedValue != 0 && field.Get(actual) != expectedValue)
                {
                    pending.Add(field.Name);
                }
            }

            return pending;
        }

        public static Fass2NodeMessage BuildActionPatch(Fass2NodeMessage expected, Fass2NodeMessage actual)
        {
            if (expected == null)
            {
                return null;
            }

            var patch = new Fass2NodeMessage { Node = expected.Node };
            var hasPatch = false;

            if (expected.StartStop != 0 && (actual == null || actual.StartStop != expected.StartStop))
            {
                patch.StartStop = expected.StartStop;
                hasPatch = true;
            }

            if (VerifyTrajectoryFields)
            {
                if (expected.Speed != 0 && (actual == null || actual.Speed != expected.Speed))
                {
                    patch.Speed = expected.Speed;
                    hasPatch = true;
                }

                foreach (var field in TrajectoryFields)
                {
                    var expectedValue = field.Get(expected);
                    if (expectedValue != 0 && (actual == null || field.Get(actual) != expectedValue))
                    {
                        field.Set(patch, expectedValue);
                        hasPatch = true;
                    }
                }
            }

            foreach (var field in MechanismFields)
            {
                var expectedValue = field.Get(expected);
                if (expectedValue != 0 && (actual == null || field.Get(actual) != expectedValue))
                {
                    field.Set(patch, expectedValue);
                    hasPatch = true;
                }
            }

            return hasPatch ? patch : null;
        }

        public static string DescribePending(Fass2NodeMessage expected, Fass2NodeMessage actual)
        {
            var pending = ListPendingFields(expected, actual);
            if (pending.Count == 0)
            {
                return string.Empty;
            }

            var builder = new StringBuilder();
            for (var i = 0; i < pending.Count; i++)
            {
                if (i > 0)
                {
                    builder.Append(',');
                }

                builder.Append(pending[i]);
            }

            return builder.ToString();
        }
    }
}
