using MudBlazor;
using RobotNet.MapShares;
using RobotNet.MapShares.Dtos;
using RobotNet.MapShares.Enums;
using RobotNet.MapShares.Models;
using RobotNet.Shares;
using RobotNet.WebApp.Components;
using RobotNet.WebApp.Maps.Models;
using System.Net.Http.Json;

namespace RobotNet.WebApp.Maps.Components.Editor;

public class MergeNodeBackup
{
    public Guid NodeId { get; set; }
    public List<(Guid edgeId, NodeModel node)> EdgesMerge { get; set; } = [];
}

public partial class MapContainer
{
    public readonly List<MapEditorBackup> EditorBackup = [];

    public async Task CreateEdge(double x1, double y1, double x2, double y2, TrajectoryDegree degree = TrajectoryDegree.One, double cpX1 = 0, double cpY1 = 0, double cpX2 = 0, double cpY2 = 0)
    {
        if (MapData is null) { Snackbar.Add("Dữ liệu bản đồ rỗng", Severity.Warning); return; }
        if (MapData.Active) { Snackbar.Add("Không thể tạo edge do bản đồ đang được Active", Severity.Warning); return; }

        var result = await (await Http.PostAsJsonAsync($"api/Edges", new EdgeCreateModel()
        {
            MapId = MapData.Id,
            X1 = x1,
            Y1 = y1,
            X2 = x2,
            Y2 = y2,
            TrajectoryDegree = degree,
            ControlPoint1X = cpX1,
            ControlPoint1Y = cpY1,
            ControlPoint2X = cpX2,
            ControlPoint2Y = cpY2,
        })).Content.ReadFromJsonAsync<MessageResult<EdgeCreateDto>>();
        if (result is null) { Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error); return; }
        else if (!result.IsSuccess) { Snackbar.Add(result.Message ?? "Tạo Edge không thành công", Severity.Error); return; }
        else if (result.Data == null || !result.Data.EdgesDto.Any() || result.Data.EdgesDto.Any(e => e.StartNode is null || e.EndNode is null))
        {
            Snackbar.Add("Lỗi dữ liệu trả về", Severity.Error);
            return;
        }

        foreach (var edge in result.Data.EdgesDto)
        {
            if (edge.StartNode is null || edge.EndNode is null) continue;
            if (!Nodes.ContainsKey(edge.StartNodeId)) Nodes.Add(edge.StartNode);
            if (!Nodes.ContainsKey(edge.EndNodeId)) Nodes.Add(edge.EndNode);

            Edges.Add(edge, Nodes[edge.StartNodeId], Nodes[edge.EndNodeId]);
        }

