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

namespace RobotNet.MapManager.Controllers;

[Route("api/[controller]")]
[ApiController]
[Authorize]
public class EdgesController(MapEditorDbContext MapDb, LoggerController<EdgesController> Logger) : ControllerBase
{
    [HttpPost]
    [Route("")]
    public async Task<MessageResult<EdgeCreateDto>> CreateEdge([FromBody] EdgeCreateModel model)
    {
        try
        {
            var map = await MapDb.Maps.FindAsync(model.MapId);
            if (map == null) return new(false, $"Không tồn tại map id = {model.MapId}");

            if (Math.Sqrt(Math.Pow(model.X1 - model.X2, 2) + Math.Pow(model.Y1 - model.Y2, 2)) < map.EdgeMinLengthDefault) return new(false, "Độ dài edge quá nhỏ");

            var nodes = await MapDb.Nodes.Where(n => n.MapId == model.MapId).ToListAsync();
            var edges = await MapDb.Edges.Where(e => e.MapId == model.MapId && e.TrajectoryDegree == TrajectoryDegree.One).ToListAsync();

            var closesStartNode = MapEditorHelper.GetClosesNode(model.X1, model.Y1, [.. nodes.Select(node => new NodeDto()
            {
                MapId = node.MapId,
                X = node.X,
                Y = node.Y,
                Theta = node.Theta,
                Actions = node.Actions,
                Id = node.Id,
                Name = node.Name,
                AllowedDeviationTheta = node.AllowedDeviationTheta,
                AllowedDeviationXy = node.AllowedDeviationXy
            })]);

            var closesEndNode = MapEditorHelper.GetClosesNode(model.X2, model.Y2, [.. nodes.Select(node => new NodeDto()
            {
                MapId = node.MapId,
                X = node.X,
                Y = node.Y,
                Theta = node.Theta,
                Actions = node.Actions,
                Id = node.Id,
                Name = node.Name,
                AllowedDeviationTheta = node.AllowedDeviationTheta,
                AllowedDeviationXy = node.AllowedDeviationXy
            })]);

            Node? startNode = await MapDb.Nodes.FindAsync(closesStartNode?.Id);
            Node? endNode = await MapDb.Nodes.FindAsync(closesEndNode?.Id);
            List<Guid> RemoveEdge = [];
            List<EdgeDto> AddEdgeDto = [];

            if (startNode is null)
            {
                startNode = ServerHelper.CreateNode(map, model.X1, model.Y1);
                await MapDb.Nodes.AddAsync(startNode);
                var closesEdge = ServerHelper.GetClosesEdge(model.X1, model.Y1, nodes, edges, 0.1);
                if (closesEdge is not null)
                {
                    var closesEdgeStartNode = await MapDb.Nodes.FirstOrDefaultAsync(n => n.Id == closesEdge.StartNodeId);
                    var closesEdgeEndNode = await MapDb.Nodes.FirstOrDefaultAsync(n => n.Id == closesEdge.EndNodeId);
                    if (closesEdgeStartNode is not null && closesEdgeEndNode is not null)
                    {
                        var startEdge = ServerHelper.CreateEdge(map, closesEdgeStartNode.Id, startNode.Id, TrajectoryDegree.One);
                        var endEdge = ServerHelper.CreateEdge(map, startNode.Id, closesEdgeEndNode.Id, TrajectoryDegree.One);
                        await MapDb.Edges.AddAsync(startEdge);
                        await MapDb.Edges.AddAsync(endEdge);

                        MapDb.Edges.Remove(closesEdge);
                        edges.Remove(closesEdge);
                        RemoveEdge.Add(closesEdge.Id);

                        edges.Add(startEdge);
                        edges.Add(endEdge);
                        nodes.Add(startNode);
                        AddEdgeDto.Add(ServerHelper.CreateEdgeDto(startEdge, closesEdgeStartNode, startNode));
                        AddEdgeDto.Add(ServerHelper.CreateEdgeDto(endEdge, startNode, closesEdgeEndNode));
                    }
                }
            }
            await MapDb.SaveChangesAsync();

            if (endNode is null)
            {
                endNode = ServerHelper.CreateNode(map, model.X2, model.Y2);
                await MapDb.Nodes.AddAsync(endNode);

                var closesEdge = ServerHelper.GetClosesEdge(model.X2, model.Y2, nodes, edges, 0.1);
                if (closesEdge is not null)
                {
                    var closesEdgeStartNode = await MapDb.Nodes.FirstOrDefaultAsync(n => n.Id == closesEdge.StartNodeId);
                    var closesEdgeEndNode = await MapDb.Nodes.FirstOrDefaultAsync(n => n.Id == closesEdge.EndNodeId);
                    if (closesEdgeStartNode is not null && closesEdgeEndNode is not null)
                    {
                        var startEdge = ServerHelper.CreateEdge(map, closesEdgeStartNode.Id, endNode.Id, TrajectoryDegree.One);
                        var endEdge = ServerHelper.CreateEdge(map, endNode.Id, closesEdgeEndNode.Id, TrajectoryDegree.One);
                        await MapDb.Edges.AddAsync(startEdge);
                        await MapDb.Edges.AddAsync(endEdge);

                        MapDb.Edges.Remove(closesEdge);
                        RemoveEdge.Add(closesEdge.Id);
                        AddEdgeDto.Add(ServerHelper.CreateEdgeDto(startEdge, closesEdgeStartNode, endNode));
                        AddEdgeDto.Add(ServerHelper.CreateEdgeDto(endEdge, endNode, closesEdgeEndNode));
                    }
                }
            }

            var edge = ServerHelper.CreateEdge(map, startNode.Id, endNode.Id, model.TrajectoryDegree, model.ControlPoint1X, model.ControlPoint1Y, model.ControlPoint2X, model.ControlPoint2Y);
            await MapDb.Edges.AddAsync(edge);

            await MapDb.SaveChangesAsync();

            AddEdgeDto.Add(ServerHelper.CreateEdgeDto(edge, startNode, endNode));

            return new(true)
            {
                Data = new EdgeCreateDto()
                {
                    EdgesDto = AddEdgeDto,
                    RemoveEdge = RemoveEdge,
                }
            };
        }
        catch (Exception ex)
        {
            Logger.Warning($"CreateEdge: Hệ thống có lỗi xảy ra - {ex.Message}");
            return new(false, $"CreateEdge: Hệ thống có lỗi xảy ra - {ex.Message}");
        }
    }

