using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using RobotNet.MapManager.Data;
using RobotNet.MapManager.Services;
using RobotNet.MapShares;
using RobotNet.MapShares.Dtos;
using RobotNet.MapShares.Models;
using RobotNet.Shares;
using System.Text.Json;

namespace RobotNet.MapManager.Controllers;

[Route("api/[controller]")]
[ApiController]
[Authorize]
public class MapsDataController(MapEditorDbContext MapDb, LoggerController<MapsDataController> Logger) : ControllerBase
{
    [HttpGet]
    [Route("{id}")]
    public async Task<MessageResult<MapDataDto>> GetMapData(Guid id)
    {
        var map = await MapDb.Maps.FindAsync(id);
        if (map is null) return new(false, $"Không tìm thấy bản đồ: {id}");

        var result = new MessageResult<MapDataDto>(true)
        {
            Data = new()
            {
                Id = map.Id,
                Name = map.Name,
                OriginX = map.OriginX,
                OriginY = map.OriginY,
                Resolution = map.Resolution,
                ImageHeight = map.ImageHeight,
                ImageWidth = map.ImageWidth,
                Active = map.Active,
                Nodes = [.. MapDb.Nodes.Where(node => node.MapId == id).Select(node => new NodeDto()
                {
                    Id = node.Id,
                    MapId = node.MapId,
                    Name = node.Name,
                    Theta = node.Theta,
                    X = node.X,
                    Y = node.Y,
                    AllowedDeviationXy = node.AllowedDeviationXy,
                    AllowedDeviationTheta = node.AllowedDeviationTheta,
                    Actions = node.Actions,
                })],
                Edges = [.. MapDb.Edges.Where(edge => edge.MapId == id).Select(edge => new EdgeDto()
                {
                    Id = edge.Id,
                    MapId = edge.MapId,
                    StartNodeId = edge.StartNodeId,
                    EndNodeId = edge.EndNodeId,

                    MaxSpeed = edge.MaxSpeed,
                    MaxHeight = edge.MaxHeight,
                    MinHeight = edge.MinHeight,
                    DirectionAllowed = edge.DirectionAllowed,
                    RotationAllowed = edge.RotationAllowed,
                    TrajectoryDegree = edge.TrajectoryDegree,
                    ControlPoint1X = edge.ControlPoint1X,
                    ControlPoint1Y = edge.ControlPoint1Y,
                    ControlPoint2X = edge.ControlPoint2X,
                    ControlPoint2Y = edge.ControlPoint2Y,
                    MaxRotationSpeed = edge.MaxRotationSpeed,
                    Actions = edge.Actions,
                    AllowedDeviationXy = edge.AllowedDeviationXy,
                    AllowedDeviationTheta = edge.AllowedDeviationTheta,
                })],
                Zones = [.. MapDb.Zones.Where(zone => zone.MapId == id).Select(zone => new ZoneDto()
                {
                    Id = zone.Id,
                    MapId = zone.MapId,
                    Type = zone.Type,
                    Name = zone.Name,
                    X1 = zone.X1,
                    X2 = zone.X2,
                    Y1 = zone.Y1,
                    Y2 = zone.Y2,
                    X3 = zone.X3,
                    Y3 = zone.Y3,
                    X4 = zone.X4,
                    Y4 = zone.Y4,
                }).OrderBy(z => z.Type)],
                Elements = [.. MapDb.Elements.Where(el => el.MapId == id).Select(element => new ElementDto()
                {
                    Id = element.Id,
                    MapId = element.MapId,
                    ModelId = element.ModelId,
                    Name = element.Name,
                    NodeId = element.NodeId,
                    OffsetX = element.OffsetX,
                    OffsetY = element.OffsetY,
                    IsOpen = element.IsOpen,
                    Content = element.Content,
                })],
                Actions = [.. MapDb.Actions.Where(a => a.MapId == id).Select(action => new ActionDto()
                {
                    Id = action.Id,
                    MapId = action.MapId,
                    Name = action.Name,
                    Content = action.Content,
                })]
            }
        };
        return result;
    }

