using Microsoft.EntityFrameworkCore;
using RobotNet.MapShares;
using RobotNet.MapShares.Dtos;
using RobotNet.MapShares.Enums;
using RobotNet.RobotManager.Data;
using RobotNet.RobotManager.Services.OpenACS;
using RobotNet.RobotManager.Services.Planner.Space;
using RobotNet.RobotManager.Services.Traffic;
using RobotNet.RobotShares.Dtos;
using RobotNet.RobotShares.Enums;
using RobotNet.RobotShares.VDA5050;
using RobotNet.RobotShares.VDA5050.Factsheet;
using RobotNet.RobotShares.VDA5050.FactsheetExtend;
using RobotNet.RobotShares.VDA5050.Order;
using RobotNet.RobotShares.VDA5050.State;
using RobotNet.RobotShares.VDA5050.Type;
using RobotNet.RobotShares.VDA5050.Visualization;
using RobotNet.Shares;
using System.Text.Json;
using Action = RobotNet.RobotShares.VDA5050.InstantAction.Action;
using NodePosition = RobotNet.RobotShares.VDA5050.Order.NodePosition;

namespace RobotNet.RobotManager.Services.Robot;

public class RobotController(string robotid,
                             string Manufacturer,
                             string Version,
                             NavigationType NavigationType,
                             IServiceProvider ServiceProvider,
                             Func<string, string, bool> FuncPub) : IRobotController, IDisposable
{
    public string SerialNumber { get; } = robotid;
    public bool IsOnline { get; set; }
    public bool IsWorking => (RobotOrder is not null && RobotOrder.IsProcessing) || StateMsg.NodeStates.Length != 0 || StateMsg.EdgeStates.Length != 0;
    public string State => GetState();
    public string[] CurrentZones => RobotOrder is null ? [] : [.. RobotOrder.CurrentZones.Select(z => z.Name)];
    public RobotOrderDto OrderState => GetOrderState();
    public RobotActionDto[] ActionStates => GetActionsState();
    public AutoResetEvent RobotUpdated { get; set; } = new(false);
    public StateMsg StateMsg { get; set; } = new();
    public VisualizationMsg VisualizationMsg { get; set; } = new();
    public FactSheetMsg FactSheetMsg { get; set; } = new();
    public FactsheetExtendMsg FactsheetExtendMsg { get; set; } = new();
    public NavigationPathEdge[] BasePath => RobotOrder is null ? [] : RobotOrder.BasePath;
    public NavigationPathEdge[] FullPath => RobotOrder is null ? [] : RobotOrder.FullPath;

    private readonly LoggerController<RobotController> Logger = ServiceProvider.GetRequiredService<LoggerController<RobotController>>();
    private readonly TimeSpan WaittingRobotFeedbackTime = TimeSpan.FromSeconds(10);
    private RobotOrder? RobotOrder;

    private CancellationTokenSource? CancelRandom;

    public void Log(string message, LogLevel level = LogLevel.Information)
    {
        switch (level)
        {
            case LogLevel.Trace: Logger.Trace($"{SerialNumber} - {message}"); break;
            case LogLevel.Error: Logger.Error($"{SerialNumber} - {message}"); break;
            case LogLevel.Warning: Logger.Warning($"{SerialNumber} - {message}"); break;
            case LogLevel.Critical: Logger.Critical($"{SerialNumber} - {message}"); break;
            case LogLevel.Information: Logger.Info($"{SerialNumber} - {message}"); break;
            case LogLevel.Debug: Logger.Debug($"{SerialNumber} - {message}"); break;
            default: Logger.Debug($"{SerialNumber} - {message}"); break;
        }
    }

    public async Task<MessageResult> CancelOrder()
    {
        try
        {
            if (StateMsg.NodeStates.Length != 0 || StateMsg.EdgeStates.Length != 0)
            {
                Action cancelOrderAction = new()
                {
                    ActionDescription = "Yêu cầu hủy nhiệm vụ hiện tại",
                    ActionParameters = [new RobotShares.VDA5050.InstantAction.ActionParameter()
                    {
                        Key = "ORDER_ID",
                        Value = StateMsg.OrderId,
                    }],
                    ActionType = ActionType.cancelOrder.ToString(),
                    BlockingType = RobotShares.VDA5050.InstantAction.BlockingType.NONE.ToString(),
                };
                var pubAction = await InstantAction(cancelOrderAction, true);
                if (!pubAction.IsSuccess) return new(false, pubAction.Message);
            }
            if (CancelRandom is not null && !CancelRandom.IsCancellationRequested) CancelRandom.Cancel();
            if (RobotOrder is not null && RobotOrder.IsProcessing)
            {
                CancellationTokenSource cancellationToken = new();
                cancellationToken.CancelAfter(TimeSpan.FromSeconds(10));
                var waitCancel = await RobotOrder.Cancel(cancellationToken.Token);
                if (!waitCancel) return new(false, "Robot không kết thúc order");
            }
            return new(true);
        }
        catch (Exception ex)
        {
            Logger.Warning($"{SerialNumber} Cancel Order logs: {ex.Message}");
            return new(false, $"{SerialNumber} Cancel Order logs: {ex.Message}");
        }
    }

    public async Task<MessageResult> CancelAction()
    {
        try
        {
            Action cancelOrderAction = new()
            {
                ActionDescription = "Yêu cầu hủy Action hiện tại",
                ActionParameters = [new RobotShares.VDA5050.InstantAction.ActionParameter()
                    {
                        Key = "ORDER_ID",
                        Value = SerialNumber,
                    }],
                ActionType = ActionType.cancelOrder.ToString(),
                BlockingType = RobotShares.VDA5050.InstantAction.BlockingType.NONE.ToString(),
            };
            var pubAction = await InstantAction(cancelOrderAction, true);
            return new(pubAction.IsSuccess, pubAction.Message);
        }
        catch (Exception ex)
        {
            Logger.Warning($"{SerialNumber} Cancel Action logs: {ex.Message}");
            return new(false, $"{SerialNumber} Cancel Action logs: {ex.Message}");
        }
    }

    public void Initialize(double x, double y, double theta)
    {
        throw new NotImplementedException();
    }

    public async Task<MessageResult<string>> InstantAction(Action action, bool waittingFisished)
    {
        CancellationTokenSource CancellationToken = new();
        CancellationToken.CancelAfter(WaittingRobotFeedbackTime);
        bool IsFeedback = false;
        int RepeatCounter = 0;
        try
        {
            action.ActionId = Guid.NewGuid().ToString();
            var instantActionsMsg = new RobotNet.RobotShares.VDA5050.InstantAction.InstantActionsMsg()
            {
                HeaderId = 1,
                Manufacturer = Manufacturer,
                Version = Version,
                Timestamp = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"),
                SerialNumber = SerialNumber,
                Actions = [action],
            };
            var pubInstantAction = FuncPub.Invoke(VDA5050Topic.InstantActions, JsonSerializer.Serialize(instantActionsMsg, JsonOptionExtends.Write));
            if (!pubInstantAction)
            {
                string msg = $"Gửi Action xuống cho robot không thành công: {action.ActionType}";
                Log(msg, LogLevel.Warning);
                return new(false, msg);
            }
            while (!IsFeedback)
            {
                if (StateMsg is not null && StateMsg.ActionStates is not null)
                {
                    var actionstate = StateMsg.ActionStates.FirstOrDefault(ac => ac.ActionId == action.ActionId);
                    if (actionstate is not null)
                    {
                        IsFeedback = true;
                        break;
                    }
                }
                if(CancellationToken.IsCancellationRequested)
                {
                    if (RepeatCounter++ == 0)
                    {
                        await Task.Delay(1000);
                        CancellationToken = new();
                        CancellationToken.CancelAfter(WaittingRobotFeedbackTime);
                        pubInstantAction = FuncPub.Invoke(VDA5050Topic.InstantActions, JsonSerializer.Serialize(instantActionsMsg, JsonOptionExtends.Write));
                        if (!pubInstantAction)
                        {
                            string msg = $"Gửi Action xuống cho robot không thành công: {action.ActionType}";
                            Log(msg, LogLevel.Warning);
                            return new(false, msg);
                        }
                    }
                    else return new(false, $"Robot không đồng ý thực hiện action: {action.ActionType}");
                }
                await Task.Delay(500);
            }
            if (waittingFisished)
            {
                while (!CancellationToken.IsCancellationRequested)
                {
                    if (StateMsg is not null)
                    {
                        if (StateMsg.ActionStates is not null)
                        {
                            var actionCancelOrder = StateMsg.ActionStates.FirstOrDefault(a => a.ActionId == action.ActionId);
                            if (actionCancelOrder is not null)
                            {
                                if (ActionFinish(actionCancelOrder.ActionStatus)) return new(true);
                                if (ActionFailed(actionCancelOrder.ActionStatus))
                                {
                                    string msg = $"Robot trả về thực hiện action lỗi: {action.ActionType}";
                                    Log(msg, LogLevel.Warning);
                                    return new(false, msg);
                                }
                            }
                        }
                        if (GetErrorLevel() != ErrorLevel.NONE)
                        {
                            var errorStr = StateMsg.Errors.Select(error => error.ErrorType).ToArray();
                            string msg = $"Robot xảy ra lỗi sau khi gọi thực hiện action {action.ActionType}";
                            Log($"{msg} - {string.Join(",", errorStr)}", LogLevel.Warning);
                            return new(false, msg);
                        }
                    }
                    if (CancellationToken.IsCancellationRequested) return new(false, "Robot đang thực hiện Action nhưng đã quá thời gian timeout 10s");
                    await Task.Delay(500);
                }
            }
            if (CancellationToken.IsCancellationRequested) throw new Exception();
            return new(true) { Data = action.ActionId };
        }
        catch (Exception ex)
        {
            string msg = $"Có lỗi xảy ra trong quá trình gửi action : {ex.Message}";
            Log(msg, LogLevel.Warning);
            return new(false, msg);
        }
    }

    public MessageResult MoveStraight(double x, double y)
    {
        return new(false, "Chức năng này hiện không khả dụng");
    }

    public async Task<MessageResult> MoveToNode(string goalName, IDictionary<string, IEnumerable<RobotShares.VDA5050.InstantAction.Action>>? actions = null, double? lastAngle = null)
    {
        try
        {
            if (IsWorking) return new(false, "Robot đang thực hiện nhiệm vụ");

            using var scope = ServiceProvider.CreateScope();
            var PathPlanner = scope.ServiceProvider.GetRequiredService<PathPlanner>();
            var robotDb = scope.ServiceProvider.GetRequiredService<RobotEditorDbContext>();
            var MapManager = scope.ServiceProvider.GetRequiredService<MapManager>();
            var TrafficManager = scope.ServiceProvider.GetRequiredService<TrafficManager>();
            var TrafficACS = scope.ServiceProvider.GetRequiredService<TrafficACS>();
            var orderLogger = scope.ServiceProvider.GetRequiredService<LoggerController<RobotOrder>>();

            var robot = await robotDb.Robots.FirstOrDefaultAsync(r => r.RobotId == SerialNumber) ??
                        throw new Exception($"Không tìm thấy robot {SerialNumber} trong kho dữ liệu");
            var robotModel = await robotDb.RobotModels.FirstOrDefaultAsync(r => r.Id == robot.ModelId) ??
                        throw new Exception($"Không tìm thấy robot model {robot.ModelId} trong kho dữ liệu");

            var path = await PathPlanner.Planning(VisualizationMsg.AgvPosition.X, VisualizationMsg.AgvPosition.Y, VisualizationMsg.AgvPosition.Theta, NavigationType, robot.MapId, goalName);
            if (!path.IsSuccess) return new(false, path.Message);
            if(path.IsSuccess && path.Data.Nodes.Length < 2)
            {
                RobotOrder?.CreateComledted();
                return new(true, "");
            }
            if (path.Data.Nodes is null || path.Data.Edges is null || path.Data.Edges.Length == 0 || path.Data.Nodes.Length < 2)
                return new(false, $"Đường dẫn tới đích {goalName} từ [{VisualizationMsg.AgvPosition.X} - {VisualizationMsg.AgvPosition.Y} - {VisualizationMsg.AgvPosition.Theta}] không tồn tại");

            if (lastAngle != null)
            {
                path.Data.Nodes[^1].Theta = lastAngle.Value;
            }

            var mapData = await MapManager.GetMapData(robot.MapId);
            if (!mapData.IsSuccess) return new(false, mapData.Message);
            if (mapData is null || mapData.Data is null) return new(false, "Không thể tìm thấy bản đồ chứa robot.");
            var nodeInZones = await PathPlanner.GetZones(robot.MapId, path.Data.Nodes);
            if (!nodeInZones.IsSuccess) return new(false, mapData.Message);
            var order = await Order([.. path.Data.Nodes], [.. path.Data.Edges], mapData.Data, actions, TrafficManager.Enable);
            if (order.IsSuccess && order.Data is not null)
            {
                int counter = 0;
                while (counter++ < 2)
                {
                    var createAgent = TrafficManager.CreateAgent(robot.MapId, this, new()
                    {

                        NavigationType = robotModel.NavigationType,
                        Length = robotModel.Length,
                        Width = robotModel.Width,
                        NavigationPointX = robotModel.OriginX,
                        NavigationPointY = robotModel.OriginY,
                    },
                    [..path.Data.Nodes.Select(n => new TrafficNodeDto()
                    {
                        Id = n.Id,
                        Name = n.Name,
                        X = n.X,
                        Y = n.Y,
                        Direction = MapCompute.GetRobotDirection(n.Direction),
                    })], [..path.Data.Edges.Select(n => new TrafficEdgeDto()
                    {
                        Id = n.Id,
                        StartNodeId = n.StartNodeId,
                        EndNodeId = n.EndNodeId,
                        TrajectoryDegree = n.TrajectoryDegree,
                        ControlPoint1X = n.ControlPoint1X,
                        ControlPoint1Y = n.ControlPoint1Y,
                        ControlPoint2X = n.ControlPoint2X,
                        ControlPoint2Y = n.ControlPoint2Y,
                    })]);
                    if (createAgent.IsSuccess) break;
                    Logger.Warning($"{SerialNumber} - Không thể tạo traffic agent: {createAgent.Message}");
                    if (counter > 1 && !createAgent.IsSuccess)
                    {
                        var cancel = await CancelOrder();
                        if (!cancel.IsSuccess) Logger.Warning($"{SerialNumber} - Không thể hủy bỏ nhiệm vụ đã giao: {cancel.Message}");
                        return new(false, $"Không thể tạo traffic agent: {createAgent.Message} - {(cancel.IsSuccess ? "Đã hủy order" : $"Không thể hủy order: {cancel.Message}")} ");
                    }
                }
                RobotOrder = new RobotOrder([.. path.Data.Nodes], [.. path.Data.Edges], nodeInZones.Data ?? [], actions ?? new Dictionary<string, IEnumerable<Action>>(),
                                            order.Data, this, NavigationType, TrafficManager, TrafficACS, MapManager, orderLogger, Order)
                {
                    CurrentZones = RobotOrder is null ? [] : RobotOrder.CurrentZones,
                };

                return new(true);
            }
            else return new(order.IsSuccess, order.Message);
        }
        catch (Exception ex)
        {
            string msg = $"Có lỗi xảy ra trong quá trình gửi order to Node {goalName} : {ex.Message}";
            Log(msg, LogLevel.Warning);
            return new(false, msg);
        }
    }

    public MessageResult Rotate(double angle)
    {
        return new(false, "Chức năng này hiện không khả dụng");
    }

    private async Task<MessageResult> Order(OrderMsg orderMsg)
    {
        CancellationTokenSource CancellationExitToken = new();
        CancellationExitToken.CancelAfter(WaittingRobotFeedbackTime);
        try
        {
            var pubOrder = FuncPub.Invoke(VDA5050Topic.Order, JsonSerializer.Serialize(orderMsg, JsonOptionExtends.Write));
            if (!pubOrder)
            {
                string msg = $"Gửi Order xuống cho robot không thành công: {orderMsg.OrderId}";
                Log(msg, LogLevel.Warning);
                return new(false, msg);
            }
            while (!CancellationExitToken.IsCancellationRequested)
            {
                if (StateMsg is not null)
                {
                    if (StateMsg.OrderId == orderMsg.OrderId && IsWorking && StateMsg.OrderUpdateId == orderMsg.OrderUpdateId) break;
                }

                await Task.Delay(500, CancellationExitToken.Token);
            }
            if (CancellationExitToken.IsCancellationRequested) throw new Exception();
            return new(true);
        }
        catch (Exception ex)
        {
            string msg = $"Có lỗi xảy ra trong quá trình gửi order : {ex.Message}";
            if (CancellationExitToken.IsCancellationRequested) msg = $"{SerialNumber} - Robot không đồng ý thực hiện order: {orderMsg.OrderId}";

            Log(msg, LogLevel.Warning);
            return new(false, msg);
        }
    }

    private static List<Action> ConvertAction(string actions, ActionDto[] mapAction)
    {
        List<Action> Actions = [];
        var ActionIds = !string.IsNullOrEmpty(actions) ? JsonSerializer.Deserialize<Guid[]>(actions) : [];
        if (ActionIds is not null)
        {
            foreach (var actionid in ActionIds)
            {
                var actionDb = mapAction.FirstOrDefault(x => x.Id == actionid);
                if (actionDb is not null)
                {
                    var vdaAction = JsonSerializer.Deserialize<Action>(actionDb.Content, JsonOptionExtends.Read);
                    if (vdaAction is not null) Actions.Add(vdaAction);
                }
            }
        }
        return Actions;
    }

    private static Edge? ConvertToOrderEdge(EdgeDto edge, MapDataDto map)
    {
        var startNode = map.Nodes.FirstOrDefault(n => n.Id == edge.StartNodeId);
        var endNode = map.Nodes.FirstOrDefault(n => n.Id == edge.EndNodeId);
        var dataEdge = map.Edges.FirstOrDefault(e => e.Id == edge.Id);

        if (startNode is null || endNode is null || dataEdge is null) return null;

        List<ControlPoint> ControlPoints = [];
        ControlPoints.Add(new()
        {
            X = startNode.X,
            Y = startNode.Y,
            Weight = 1,
        });
        if (dataEdge.TrajectoryDegree == TrajectoryDegree.Two)
        {
            ControlPoints.Add(new()
            {
                X = dataEdge.ControlPoint1X,
                Y = dataEdge.ControlPoint1Y,
                Weight = 1,
            });
        }
        else if (dataEdge.TrajectoryDegree == TrajectoryDegree.Three)
        {
            ControlPoint controlPoint1 = new();
            ControlPoint controlPoint2 = new();
            if (dataEdge.StartNodeId == edge.StartNodeId)
            {
                controlPoint1.X = dataEdge.ControlPoint1X;
                controlPoint1.Y = dataEdge.ControlPoint1Y;
                controlPoint1.Weight = 1;
                controlPoint2.X = dataEdge.ControlPoint2X;
                controlPoint2.Y = dataEdge.ControlPoint2Y;
                controlPoint2.Weight = 1;
            }
            else
            {
                controlPoint2.X = dataEdge.ControlPoint1X;
                controlPoint2.Y = dataEdge.ControlPoint1Y;
                controlPoint2.Weight = 1;
                controlPoint1.X = dataEdge.ControlPoint2X;
                controlPoint1.Y = dataEdge.ControlPoint2Y;
                controlPoint1.Weight = 1;
            }
            ControlPoints.Add(controlPoint1);
            ControlPoints.Add(controlPoint2);
        }
        ControlPoints.Add(new()
        {
            X = endNode.X,
            Y = endNode.Y,
            Weight = 1,
        });

        var result = new Edge()
        {
            EdgeId = edge.Id.ToString(),
            EdgeDescription = $"{startNode.Name} - {endNode.Name}",
            EndNodeId = edge.EndNodeId.ToString(),
            StartNodeId = edge.StartNodeId.ToString(),
            SequenceId = 1,
            Direction = dataEdge.DirectionAllowed.ToString(),
            Orientation = 0,
            Released = true,
            Length = MapEditorHelper.GetEdgeLength(new()
            {
                X1 = startNode.X,
                X2 = endNode.X,
                Y1 = startNode.Y,
                Y2 = endNode.Y,
                TrajectoryDegree = dataEdge.TrajectoryDegree,
                ControlPoint1X = dataEdge.ControlPoint1X,
                ControlPoint1Y = dataEdge.ControlPoint1Y,
                ControlPoint2X = dataEdge.ControlPoint2X,
                ControlPoint2Y = dataEdge.ControlPoint2Y,
            }),
            MaxHeight = dataEdge.MaxHeight,
            MaxRotationSpeed = dataEdge.MaxRotationSpeed,
            OrientationType = "",
            MaxSpeed = dataEdge.MaxSpeed,
            MinHeight = dataEdge.MinHeight,
            RotationAllowed = dataEdge.RotationAllowed,
            Actions = [.. ConvertAction(dataEdge.Actions, [.. map.Actions])],
            Trajectory = new()
            {
                Degree = dataEdge.TrajectoryDegree == TrajectoryDegree.One ? 1 : dataEdge.TrajectoryDegree == TrajectoryDegree.Two ? 2 : 3,
                KnotVector = dataEdge.TrajectoryDegree == TrajectoryDegree.One ? [0, 0, 1, 1] : dataEdge.TrajectoryDegree == TrajectoryDegree.Two ? [0, 0, 0, 1, 1, 1] : [0, 0, 0, 0, 1, 1, 1, 1],
                ControlPoints = [.. ControlPoints],
            },
        };
        return result;
    }

    public static void CreateOrderMsg(ref OrderMsg orderMsgTemplate, NodeDto[] nodes, EdgeDto[] edges, MapDataDto map, IDictionary<string, IEnumerable<Action>> actions, NavigationType navigationType, bool trafficEnable)
    {
        if (nodes.Length <= 1 || edges.Length < 1) throw new ArgumentException("Dữ liệu đường đi không hợp lệ");
        List<Node> OrderNodes = [];
        List<Edge> OrderEdges = [];
        for (int i = 0; i < nodes.Length; i++)
        {
            var nodeDb = map.Nodes.FirstOrDefault(n => n.Id == nodes[i].Id);
            if (nodeDb is null)
            {
                if (i != 0) throw new ArgumentException("Đường đi tồn tại node không hợp lệ");

                var angleForward = Math.Atan2(nodes[1].Y - nodes[0].Y, nodes[1].X - nodes[0].X);
                var angleBackward = Math.Atan2(nodes[0].Y - nodes[1].Y, nodes[0].X - nodes[1].X);
                OrderNodes.Add(new()
                {
                    NodeId = nodes[i].Id.ToString(),
                    Released = true,
                    SequenceId = i,
                    NodeDescription = nodes[i].Name ?? "",
                    NodePosition = new NodePosition()
                    {
                        X = nodes[i].X,
                        Y = nodes[i].Y,
                        Theta = navigationType == NavigationType.OmniDrive ? 10 : nodes[i].Direction == Direction.FORWARD ? angleForward : angleBackward,
                        MapDescription = map.Name,
                        MapId = map.Id.ToString(),
                        AllowedDeviationTheta = nodes[i].AllowedDeviationTheta,
                        AllowedDeviationXY = nodes[i].AllowedDeviationXy,
                    },
                    Actions = actions.TryGetValue(nodes[i].Name, out IEnumerable<Action>? anoCustomActions) ? [.. anoCustomActions] : [],
                });
                continue;
            }

            double angle;
            if (i == 0)
            {
                var nearNode = MapEditorHelper.GetNearByNode(nodes[0], nodes[1], edges[0], 0.01);
                var angleForward = Math.Atan2(nearNode.Y - nodes[0].Y, nearNode.X - nodes[0].X);
                var angleBackward = Math.Atan2(nodes[0].Y - nearNode.Y, nodes[0].X - nearNode.X);
                angle = nodes[i].Direction == Direction.FORWARD ? angleForward : angleBackward;
            }
            else
            {
                var nearNode = MapEditorHelper.GetNearByNode(nodes[i], nodes[i - 1], edges[i - 1], 0.01);
                var angleForward = Math.Atan2(nodeDb.Y - nearNode.Y, nodeDb.X - nearNode.X);
                var angleBackward = Math.Atan2(nearNode.Y - nodeDb.Y, nearNode.X - nodeDb.X);

                if (nodes[i].Direction == nodes[i - 1].Direction) angle = nodes[i].Direction == Direction.FORWARD ? angleForward : angleBackward;
                else angle = nodes[i - 1].Direction == Direction.FORWARD ? angleForward : angleBackward;
            }

            List<Action> nodeActions = ConvertAction(nodeDb.Actions, [.. map.Actions]);
            if (actions.TryGetValue(nodes[i].Name, out IEnumerable<Action>? customActions) && customActions is not null)
            {
                nodeActions.AddRange(customActions);
            }
            OrderNodes.Add(new()
            {
                NodeId = nodes[i].Id.ToString(),
                Released = i == 0 || !trafficEnable,
                SequenceId = i,
                NodeDescription = nodes[i].Name ?? "",
                NodePosition = new NodePosition()
                {
                    X = nodeDb.X,
                    Y = nodeDb.Y,
                    Theta = i == (nodes.Length - 1) ? (nodeDb.Theta * Math.PI / 180) : (navigationType == NavigationType.OmniDrive ? 10 : angle),
                    MapDescription = map.Name,
                    MapId = map.Id.ToString(),
                    AllowedDeviationTheta = nodeDb.AllowedDeviationTheta,
                    AllowedDeviationXY = nodeDb.AllowedDeviationXy,
                },
                Actions = [.. nodeActions],
            });
        }
        foreach (var edge in edges)
        {
            var orderEdge = ConvertToOrderEdge(edge, map);
            if (orderEdge is null)
            {
                if (edge == edges.First())
                {
                    orderEdge = new Edge()
                    {
                        EdgeId = edge.Id.ToString(),
                        StartNodeId = edge.StartNodeId.ToString(),
                        EndNodeId = edge.EndNodeId.ToString(),
                        EdgeDescription = $"{edge.StartNodeId} - {edge.EndNodeId}",
                        Direction = edge.DirectionAllowed.ToString(),
                        Orientation = 0,
                        Released = false,
                        Length = 0,
                        MaxRotationSpeed = 0.5,
                        OrientationType = "",
                        MaxSpeed = 0.5,
                        MinHeight = 0,
                        RotationAllowed = true,
                        Actions = [.. ConvertAction(edge.Actions, [.. map.Actions])],
                        Trajectory = new()
                        {
                            Degree = 1,
                            KnotVector = [0, 0, 1, 1],
                            ControlPoints = [new()
                                {
                                    X = nodes[0].X,
                                    Y = nodes[0].Y,
                                    Weight = 1,
                                },
                                new()
                                {
                                    X = nodes[1].X,
                                    Y = nodes[1].Y,
                                    Weight = 1,
                                }],
                        },
                    };
                }
                else throw new ArgumentException("Đường đi tồn tại edge không hợp lệ");
            }
            orderEdge.SequenceId = Array.IndexOf(edges, edge);
            orderEdge.Released = !trafficEnable;
            OrderEdges.Add(orderEdge);
        }
        orderMsgTemplate.Nodes = [.. OrderNodes];
        orderMsgTemplate.Edges = [.. OrderEdges];
    }

    private async Task<MessageResult<OrderMsg?>> Order(List<NodeDto> nodes, List<EdgeDto> edges, MapDataDto map, IDictionary<string, IEnumerable<Action>>? actions, bool trafficManager)
    {
        try
        {
            var OrderMsg = new OrderMsg()
            {
                HeaderId = 1,
                Manufacturer = Manufacturer,
                Version = Version,
                Timestamp = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"),
                SerialNumber = SerialNumber,
                OrderId = Guid.NewGuid().ToString(),
                OrderUpdateId = 1,
                ZoneSetId = map.Name ?? "",
            };
            CreateOrderMsg(ref OrderMsg, [.. nodes], [.. edges], map, actions ?? new Dictionary<string, IEnumerable<Action>>(), NavigationType, trafficManager);
            var pubOrder = await Order(OrderMsg);
            if (pubOrder.IsSuccess) return new(true) { Data = OrderMsg };
            return new(false, pubOrder.Message);
        }
        catch (Exception ex)
        {
            string msg = "Có lỗi xảy ra trong quá trình tạo Order";
            Log($"{msg}: {ex.Message}", LogLevel.Warning);
            return new(true, msg);
        }
    }

    private string GetState()
    {
        if (StateMsg.Information is not null)
        {
            var RobotGeneral = StateMsg.Information.FirstOrDefault(info => info.InfoType == InformationType.robot_general.ToString());
            if (RobotGeneral is not null)
            {
                var references = RobotGeneral.InfoReferences.FirstOrDefault(key => key.ReferenceKey == InformationReferencesKey.robot_state.ToString());
                if (references is not null) return references.ReferenceValue;
            }
        }
        return "";
    }

    private RobotOrderDto GetOrderState()
    {
        return new()
        {
            OrderId = StateMsg.OrderId,
            IsCompleted = (RobotOrder is not null && RobotOrder.IsCompleted) || RobotOrder is null,
            IsError = RobotOrder is not null && RobotOrder.IsError,
            IsProcessing = RobotOrder is not null && RobotOrder.IsProcessing,
            IsCanceled = RobotOrder is not null && RobotOrder.IsCanceled,
            Errors = RobotOrder is not null ? RobotOrder.Errors : [],
        };
    }

    private RobotActionDto[] GetActionsState()
    {
        List<RobotActionDto> Actions = [];
        foreach (var action in StateMsg.ActionStates)
        {
            Actions.Add(new RobotActionDto()
            {
                ActionId = action.ActionId,
                Action = action,
                IsError = ActionFailed(action.ActionStatus),
                IsCompleted = ActionFinish(action.ActionStatus),
                IsProcessing = ActionRunning(action.ActionStatus),
                Errors = [.. GetErrorString()],
            });
        }
        return [.. Actions];
    }

    private ErrorLevel GetErrorLevel()
    {
        if (StateMsg.Errors is not null)
        {
            if (StateMsg.Errors.Any(error => error.ErrorLevel == ErrorLevel.FATAL.ToString())) return ErrorLevel.FATAL;
            if (StateMsg.Errors.Any(error => error.ErrorLevel == ErrorLevel.WARNING.ToString())) return ErrorLevel.WARNING;
        }
        return ErrorLevel.NONE;
    }

    private static bool ActionFinish(string status) => status == ActionStatus.FINISHED.ToString();

    private static bool ActionFailed(string status) => status == ActionStatus.FAILED.ToString();

    private static bool ActionRunning(string status) => status == ActionStatus.RUNNING.ToString();

    private IEnumerable<string> GetErrorString()
    {
        if (StateMsg.Errors is not null && StateMsg.Errors.Length > 0)
        {
            foreach (var error in StateMsg.Errors)
            {
                yield return $"{error.ErrorType} - {error.ErrorDescription}";
            }
        }
        yield break;
    }

    public Task<MessageResult> MoveRandom(List<string> nodes)
    {
        try
        {
            CancelRandom = new CancellationTokenSource();
            var random = new Random();
            var randomTask = Task.Run(async () =>
            {
                while (!CancelRandom.IsCancellationRequested)
                {
                    var index = random.Next(nodes.Count);
                    await MoveToNode(nodes[index]);
                    while (!CancelRandom.IsCancellationRequested)
                    {
                        if (!IsWorking) break;
                        await Task.Delay(1000);
                    }
                    await Task.Delay(2000);
                }
            }, cancellationToken: CancelRandom.Token);
        }
        catch { }
        return Task.FromResult<MessageResult>(new(true));
    }

    public void Dispose()
    {
        if (IsOnline) IsOnline = false;
        GC.SuppressFinalize(this);
    }
}