        foreach (var removeEdgeId in result.Data.RemoveEdge)
        {
            var removeEdge = Edges.FirstOrDefault(e => e.Id == removeEdgeId);
            if (removeEdge is not null)
            {
                Edges.Remove(removeEdge);
                var edgeEditSteps = EditorBackup.Where(step => (step.Type == MapEditorBackupType.ControlPoint1Edge || step.Type == MapEditorBackupType.ControlPoint2Edge) && step.Id == removeEdge.Id);
            }
        }
        Snackbar.Add("Tạo Edge mới thành công", Severity.Success);
    }

    public async Task DeleteEdge()
    {
        if (EditorBackup.Count > 0)
        {
            if (!await SaveChanged()) return;
        }

        if (Edges.ActivedEdges.Count == 0) return;
        if (MapData is null) { Snackbar.Add("Dữ liệu bản đồ rỗng", Severity.Warning); return; }
        if (MapData.Active) { Snackbar.Add("Không thể xóa edge do bản đồ đang được Active", Severity.Warning); return; }

        var parameters = new DialogParameters<ConfirmDialog>
        {
            { x => x.Content, "Bạn chắc chắn muốn xóa edge đi không?" },
            { x => x.ConfirmText, "Delete" },
            { x => x.Color, Color.Secondary }
        };
        var Confirm = await Dialog.ShowAsync<ConfirmDialog>("Xoá Edge", parameters);
        var ConfirmResult = await Confirm.Result;
        if (ConfirmResult is not null && ConfirmResult.Data is not null && bool.TryParse(ConfirmResult.Data.ToString(), out bool data) && data)
        {
            OverlayIsVisible = true;
            StateHasChanged();

            var deleteListEdgeId = Edges.ActivedEdges.Select(x => x.Id).ToList();
            HttpRequestMessage request = new()
            {
                Content = JsonContent.Create(deleteListEdgeId),
                Method = HttpMethod.Delete,
                RequestUri = new Uri($"{Http.BaseAddress}api/Edges")
            };
            var response = await Http.SendAsync(request);
            var result = await response.Content.ReadFromJsonAsync<MessageResult>();
            if (result is null) { Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error); return; }
            else if (!result.IsSuccess) { Snackbar.Add(result.Message ?? "Xóa Edge không thành công", Severity.Error); return; }

            if (Edges.ActivedEdges.Count < 20)
            {
                List<EdgeModel> ActivedEdgeCopy = [.. Edges.ActivedEdges];
                Nodes.UnActivedNode();
                Edges.UnActivedEdge();
                foreach (var edge in ActivedEdgeCopy)
                {
                    var removeEdge = Edges.FirstOrDefault(e => e.Id == edge.Id);
                    if (removeEdge == null) continue;
                    Edges.Remove(removeEdge);
                    var edgeEditSteps = EditorBackup.Where(step => (step.Type == MapEditorBackupType.ControlPoint1Edge || step.Type == MapEditorBackupType.ControlPoint2Edge) && step.Id == removeEdge.Id);
                    for (int i = 0; i < edgeEditSteps.Count(); i++) EditorBackup.Remove(edgeEditSteps.ElementAt(i));

                    if (removeEdge.StartNode.NumberOfEdgeReference <= 1)
                    {
                        Nodes.Remove(removeEdge.StartNode);
                        var element = Elements.FirstOrDefault(el => el.NodeId == removeEdge.StartNode.Id);
                        if (element is not null) Elements.Remove(element);
                        var nodeEditSteps = EditorBackup.Where(step => step.Type == MapEditorBackupType.Node && step.Id == removeEdge.StartNode.Id);
                        for (int i = 0; i < nodeEditSteps.Count(); i++) EditorBackup.Remove(nodeEditSteps.ElementAt(i));
                    }

                    if (removeEdge.EndNode.NumberOfEdgeReference <= 1)
                    {
                        Nodes.Remove(removeEdge.EndNode);
                        var element = Elements.FirstOrDefault(el => el.NodeId == removeEdge.EndNode.Id);
                        if (element is not null) Elements.Remove(element);
                        var nodeEditSteps = EditorBackup.Where(step => step.Type == MapEditorBackupType.Node && step.Id == removeEdge.EndNode.Id);
                        for (int i = 0; i < nodeEditSteps.Count(); i++) EditorBackup.Remove(nodeEditSteps.ElementAt(i));
                    }
                }

                await OnSelectedEdgeChanged(null);
                await MultiselectedEdgeChanged.InvokeAsync(false);
                await MultiselectedNodeChanged.InvokeAsync(false);

                Snackbar.Add("Xóa Edge thành công", Severity.Success);
                OverlayIsVisible = false;
            }
            else await LoadMap();
            StateHasChanged();
        }

    }

    public async Task CreateZone(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4, ZoneType type)
    {
        if (MapData is null) { Snackbar.Add("Dữ liệu bản đồ rỗng", Severity.Warning); return; }
        if (MapData.Active) { Snackbar.Add("Không thể tạo edge do bản đồ đang được Active", Severity.Warning); return; }

        var result = await (await Http.PostAsJsonAsync($"api/Zones", new ZoneCreateModel()
        {
            MapId = MapData.Id,
            X1 = x1,
            Y1 = y1,
            X2 = x2,
            Y2 = y2,
            X3 = x3,
            Y3 = y3,
            X4 = x4,
            Y4 = y4,
            Type = type
        })).Content.ReadFromJsonAsync<MessageResult<ZoneDto>>();

        if (result is null) { Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error); return; }
        else if (!result.IsSuccess) { Snackbar.Add(result.Message ?? "Tạo Zone không thành công", Severity.Error); return; }
        else if (result.Data is null) { Snackbar.Add("Lỗi dữ liệu trả về", Severity.Error); return; }

        Zones.Add(result.Data);
        Snackbar.Add("Tạo Zone mới thành công", Severity.Success);
    }

    public async Task DeleteZone()
    {
        if (EditorBackup.Count > 0)
        {
            if (!await SaveChanged()) return;
        }

        if (Zones.ZoneActived is null) return;
        if (MapData is null) { Snackbar.Add("Dữ liệu bản đồ rỗng", Severity.Warning); return; }
        if (MapData.Active) { Snackbar.Add("Không thể tạo edge do bản đồ đang được Active", Severity.Warning); return; }

        var parameters = new DialogParameters<ConfirmDialog>
        {
            { x => x.Content, "Bạn chắc chắn muốn xóa Zone đi không?" },
            { x => x.ConfirmText, "Delete" },
            { x => x.Color, Color.Secondary }
        };
        var Confirm = await Dialog.ShowAsync<ConfirmDialog>("Xoá Zone", parameters);
        var ConfirmResult = await Confirm.Result;
        if (ConfirmResult is not null && ConfirmResult.Data is not null && bool.TryParse(ConfirmResult.Data.ToString(), out bool data) && data)
        {

            var result = await Http.DeleteFromJsonAsync<MessageResult>($"/api/zones/{Zones.ZoneActived.Id}");
            if (result is null) { Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error); return; }
            else if (!result.IsSuccess) { Snackbar.Add(result.Message ?? "Xoá Zone không thành công", Severity.Error); return; }


            var removeZone = Zones.ZoneActived;
            Zones.Remove(removeZone);
            var zoneEditSteps = EditorBackup.Where(step => step.Id == removeZone.Id);
            for (int i = 0; i < zoneEditSteps.Count(); i++) EditorBackup.Remove(zoneEditSteps.ElementAt(i));

            ZoneControlPointRef.SetControl(null);
            Snackbar.Add("Xoá Zone thành công", Severity.Success);
        }
    }

    public async Task<bool> SaveChanged()
    {
        if (EditorBackup.Count == 0) return true;
        if (MapData is null) { Snackbar.Add("Dữ liệu bản đồ rỗng", Severity.Warning); return false; }
        if (MapData.Active) { Snackbar.Add("Không thể tạo edge do bản đồ đang được Active", Severity.Warning); return false; }

        var parameters = new DialogParameters<ConfirmDialog>
        {
            { x => x.Content, "Có dữ liệu thay đổi, bạn có muốn lưu lại không?" },
            { x => x.ConfirmText, "Save" },
            { x => x.Color, Color.Primary }
        };
        var Confirm = await Dialog.ShowAsync<ConfirmDialog>("Lưu", parameters);
        var ConfirmResult = await Confirm.Result;
        if (ConfirmResult is not null && ConfirmResult.Data is not null && bool.TryParse(ConfirmResult.Data.ToString(), out bool data) && data)
        {
            var editBackupSteps = new List<MapEditorBackup>();
            foreach (var backup in EditorBackup)
            {
                switch (backup.Type)
                {
                    case MapEditorBackupType.Node:
                        if (Nodes.TryGetValue(backup.Id, out var node) && node != null)
                        {
                            editBackupSteps.Add(new()
                            {
                                Type = MapEditorBackupType.Node,
                                Id = node.Id,
                                Obj = new PositionBackup()
                                {
                                    Id = node.Id,
                                    X = node.X,
                                    Y = node.Y,
                                }
                            });
                        }
                        break;
                    case MapEditorBackupType.ControlPoint1Edge:
                        if (Edges.TryGetValue(backup.Id, out var edge1) && edge1 is not null)
                        {
                            editBackupSteps.Add(new()
                            {
                                Type = MapEditorBackupType.ControlPoint1Edge,
                                Id = edge1.Id,
                                Obj = new PositionBackup()
                                {
                                    Id = edge1.Id,
                                    X = edge1.ControlPoint1X,
                                    Y = edge1.ControlPoint1Y,
                                }
                            });
                        }
                        break;
                    case MapEditorBackupType.ControlPoint2Edge:
                        if (Edges.TryGetValue(backup.Id, out var edge2) && edge2 is not null)
                        {
                            editBackupSteps.Add(new()
                            {
                                Type = MapEditorBackupType.ControlPoint2Edge,
                                Id = edge2.Id,
                                Obj = new PositionBackup()
                                {
                                    Id = edge2.Id,
                                    X = edge2.ControlPoint2X,
                                    Y = edge2.ControlPoint2Y,
                                }
                            });
                        }
                        break;
                    case MapEditorBackupType.Zone:
                        if (Zones.TryGetValue(backup.Id, out var zone) && zone is not null)
                        {
                            editBackupSteps.Add(new()
                            {
                                Type = MapEditorBackupType.Zone,
                                Id = zone.Id,
                                Obj = new ZoneShapeBackup()
                                {
                                    X1 = zone.X1,
                                    Y1 = zone.Y1,
                                    X2 = zone.X2,
                                    Y2 = zone.Y2,
                                    X3 = zone.X3,
                                    Y3 = zone.Y3,
                                    X4 = zone.X4,
                                    Y4 = zone.Y4,
                                }
                            });
                        }
                        break;
                    case MapEditorBackupType.MoveNode:
                        if (backup.Obj is List<PositionBackup> nodesChange)
                        {
                            foreach (var nodeChange in nodesChange)
                            {
                                if (Nodes.TryGetValue(nodeChange.Id, out var nodemove) && nodemove != null)
                                {
                                    editBackupSteps.Add(new()
                                    {
                                        Type = MapEditorBackupType.Node,
                                        Id = nodemove.Id,
                                        Obj = new PositionBackup()
                                        {
                                            Id = nodemove.Id,
                                            X = nodemove.X,
                                            Y = nodemove.Y,
                                        }
                                    });
                                }
                            }
                        }
                        break;
                    case MapEditorBackupType.MoveEdge:
                        List<EdgeBackup> saveEdge = [];
                        if (backup.Obj is List<EdgeBackup> edgesBackup)
                        {
                            foreach (var edgeChange in edgesBackup)
                            {
                                if (Edges.TryGetValue(edgeChange.Id, out var edge) && edge != null)
                                {
                                    saveEdge.Add(new()
                                    {
                                        Id = edge.Id,
                                        TrajectoryDegree = edge.TrajectoryDegree,
                                        StartX = edge.X1,
                                        StartY = edge.Y1,
                                        EndX = edge.X2,
                                        EndY = edge.Y2,
                                        ControlPoint1X = edge.ControlPoint1X,
                                        ControlPoint1Y = edge.ControlPoint1Y,
                                        ControlPoint2X = edge.ControlPoint2X,
                                        ControlPoint2Y = edge.ControlPoint2Y
                                    });
                                }
                            }
                        }
                        editBackupSteps.Add(new()
                        {
                            Type = MapEditorBackupType.MoveEdge,
                            Obj = saveEdge,
                        });
                        break;
                    case MapEditorBackupType.Copy:
                        if (MapCopyRef.isMoving)
                        {
                            List<EdgeMapCopyModel> edgeCopyModel = [.. MapCopyRef.EdgeModels.Select(e => new EdgeMapCopyModel()
                            {
                                X1 = e.X1,
                                Y1 = e.Y1,
                                X2 = e.X2,
                                Y2 = e.Y2,
                                MapId = e.MapId,
                                TrajectoryDegree = e.TrajectoryDegree,
                                StartNodeId = e.StartNode.Id,
                                EndNodeId = e.EndNode.Id,
                                ControlPoint1X = e.ControlPoint1X,
                                ControlPoint1Y = e.ControlPoint1Y,
                                ControlPoint2X = e.ControlPoint2X,
                                ControlPoint2Y = e.ControlPoint2Y,
                                Actions = e.Actions,
                                AllowedDeviationTheta = e.AllowedDeviationTheta,
                                AllowedDeviationXy = e.AllowedDeviationXy,
                                DirectionAllowed = e.DirectionAllowed,
                                RotationAllowed = e.RotationAllowed,
                                MaxHeight = e.MaxHeight,
                                MinHeight = e.MinHeight,
                                MaxRotationSpeed = e.MaxRotationSpeed,
                                MaxSpeed = e.MaxSpeed,
                            })];
                            editBackupSteps.Add(new()
                            {
                                Type = MapEditorBackupType.Copy,
                                Obj = edgeCopyModel,
                            });
                        }
                        else MapCopyRef.OnShow(false, []);
                        break;
                    case MapEditorBackupType.SplitNode:
                        editBackupSteps.Add(backup);
                        break;
                    case MapEditorBackupType.MergeNode:
                        if (backup.Obj is MergeNodeBackup mergeBackup)
                        {
                            Dictionary<Guid, Guid> edgeMerge = [];
                            mergeBackup.EdgesMerge.ForEach(e => edgeMerge.Add(e.edgeId, e.node.Id));
                            editBackupSteps.Add(new()
                            {
                                Type = MapEditorBackupType.MergeNode,
                                Id = mergeBackup.NodeId,
                                Obj = new MergeNodeUpdate()
                                {
                                    NodeId = mergeBackup.NodeId,
                                    EdgesMerge = edgeMerge,
                                }
                            });
                        }
                        break;
                }

            }

            var result = await (await Http.PutAsJsonAsync($"api/MapsData/{MapData.Id}/updates", new MapEditorBackupModel() { Steps = [.. editBackupSteps], })).Content.ReadFromJsonAsync<MessageResult<IEnumerable<EdgeDto>>>();
            if (result is null) { Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error); return false; }
            else if (!result.IsSuccess) { Snackbar.Add(result.Message ?? "Tạo Edge không thành công", Severity.Error); return false; }
            else if (result.Data is null) { Snackbar.Add("Lỗi dữ liệu trả về", Severity.Error); return false; }
            else if (result.Data.Any())
            {
                foreach (var edge in result.Data)
                {
                    if (edge.StartNode is null || edge.EndNode is null) continue;
                    if (!Nodes.ContainsKey(edge.StartNodeId)) Nodes.Add(edge.StartNode);
                    if (!Nodes.ContainsKey(edge.EndNodeId)) Nodes.Add(edge.EndNode);
                    Edges.Add(edge, Nodes[edge.StartNodeId], Nodes[edge.EndNodeId]);
                }
                MapCopyRef.OnShow(false, []);
            }

            EditorBackup.Clear();
            await NodesUndoableChanged.InvokeAsync(false);
            Snackbar.Add("Cập nhật thành công", Severity.Success);
            return true;
        }
        return false;
    }

    public void UndoEditorBackup()
    {
        if (EditorBackup.Count == 0 || MapData.Active) return;

        var lastChange = EditorBackup[^1];
        EditorBackup.Remove(lastChange);

        switch (lastChange.Type)
        {
            case MapEditorBackupType.Node:
                if (lastChange.Obj is PositionBackup nodepos)
                {
                    if (Nodes.TryGetValue(lastChange.Id, out var node) && node != null)
                    {
                        node.UpdatePosition(nodepos.X, nodepos.Y);
                    }
                }
                break;
            case MapEditorBackupType.ControlPoint1Edge:
                if (lastChange.Obj is PositionBackup cppos)
                {
                    if (Edges.TryGetValue(lastChange.Id, out var edge) && edge != null)
                    {
                        edge.UpdateControlPoint1(cppos.X, cppos.Y);
                    }
                }
                break;
            case MapEditorBackupType.ControlPoint2Edge:
                if (lastChange.Obj is PositionBackup cp2pos)
                {
                    if (Edges.TryGetValue(lastChange.Id, out var edge) && edge != null)
                    {
                        edge.UpdateControlPoint2(cp2pos.X, cp2pos.Y);
                    }
                }
                break;
            case MapEditorBackupType.Zone:
                if (lastChange.Obj is ZoneShapeBackup zoneshape)
                {
                    if (Zones.TryGetValue(lastChange.Id, out var zone) && zone is not null)
                    {
                        zone.UpdateControlNode(zoneshape.X1, zoneshape.Y1, zoneshape.X2, zoneshape.Y2, zoneshape.X3, zoneshape.Y3, zoneshape.X4, zoneshape.Y4);
                    }
                }
                break;
            case MapEditorBackupType.MoveNode:
                if (lastChange.Obj is List<PositionBackup> nodesbackup)
                {
                    foreach (var node in nodesbackup)
                    {
                        if (Nodes.TryGetValue(node.Id, out var model) && model != null)
                        {
                            model.UpdatePosition(node.X, node.Y);
                        }
                    }
                }
                break;
            case MapEditorBackupType.MoveEdge:
                if (lastChange.Obj is List<EdgeBackup> edgesBackup)
                {
                    foreach (var edge in edgesBackup)
                    {
                        if (Edges.TryGetValue(edge.Id, out var model) && model != null)
                        {
                            model.StartNode.UpdatePosition(edge.StartX, edge.StartY);
                            model.EndNode.UpdatePosition(edge.EndX, edge.EndY);
                            if (model.TrajectoryDegree == TrajectoryDegree.Two || model.TrajectoryDegree == TrajectoryDegree.Three) model.UpdateControlPoint1(edge.ControlPoint1X, edge.ControlPoint1Y);
                            if (model.TrajectoryDegree == TrajectoryDegree.Three) model.UpdateControlPoint2(edge.ControlPoint2X, edge.ControlPoint2Y);
                        }
                    }
                }
                break;
            case MapEditorBackupType.Copy:
                MapCopyRef.OnShow(false, []);
                break;
            case MapEditorBackupType.SplitNode:
                if (lastChange.Obj is SplitNodeBackup splitBackup)
                {
                    var node = Nodes[splitBackup.NodeId];
                    if (node is not null)
                    {
                        foreach (var data in splitBackup.EdgeSplit)
                        {
                            var edge = Edges[data.Key];
                            if (edge is not null && (edge.StartNode.Id == data.Value.Id || edge.EndNode.Id == data.Value.Id))
                            {
                                edge.UpdateNode(data.Value.Id, node);
                                Nodes.Remove(Nodes[data.Value.Id]);
                            }
                        }
                    }
                }
                break;
            case MapEditorBackupType.MergeNode:
                if (lastChange.Obj is MergeNodeBackup mergeBackup)
                {
                    foreach (var (edgeId, node) in mergeBackup.EdgesMerge)
                    {
                        var edge = Edges[edgeId];
                        if (edge is not null && (edge.StartNode.Id == mergeBackup.NodeId || edge.EndNode.Id == mergeBackup.NodeId))
                        {
                            Nodes.Add(new NodeDto()
                            {
                                Id = node.Id,
                                MapId = node.MapId,
                                Name = node.Name,
                                X = node.X,
                                Y = node.Y,
                                Theta = node.Theta,
                                AllowedDeviationXy = node.AllowedDeviationXy,
                                AllowedDeviationTheta = node.AllowedDeviationTheta,
                                Actions = node.Actions
                            });
                            edge.UpdateNode(mergeBackup.NodeId, Nodes[node.Id]);
                        }
                    }
                }
                break;
        }

        NodesUndoableChanged.InvokeAsync(EditorBackup.Count > 0);
    }

    public void HorizontalLeft()
    {
        if (Nodes.ActivedNodes.Count > 0)
        {
            List<PositionBackup> ActiveNode = [];
            double MinX = Nodes.ActivedNodes.First().X;
            foreach (var node in Nodes.ActivedNodes)
            {
                ActiveNode.Add(new()
                {
                    Id = node.Id,
                    X = node.X,
                    Y = node.Y,
                });
                if (MinX > node.X) MinX = node.X;
            }

            EditorBackup.Add(new()
            {
                Type = MapEditorBackupType.MoveNode,
                Obj = ActiveNode,
            });
            NodesUndoableChanged.InvokeAsync(true);

            foreach (var node in Nodes.ActivedNodes)
            {
                node.UpdatePosition(MinX, node.Y);
            }
        }
    }

    public void HorizontalRight()
    {
        if (Nodes.ActivedNodes.Count > 0)
        {
            List<PositionBackup> ActiveNode = [];
            double MaxX = Nodes.ActivedNodes.First().X;
            foreach (var node in Nodes.ActivedNodes)
            {
                ActiveNode.Add(new()
                {
                    Id = node.Id,
                    X = node.X,
                    Y = node.Y,
                });
                if (MaxX < node.X) MaxX = node.X;
            }

            EditorBackup.Add(new()
            {
                Type = MapEditorBackupType.MoveNode,
                Obj = ActiveNode,
            });
            NodesUndoableChanged.InvokeAsync(true);

            foreach (var node in Nodes.ActivedNodes)
            {
                node.UpdatePosition(MaxX, node.Y);
            }
        }
    }

    public void VerticalTop()
    {
        if (Nodes.ActivedNodes.Count > 0)
        {
            List<PositionBackup> ActiveNode = [];
            double MaxY = Nodes.ActivedNodes.First().Y;
            foreach (var node in Nodes.ActivedNodes)
            {
                ActiveNode.Add(new()
                {
                    Id = node.Id,
                    X = node.X,
                    Y = node.Y,
                });
                if (MaxY < node.Y) MaxY = node.Y;
            }

            EditorBackup.Add(new()
            {
                Type = MapEditorBackupType.MoveNode,
                Obj = ActiveNode,
            });
            NodesUndoableChanged.InvokeAsync(true);

            foreach (var node in Nodes.ActivedNodes)
            {
                node.UpdatePosition(node.X, MaxY);
            }
        }
    }

    public void VerticalBottom()
    {
        if (Nodes.ActivedNodes.Count > 0)
        {
            List<PositionBackup> ActiveNode = [];
            double MinY = Nodes.ActivedNodes.First().Y;
            foreach (var node in Nodes.ActivedNodes)
            {
                ActiveNode.Add(new()
                {
                    Id = node.Id,
                    X = node.X,
                    Y = node.Y,
                });
                if (MinY > node.Y) MinY = node.Y;
            }

            EditorBackup.Add(new()
            {
                Type = MapEditorBackupType.MoveNode,
                Obj = ActiveNode,
            });
            NodesUndoableChanged.InvokeAsync(true);

            foreach (var node in Nodes.ActivedNodes)
            {
                node.UpdatePosition(node.X, MinY);
            }
        }
    }

    public void HorizontalCenter()
    {
        if (Nodes.ActivedNodes.Count > 2)
        {
            List<PositionBackup> ActiveNode = [];
            double MinX = Nodes.ActivedNodes.First().X;
            double MaxX = Nodes.ActivedNodes.First().X;
            foreach (var node in Nodes.ActivedNodes)
            {
                ActiveNode.Add(new()
                {
                    Id = node.Id,
                    X = node.X,
                    Y = node.Y,
                });
                if (MinX > node.X) MinX = node.X;
                if (MaxX < node.X) MaxX = node.X;
            }

            EditorBackup.Add(new()
            {
                Type = MapEditorBackupType.MoveNode,
                Obj = ActiveNode,
            });
            NodesUndoableChanged.InvokeAsync(true);

            List<NodeModel> NodeSort = [.. Nodes.ActivedNodes.OrderBy(n => n.X)];
            var offset = (MaxX - MinX) / (Nodes.ActivedNodes.Count - 1);

            for (int i = 1; i < NodeSort.Count - 1; i++)
            {
                var node = Nodes.ActivedNodes.FirstOrDefault(n => n.Id == NodeSort[i].Id);
                node?.UpdatePosition(NodeSort[0].X + offset * i, node.Y);
            }
        }
    }

    public void VerticalCenter()
    {
        if (Nodes.ActivedNodes.Count > 2)
        {
            List<PositionBackup> ActiveNode = [];
            double MinY = Nodes.ActivedNodes.First().Y;
            double MaxY = Nodes.ActivedNodes.First().Y;
            foreach (var node in Nodes.ActivedNodes)
            {
                ActiveNode.Add(new()
                {
                    Id = node.Id,
                    X = node.X,
                    Y = node.Y,
                });
                if (MinY > node.Y) MinY = node.Y;
                if (MaxY < node.Y) MaxY = node.Y;
            }

            EditorBackup.Add(new()
            {
                Type = MapEditorBackupType.MoveNode,
                Obj = ActiveNode,
            });
            NodesUndoableChanged.InvokeAsync(true);

            List<NodeModel> NodeSort = [.. Nodes.ActivedNodes.OrderBy(n => n.Y)];
            var offset = (MaxY - MinY) / (Nodes.ActivedNodes.Count - 1);

            for (int i = 1; i < NodeSort.Count - 1; i++)
            {
                var node = Nodes.ActivedNodes.FirstOrDefault(n => n.Id == NodeSort[i].Id);
                node?.UpdatePosition(node.X, NodeSort[0].Y + offset * i);
            }
        }
    }

    public void ZoneUpdateShape(double x, double y)
    {
        if (Zones.ZoneActived is not null)
        {
            if (EditorBackup.Count == 0 || EditorBackup.Last().Id != Zones.ZoneActived.Id || EditorBackup.Count > 0 && EditorBackup.Last().Obj is ZoneShapeBackup zonebackup && zonebackup.NodeChange != Zones.ZoneActived.ActiveNode)
            {
                EditorBackup.Add(new()
                {
                    Id = Zones.ZoneActived.Id,
                    Type = MapEditorBackupType.Zone,
                    Obj = new ZoneShapeBackup()
                    {
                        NodeChange = Zones.ZoneActived.ActiveNode,
                        X1 = Zones.ZoneActived.X1,
                        Y1 = Zones.ZoneActived.Y1,
                        X2 = Zones.ZoneActived.X2,
                        Y2 = Zones.ZoneActived.Y2,
                        X3 = Zones.ZoneActived.X3,
                        Y3 = Zones.ZoneActived.Y3,
                        X4 = Zones.ZoneActived.X4,
                        Y4 = Zones.ZoneActived.Y4,
                    }
                });
                NodesUndoableChanged.InvokeAsync(true);
            }
            if (Zones.ZoneActived.ActiveNode > 0) Zones.ZoneActived.UpdateControlNode(Zones.ZoneActived.ActiveNode, x, y);
        }
    }

    public void EdgesPositionMove(double x, double y)
    {
        if (EditorBackup.Count == 0)
        {
            List<EdgeBackup> BackupEdge = [];
            foreach (var edge in Edges.ActivedEdges)
            {
                BackupEdge.Add(new()
                {
                    Id = edge.Id,
                    TrajectoryDegree = edge.TrajectoryDegree,
                    StartX = edge.X1,
                    StartY = edge.Y1,
                    EndX = edge.X2,
                    EndY = edge.Y2,
                    ControlPoint1X = edge.ControlPoint1X,
                    ControlPoint1Y = edge.ControlPoint1Y,
                    ControlPoint2X = edge.ControlPoint2X,
                    ControlPoint2Y = edge.ControlPoint2Y,
                });
            }
            EditorBackup.Add(new()
            {
                Type = MapEditorBackupType.MoveEdge,
                Obj = BackupEdge,
            });
            NodesUndoableChanged.InvokeAsync(true);
        }
        Edges.UpdateMoveEdge(x, y);
    }

    public void NodesPositionMove(double x, double y)
    {
        if (EditorBackup.Count == 0)
        {
            List<PositionBackup> ActiveNode = [];
            foreach (var node in Nodes.ActivedNodes)
            {
                ActiveNode.Add(new()
                {
                    Id = node.Id,
                    X = x,
                    Y = y,
                });
            }
            EditorBackup.Add(new()
            {
                Type = MapEditorBackupType.MoveNode,
                Obj = ActiveNode,
            });
            NodesUndoableChanged.InvokeAsync(true);
        }
        Nodes.UpdateMoveNode(x, y);
    }

    public void NodePositionMove(double x, double y)
    {
        if (Nodes.SelectedNode is not null)
        {
            if (EditorBackup.Count == 0 || EditorBackup.Last().Id != Nodes.SelectedNode.Id)
            {
                EditorBackup.Add(new()
                {
                    Id = Nodes.SelectedNode.Id,
                    Type = MapEditorBackupType.Node,
                    Obj = new PositionBackup()
                    {
                        Id = Nodes.SelectedNode.Id,
                        X = Nodes.SelectedNode.X,
                        Y = Nodes.SelectedNode.Y,
                    }
                });
                NodesUndoableChanged.InvokeAsync(true);
            }

            Nodes.SelectedNode.UpdatePosition(x, y);
        }
        else if (Edges.ActivedEdges.Count == 1)
        {
            if (Edges.ActivedEdges[0].ActivedControlPoint1)
            {
                if (EditorBackup.Count == 0 || EditorBackup.Last().Id != Edges.ActivedEdges[0].Id || EditorBackup.Last().Id == Edges.ActivedEdges[0].Id && EditorBackup.Last().Type != MapEditorBackupType.ControlPoint1Edge)
                {
                    EditorBackup.Add(new()
                    {
                        Id = Edges.ActivedEdges[0].Id,
                        Type = MapEditorBackupType.ControlPoint1Edge,
                        Obj = new PositionBackup()
                        {
                            Id = Edges.ActivedEdges[0].Id,
                            X = Edges.ActivedEdges[0].ControlPoint1X,
                            Y = Edges.ActivedEdges[0].ControlPoint1Y,
                        }
                    });
                    NodesUndoableChanged.InvokeAsync(true);
                }
                Edges.ActivedEdges[0].UpdateControlPoint1(x, y);
            }
            else if (Edges.ActivedEdges[0].ActivedControlPoint2)
            {
                if (EditorBackup.Count == 0 || EditorBackup.Last().Id != Edges.ActivedEdges[0].Id || EditorBackup.Last().Id == Edges.ActivedEdges[0].Id && EditorBackup.Last().Type != MapEditorBackupType.ControlPoint2Edge)
                {
                    EditorBackup.Add(new()
                    {
                        Id = Edges.ActivedEdges[0].Id,
                        Type = MapEditorBackupType.ControlPoint2Edge,
                        Obj = new PositionBackup()
                        {
                            Id = Edges.ActivedEdges[0].Id,
                            X = Edges.ActivedEdges[0].ControlPoint2X,
                            Y = Edges.ActivedEdges[0].ControlPoint2Y,
                        }
                    });
                    NodesUndoableChanged.InvokeAsync(true);
                }
                Edges.ActivedEdges[0].UpdateControlPoint2(x, y);
            }
        }
    }

    public async Task SplitNode()
    {
        if (EditorBackup.Count > 0)
        {
            if (!await SaveChanged()) return;
        }

        if (MapData is null) { Snackbar.Add("Dữ liệu bản đồ rỗng", Severity.Warning); return; }
        if (MapData.Active) { Snackbar.Add("Không thể tạo edge do bản đồ đang được Active", Severity.Warning); return; }

        if (Nodes.ActivedNodes.Count > 1) { Snackbar.Add("Vui lòng chỉ chọn 1 Node để tách", Severity.Warning); return; }
        if (Nodes.ActivedNodes.Count == 0) { Snackbar.Add("Vui lòng chọn 1 Node để tách", Severity.Warning); return; }

        var nodeSplit = Nodes.ActivedNodes[0];

        var edges = Edges.Where(edge => edge.StartNode.Id == nodeSplit.Id || edge.EndNode.Id == nodeSplit.Id).ToList();
        if (edges.Count <= 1) { Snackbar.Add("Không thể tách node đơn", Severity.Warning); return; }

        Dictionary<Guid, NodeDto> EdgeSplit = [];
        for (int i = 1; i < edges.Count; i++)
        {
            var newNode = new NodeDto()
            {
                Id = Guid.NewGuid(),
                Name = string.Empty,
                X = nodeSplit.X,
                Y = nodeSplit.Y,
                Theta = nodeSplit.Theta,
                MapId = nodeSplit.MapId,
                AllowedDeviationXy = nodeSplit.AllowedDeviationXy,
                AllowedDeviationTheta = nodeSplit.AllowedDeviationTheta,
                Actions = nodeSplit.Actions,
            };
            Nodes.Add(newNode);
            edges[i].UpdateNode(nodeSplit.Id, Nodes[newNode.Id]);
            EdgeSplit.Add(edges[i].Id, newNode);
        }
        Nodes.UnActivedNode();
        EditorBackup.Add(new()
        {
            Type = MapEditorBackupType.SplitNode,
            Id = nodeSplit.Id,
            Obj = new SplitNodeBackup()
            {
                NodeId = nodeSplit.Id,
                EdgeSplit = EdgeSplit,
            },
        });
        await NodesUndoableChanged.InvokeAsync(true);
        Snackbar.Add("Tách Node thành công", Severity.Success);
    }

    public async Task MergeNode()
    {
        if (EditorBackup.Count > 0)
        {
            if (!await SaveChanged()) return;
        }

        if (MapData is null) { Snackbar.Add("Dữ liệu bản đồ rỗng", Severity.Warning); return; }
        if (MapData.Active) { Snackbar.Add("Không thể tạo edge do bản đồ đang được Active", Severity.Warning); return; }

        if (Nodes.ActivedNodes.Count <= 1) { Snackbar.Add("Vui lòng chọn nhiều hơn 1 Node để gộp", Severity.Warning); return; }
        if (Edges.ActivedEdges.Count > 0) { Snackbar.Add("Vui lòng không chọn Edge", Severity.Warning); return; }

        MergeNodeBackup MergeNodeBackup = new()
        {
            NodeId = Nodes.ActivedNodes[0].Id,
        };
        List<NodeModel> activedNodes = [.. Nodes.ActivedNodes];
        Nodes.UnActivedNode();
        for (int i = 1; i < activedNodes.Count; i++)
        {
            var edges = Edges.Where(e => e.StartNode.Id == activedNodes[i].Id || e.EndNode.Id == activedNodes[i].Id).ToList();
            if (edges.Count > 0)
            {
                foreach (var edge in edges)
                {
                    edge.UpdateNode(activedNodes[i].Id, Nodes[activedNodes[0].Id]);
                    MergeNodeBackup.EdgesMerge.Add((edge.Id, activedNodes[i]));
                    Nodes.Remove(activedNodes[i]);
                }
            }
        }
        EditorBackup.Add(new()
        {
            Type = MapEditorBackupType.MergeNode,
            Id = activedNodes[0].Id,
            Obj = MergeNodeBackup,
        });
        await NodesUndoableChanged.InvokeAsync(true);
        Snackbar.Add("Gộp Node thành công", Severity.Success);
    }

    public async Task ScanerActive(double x1, double y1, double x2, double y2)
    {
        List<EdgeModel> activeEdges = [];
        List<NodeModel> activeNodes = [];
        foreach (var edge in Edges)
        {
            if (MapEditorHelper.NodeInScanZone(edge.StartNode.X, edge.StartNode.Y, x1, y1, x2, y2) && MapEditorHelper.NodeInScanZone(edge.EndNode.X, edge.EndNode.Y, x1, y1, x2, y2))
            {
                activeEdges.Add(edge);
                if (!activeNodes.Any(n => n.Id == edge.StartNode.Id)) activeNodes.Add(edge.StartNode);
                if (!activeNodes.Any(n => n.Id == edge.EndNode.Id)) activeNodes.Add(edge.EndNode);
            }
        }
        foreach (var node in Nodes)
        {
            if (!activeNodes.Any(n => n.Id == node.Id) && MapEditorHelper.NodeInScanZone(node.X, node.Y, x1, y1, x2, y2)) activeNodes.Add(node);
        }

        Edges.ActivedEdge(activeEdges);
        Nodes.ActivedNode(activeNodes);

        await MultiselectedNodeChanged.InvokeAsync(activeNodes.Count > 0);
        await MultiselectedEdgeChanged.InvokeAsync(activeEdges.Count > 0);
    }

    public async Task CheckMap()
    {
        await MapIsChecking.InvokeAsync(true);

        var mapSetting = await Http.GetFromJsonAsync<MessageResult<MapSettingDefaultDto>>($"api/MapsSetting/{MapData.Id}");

        if (mapSetting is null) { Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error); return; }
        else if (!mapSetting.IsSuccess) { Snackbar.Add(mapSetting.Message ?? "Lấy dữ liệu bản đồ không thành công", Severity.Error); return; }
        else if (mapSetting.Data == null) { Snackbar.Add("Lỗi dữ liệu trả về", Severity.Error); return; }

        foreach (var edge in Edges)
        {
            var caculateEdge = new EdgeCaculatorModel()
            {
                MapId = edge.MapId,
                X1 = edge.X1,
                Y1 = edge.Y1,
                X2 = edge.X2,
                Y2 = edge.Y2,
                ControlPoint1X = edge.ControlPoint1X,
                ControlPoint1Y = edge.ControlPoint1Y,
                ControlPoint2X = edge.ControlPoint2X,
                ControlPoint2Y = edge.ControlPoint2Y,
                TrajectoryDegree = edge.TrajectoryDegree,
            };
            if (MapEditorHelper.GetEdgeLength(caculateEdge) < mapSetting.Data.EdgeMinLength) edge.SetError(true);
            else edge.SetError(false);
        }

        List<ZoneModel> ErrorZones = [];
        foreach (var zone in Zones)
        {
            if (MapEditorHelper.CalculateQuadrilateralArea(zone.X1, zone.Y1, zone.X2, zone.Y2, zone.X3, zone.Y3, zone.X4, zone.Y4) < mapSetting.Data.ZoneMinSquare)
            {
                ErrorZones.Add(zone);
            }
        }

        List<NodeModel> ErrorNodes = [];
        foreach (var node in Nodes)
        {
            if (ErrorNodes.Any(e => e.Id == node.Id)) continue;
            foreach (var checkNode in Nodes)
            {
                if (checkNode.Id == node.Id) continue;
                var distance = Math.Sqrt(Math.Pow(node.X - checkNode.X, 2) + Math.Pow(node.Y - checkNode.Y, 2));

                if (distance < mapSetting.Data.EdgeMinLength)
                {
                    if (!Edges.Any(e => e.StartNode.Id == node.Id && e.EndNode.Id == checkNode.Id || e.EndNode.Id == node.Id && e.StartNode.Id == checkNode.Id))
                    {
                        ErrorNodes.Add(node);
                        node.SetError(true);
                        break;
                    }
                }
            }
            if (!ErrorNodes.Any(e => e.Id == node.Id)) node.SetError(false);
        }

        await MapIsChecking.InvokeAsync(false);
        Snackbar.Add("Kiểm tra xong", Severity.Success);
    }
}