    [HttpPut]
    [Route("{id}/updates")]
    public async Task<MessageResult<IEnumerable<EdgeDto>>> Updates(Guid id, MapEditorBackupModel model)
    {
        if (model == null || model.Steps == null) return new(false, "Dữ liệu đầu vào không hợp lệ");
        try
        {
            var map = await MapDb.Maps.FindAsync(id);
            if (map == null) return new(false, $"Không tồn tại map id = {id}");

            List<EdgeDto> EdgeDtos = [];
            foreach (var step in model.Steps)
            {
                switch (step.Type)
                {
                    case MapEditorBackupType.Node:
                        PositionBackup? nodeUpdate = JsonSerializer.Deserialize<PositionBackup>(step.Obj.ToString() ?? "", JsonOptionExtends.Read);
                        if (nodeUpdate is not null)
                        {
                            var nodeDb = await MapDb.Nodes.FindAsync(step.Id);
                            if (nodeDb is not null)
                            {
                                nodeDb.X = nodeUpdate.X;
                                nodeDb.Y = nodeUpdate.Y;
                            }
                        }
                        break;
                    case MapEditorBackupType.ControlPoint1Edge:
                        PositionBackup? controlPoint1 = JsonSerializer.Deserialize<PositionBackup>(step.Obj.ToString() ?? "", JsonOptionExtends.Read);
                        if (controlPoint1 is not null)
                        {
                            var edgeDb = await MapDb.Edges.FindAsync(step.Id);
                            if (edgeDb is not null)
                            {
                                edgeDb.ControlPoint1X = controlPoint1.X;
                                edgeDb.ControlPoint1Y = controlPoint1.Y;
                            }
                        }
                        break;
                    case MapEditorBackupType.ControlPoint2Edge:
                        PositionBackup? controlPoint2 = JsonSerializer.Deserialize<PositionBackup>(step.Obj.ToString() ?? "", JsonOptionExtends.Read);
                        if (controlPoint2 is not null)
                        {
                            var edgeDb = await MapDb.Edges.FindAsync(step.Id);
                            if (edgeDb is not null)
                            {
                                edgeDb.ControlPoint2X = controlPoint2.X;
                                edgeDb.ControlPoint2Y = controlPoint2.Y;
                            }
                        }
                        break;
                    case MapEditorBackupType.Zone:
                        ZoneShapeBackup? zoneUpdate = JsonSerializer.Deserialize<ZoneShapeBackup>(step.Obj.ToString() ?? "", JsonOptionExtends.Read);
                        if (zoneUpdate is not null)
                        {
                            var zoneDb = await MapDb.Zones.FindAsync(step.Id);
                            if (zoneDb is not null)
                            {
                                zoneDb.X1 = zoneUpdate.X1;
                                zoneDb.Y1 = zoneUpdate.Y1;
                                zoneDb.X2 = zoneUpdate.X2;
                                zoneDb.Y2 = zoneUpdate.Y2;
                                zoneDb.X3 = zoneUpdate.X3;
                                zoneDb.Y3 = zoneUpdate.Y3;
                                zoneDb.X4 = zoneUpdate.X4;
                                zoneDb.Y4 = zoneUpdate.Y4;
                            }
                        }
                        break;
                    case MapEditorBackupType.MoveEdge:
                        List<EdgeBackup>? edgesUpdate = JsonSerializer.Deserialize<List<EdgeBackup>>(step.Obj.ToString() ?? "", JsonOptionExtends.Read);
                        if (edgesUpdate is not null && edgesUpdate.Count > 0)
                        {
                            foreach (var edgeUpate in edgesUpdate)
                            {
                                var edgeDb = await MapDb.Edges.FindAsync(edgeUpate.Id);
                                if (edgeDb is not null)
                                {
                                    var startNode = await MapDb.Nodes.FindAsync(edgeDb.StartNodeId);
                                    var endNode = await MapDb.Nodes.FindAsync(edgeDb.EndNodeId);
                                    if (startNode is not null && endNode is not null)
                                    {
                                        startNode.X = edgeUpate.StartX;
                                        startNode.Y = edgeUpate.StartY;
                                        endNode.X = edgeUpate.EndX;
                                        endNode.Y = edgeUpate.EndY;
                                    }
                                    edgeDb.ControlPoint1X = edgeUpate.ControlPoint1X;
                                    edgeDb.ControlPoint1Y = edgeUpate.ControlPoint1Y;
                                    edgeDb.ControlPoint2X = edgeUpate.ControlPoint2X;
                                    edgeDb.ControlPoint2Y = edgeUpate.ControlPoint2Y;
                                }
                            }
                        }
                        break;
                    case MapEditorBackupType.Copy:
                        List<EdgeMapCopyModel>? edgesCopy = JsonSerializer.Deserialize<List<EdgeMapCopyModel>>(step.Obj.ToString() ?? "", JsonOptionExtends.Read);
                        if (edgesCopy is not null && edgesCopy.Count > 0)
                        {
                            Dictionary<Guid, Node> CreateNewNode = [];
                            foreach (var edgeCopy in edgesCopy)
                            {
                                if (!CreateNewNode.TryGetValue(edgeCopy.StartNodeId, out _))
                                {
                                    var startNode = await MapDb.Nodes.FindAsync(edgeCopy.StartNodeId);
                                    var newStartNode = await MapDb.Nodes.AddAsync(new Node()
                                    {
                                        MapId = edgeCopy.MapId,
                                        Name = map.NodeNameAutoGenerate ? $"{map.NodeNameTemplateDefault}{++map.NodeCount}" : string.Empty,
                                        X = edgeCopy.X1,
                                        Y = edgeCopy.Y1,
                                        Theta = startNode is not null ? startNode.Theta : 0,
                                        Actions = startNode is not null ? startNode.Actions : "",
                                        AllowedDeviationXy = startNode is not null ? startNode.AllowedDeviationXy : map.NodeAllowedDeviationXyDefault,
                                        AllowedDeviationTheta = startNode is not null ? startNode.AllowedDeviationTheta : map.NodeAllowedDeviationThetaDefault,
                                    });
                                    CreateNewNode.Add(edgeCopy.StartNodeId, newStartNode.Entity);
                                }
                                if (!CreateNewNode.TryGetValue(edgeCopy.EndNodeId, out _))
                                {
                                    var endNode = await MapDb.Nodes.FindAsync(edgeCopy.EndNodeId);
                                    var newEndNode = await MapDb.Nodes.AddAsync(new Node()
                                    {
                                        MapId = edgeCopy.MapId,
                                        Name = map.NodeNameAutoGenerate ? $"{map.NodeNameTemplateDefault}{++map.NodeCount}" : string.Empty,
                                        X = edgeCopy.X2,
                                        Y = edgeCopy.Y2,
                                        Theta = endNode is not null ? endNode.Theta : 0,
                                        Actions = endNode is not null ? endNode.Actions : "",
                                        AllowedDeviationXy = endNode is not null ? endNode.AllowedDeviationXy : map.NodeAllowedDeviationXyDefault,
                                        AllowedDeviationTheta = endNode is not null ? endNode.AllowedDeviationTheta : map.NodeAllowedDeviationThetaDefault,
                                    });
                                    CreateNewNode.Add(edgeCopy.EndNodeId, newEndNode.Entity);
                                }

                                var newEdge = await MapDb.Edges.AddAsync(new Edge()
                                {
                                    MapId = edgeCopy.MapId,
                                    StartNodeId = CreateNewNode[edgeCopy.StartNodeId] is null ? Guid.Empty : CreateNewNode[edgeCopy.StartNodeId].Id,
                                    EndNodeId = CreateNewNode[edgeCopy.EndNodeId] is null ? Guid.Empty : CreateNewNode[edgeCopy.EndNodeId].Id,
                                    TrajectoryDegree = edgeCopy.TrajectoryDegree,
                                    ControlPoint1X = edgeCopy.ControlPoint1X,
                                    ControlPoint1Y = edgeCopy.ControlPoint1Y,
                                    ControlPoint2X = edgeCopy.ControlPoint2X,
                                    ControlPoint2Y = edgeCopy.ControlPoint2Y,
                                    DirectionAllowed = edgeCopy.DirectionAllowed,
                                    RotationAllowed = edgeCopy.RotationAllowed,
                                    MaxSpeed = edgeCopy.MaxSpeed,
                                    MaxRotationSpeed = edgeCopy.MaxRotationSpeed,
                                    MaxHeight = edgeCopy.MaxHeight,
                                    MinHeight = edgeCopy.MinHeight,
                                    Actions = edgeCopy.Actions,
                                    AllowedDeviationTheta = edgeCopy.AllowedDeviationTheta,
                                    AllowedDeviationXy = edgeCopy.AllowedDeviationXy,
                                });
                                EdgeDtos.Add(new()
                                {

                                    Id = newEdge.Entity.Id,
                                    MapId = newEdge.Entity.MapId,
                                    StartNodeId = newEdge.Entity.StartNodeId,
                                    EndNodeId = newEdge.Entity.EndNodeId,
                                    TrajectoryDegree = newEdge.Entity.TrajectoryDegree,
                                    ControlPoint1X = newEdge.Entity.ControlPoint1X,
                                    ControlPoint1Y = newEdge.Entity.ControlPoint1Y,
                                    ControlPoint2X = newEdge.Entity.ControlPoint2X,
                                    ControlPoint2Y = newEdge.Entity.ControlPoint2Y,
                                    DirectionAllowed = newEdge.Entity.DirectionAllowed,
                                    RotationAllowed = newEdge.Entity.RotationAllowed,
                                    MaxSpeed = newEdge.Entity.MaxSpeed,
                                    MaxRotationSpeed = newEdge.Entity.MaxRotationSpeed,
                                    MaxHeight = newEdge.Entity.MaxHeight,
                                    MinHeight = newEdge.Entity.MinHeight,
                                    Actions = newEdge.Entity.Actions,
                                    AllowedDeviationXy = newEdge.Entity.AllowedDeviationXy,
                                    AllowedDeviationTheta = newEdge.Entity.AllowedDeviationTheta,
                                    StartNode = new NodeDto()
                                    {
                                        Id = CreateNewNode[edgeCopy.StartNodeId].Id,
                                        Name = CreateNewNode[edgeCopy.StartNodeId].Name,
                                        MapId = CreateNewNode[edgeCopy.StartNodeId].MapId,
                                        Theta = CreateNewNode[edgeCopy.StartNodeId].Theta,
                                        X = CreateNewNode[edgeCopy.StartNodeId].X,
                                        Y = CreateNewNode[edgeCopy.StartNodeId].Y,
                                        AllowedDeviationXy = CreateNewNode[edgeCopy.StartNodeId].AllowedDeviationXy,
                                        AllowedDeviationTheta = CreateNewNode[edgeCopy.StartNodeId].AllowedDeviationTheta,
                                        Actions = CreateNewNode[edgeCopy.StartNodeId].Actions,
                                    },
                                    EndNode = new NodeDto()
                                    {
                                        Id = CreateNewNode[edgeCopy.EndNodeId].Id,
                                        Name = CreateNewNode[edgeCopy.EndNodeId].Name,
                                        MapId = CreateNewNode[edgeCopy.EndNodeId].MapId,
                                        Theta = CreateNewNode[edgeCopy.EndNodeId].Theta,
                                        X = CreateNewNode[edgeCopy.EndNodeId].X,
                                        Y = CreateNewNode[edgeCopy.EndNodeId].Y,
                                        AllowedDeviationXy = CreateNewNode[edgeCopy.EndNodeId].AllowedDeviationXy,
                                        AllowedDeviationTheta = CreateNewNode[edgeCopy.EndNodeId].AllowedDeviationTheta,
                                        Actions = CreateNewNode[edgeCopy.EndNodeId].Actions,
                                    },
                                });
                            }
                        }
                        break;
                    case MapEditorBackupType.SplitNode:
                        var nodeSplit = await MapDb.Nodes.FindAsync(step.Id);
                        if (nodeSplit is not null)
                        {
                            SplitNodeBackup? SplitNodeUpdate = JsonSerializer.Deserialize<SplitNodeBackup>(step.Obj.ToString() ?? "", JsonOptionExtends.Read);
                            if (SplitNodeUpdate is not null)
                            {
                                foreach (var data in SplitNodeUpdate.EdgeSplit)
                                {
                                    var edge = await MapDb.Edges.FindAsync(data.Key);
                                    if (edge is not null)
                                    {
                                        var newNode = new Node()
                                        {
                                            Id = data.Value.Id,
                                            Name = data.Value.Name,
                                            X = data.Value.X,
                                            Y = data.Value.Y,
                                            Theta = data.Value.Theta,
                                            MapId = data.Value.MapId,
                                            AllowedDeviationXy = data.Value.AllowedDeviationXy,
                                            AllowedDeviationTheta = data.Value.AllowedDeviationTheta,
                                            Actions = data.Value.Actions,
                                        };
                                        if (edge.StartNodeId == nodeSplit.Id) edge.StartNodeId = newNode.Id;
                                        else if (edge.EndNodeId == nodeSplit.Id) edge.EndNodeId = newNode.Id;
                                        else continue;
                                        await MapDb.Nodes.AddAsync(newNode);
                                    }
                                }
                            }
                        }
                        break;
                    case MapEditorBackupType.MergeNode:
                        var nodemerge = await MapDb.Nodes.FindAsync(step.Id);
                        if (nodemerge is not null)
                        {
                            MergeNodeUpdate? MergeNodeUpdate = JsonSerializer.Deserialize<MergeNodeUpdate>(step.Obj.ToString() ?? "", JsonOptionExtends.Read);
                            if (MergeNodeUpdate is not null)
                            {
                                foreach (var data in MergeNodeUpdate.EdgesMerge)
                                {
                                    var edge = await MapDb.Edges.FindAsync(data.Key);
                                    if (edge is not null)
                                    {
                                        var rmNode = await MapDb.Nodes.FindAsync(data.Value);
                                        if (edge.StartNodeId == data.Value) edge.StartNodeId = nodemerge.Id;
                                        else if (edge.EndNodeId == data.Value) edge.EndNodeId = nodemerge.Id;
                                        if (rmNode is not null) MapDb.Nodes.Remove(rmNode);
                                    }
                                }
                            }
                        }
                        break;
                }
            }

            await MapDb.SaveChangesAsync();
            Logger.Info($"User {HttpContext.User.Identity?.Name} đã cập nhật dữ liệu cho bản đồ: {map.Name} - {map.Id}");
            return new(true) { Data = EdgeDtos };
        }
        catch (Exception ex)
        {
            Logger.Warning($"Updates: Hệ thống có lỗi xảy ra - {ex.Message}");
            return new(false, "Hệ thống có lỗi xảy ra");
        }
    }
}