    [HttpDelete]
    [Route("{id}")]
    public async Task<MessageResult> DeleteEdge(Guid id)
    {
        using var transaction = await MapDb.Database.BeginTransactionAsync();
        try
        {
            var edge = await MapDb.Edges.FindAsync(id);
            if (edge == null) return new(false, $"Không tồn tại edge id = {id}");

            MapDb.Edges.Remove(edge);

            if (!MapDb.Edges.Any(e => (e.StartNodeId == edge.StartNodeId || e.EndNodeId == edge.StartNodeId) && e.Id != edge.Id))
            {
                var node = await MapDb.Nodes.FindAsync(edge.StartNodeId);
                if (node != null)
                {
                    var element = await MapDb.Elements.FirstOrDefaultAsync(e => e.NodeId == node.Id);
                    if (element is not null) MapDb.Elements.Remove(element);
                    MapDb.Nodes.Remove(node);
                }
            }

            if (!MapDb.Edges.Any(e => (e.StartNodeId == edge.EndNodeId || e.EndNodeId == edge.EndNodeId) && e.Id != edge.Id))
            {
                var node = await MapDb.Nodes.FindAsync(edge.EndNodeId);
                if (node != null)
                {
                    var element = await MapDb.Elements.FirstOrDefaultAsync(e => e.NodeId == node.Id);
                    if (element is not null) MapDb.Elements.Remove(element);
                    MapDb.Nodes.Remove(node);
                }
            }

            await MapDb.SaveChangesAsync();
            await transaction.CommitAsync();
            return new(true);
        }
        catch (Exception ex)
        {
            await transaction.RollbackAsync();
            Logger.Warning($"DeleteEdge {id}: Hệ thống có lỗi xảy ra - {ex.Message}");
            return new(false, $"DeleteEdge {id}: Hệ thống có lỗi xảy ra - {ex.Message}");
        }
    }

