using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using RobotNet.MapManager.Data;
using RobotNet.MapShares.Dtos;
using RobotNet.Shares;

namespace RobotNet.MapManager.Hubs;

[Authorize]
public class MapHub(MapEditorDbContext MapDb) : Hub
{
    public async Task<MessageResult<MapDataDto>> GetMapData(Guid mapId)
    {
        var map = await MapDb.Maps.FindAsync(mapId);
        if (map is null) return new(false, $"Không tìm thấy bản đồ: {mapId}");

        return new(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 == mapId).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 == mapId).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 == mapId).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 == mapId).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 == mapId).Select(action => new ActionDto()
                {
                    Id = action.Id,
                    MapId = action.MapId,
                    Name = action.Name,
                    Content = action.Content,
                })]
            }
        };
    }

    public async Task<MessageResult<ElementDto[]>> GetElementsState(Guid mapId)
    {
        var map = await MapDb.Maps.FindAsync(mapId);
        if (map is null) return new(false, $"Không tìm thấy bản đồ: {mapId}");
        var elements = MapDb.Elements
            .Where(el => el.MapId == mapId)
            .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
            });
        return new(true, "") { Data = [..elements] };
    }

    public async Task<MessageResult<MapInfoDto>> GetMapInfoByName(string name)
    {
        var map = await MapDb.Maps.FirstOrDefaultAsync(m => m.Name == name);
        if (map == null) return new MessageResult<MapInfoDto>(false, $"Không tìm thấy map {name}");

        return new(true)
        {
            Data = new MapInfoDto()
            {
                Id = map.Id,
                Name = map.Name,
                Active = map.Active,
                OriginX = map.OriginX,
                OriginY = map.OriginY,
                Width = Math.Round(map.ImageWidth * map.Resolution, 2),
                Height = Math.Round(map.ImageHeight * map.Resolution, 2),
                Resolution = map.Resolution,
            },
        };
    }

    public async Task<MessageResult<MapInfoDto>> GetMapInfoById(Guid id)
    {
        var map = await MapDb.Maps.FirstOrDefaultAsync(m => m.Id == id);
        if (map == null) return new MessageResult<MapInfoDto>(false, $"Không tìm thấy map {id}");

        return new(true)
        {
            Data = new MapInfoDto()
            {
                Id = map.Id,
                Name = map.Name,
                Active = map.Active,
                OriginX = map.OriginX,
                OriginY = map.OriginY,
                Width = Math.Round(map.ImageWidth * map.Resolution, 2),
                Height = Math.Round(map.ImageHeight * map.Resolution, 2),
                Resolution = map.Resolution,
            },
        };
    }
}