    [HttpDelete]
    [Route("")]
    public async Task<MessageResult> DeleteEdges([FromBody] IEnumerable<Guid> DeleteEdgesId)
    {
        try
        {
            List<Edge> deleteEdges = [];
            List<Node> deleteNodes = [];
            foreach (var edgeId in DeleteEdgesId)
            {
                var edge = await MapDb.Edges.FindAsync(edgeId);
                if (edge == null) continue;

                MapDb.Edges.Remove(edge);

                if (!MapDb.Edges.Any(e => (e.StartNodeId == edge.StartNodeId || e.EndNodeId == edge.StartNodeId) && e.Id != edge.Id))
                {
                    var node = await MapDb.Nodes.FindAsync(edge.StartNodeId);
                    if (node != null)
                    {
                        var element = await MapDb.Elements.FirstOrDefaultAsync(e => e.NodeId == node.Id);
                        if (element is not null) MapDb.Elements.Remove(element);
                        MapDb.Nodes.Remove(node);
                    }
                }

                if (!MapDb.Edges.Any(e => (e.StartNodeId == edge.EndNodeId || e.EndNodeId == edge.EndNodeId) && e.Id != edge.Id))
                {
                    var node = await MapDb.Nodes.FindAsync(edge.EndNodeId);
                    if (node != null)
                    {
                        var element = await MapDb.Elements.FirstOrDefaultAsync(e => e.NodeId == node.Id);
                        if (element is not null) MapDb.Elements.Remove(element);
                        MapDb.Nodes.Remove(node);
                    }
                }

                await MapDb.SaveChangesAsync();
            }

            return new(true);
        }
        catch (Exception ex)
        {
            Logger.Warning($"DeleteEdges: Hệ thống có lỗi xảy ra - {ex.Message}");
            return new(false, $"DeleteEdges: Hệ thống có lỗi xảy ra - {ex.Message}");
        }
    }

    [HttpPut]
    [Route("")]
    public async Task<MessageResult> UpdateEdge([FromBody] EdgeUpdateModel model)
    {
        try
        {
            var edge = await MapDb.Edges.FindAsync(model.Id);
            if (edge == null) return new(false, $"Không tồn tại edge id = {model.Id}");

            edge.MaxSpeed = model.MaxSpeed;
            edge.MaxHeight = model.MaxHeight;
            edge.MinHeight = model.MinHeight;
            edge.ControlPoint1X = model.ControlPoint1X;
            edge.ControlPoint1Y = model.ControlPoint1Y;
            edge.ControlPoint2X = model.ControlPoint2X;
            edge.ControlPoint2Y = model.ControlPoint2Y;
            edge.DirectionAllowed = model.DirectionAllowed;
            edge.RotationAllowed = model.RotationAllowed;
            edge.MaxRotationSpeed = model.MaxRotationSpeed;
            edge.Actions = JsonSerializer.Serialize(model.Actions ?? []);
            edge.AllowedDeviationXy = model.AllowedDeviationXy;
            edge.AllowedDeviationTheta = model.AllowedDeviationTheta;

            await MapDb.SaveChangesAsync();
            return new(true);
        }
        catch (Exception ex)
        {
            Logger.Warning($"UpdateEdge: Hệ thống có lỗi xảy ra - {ex.Message}");
            return new(false, $"UpdateEdge: Hệ thống có lỗi xảy ra - {ex.Message}");
        }
    }
}
