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.Models;
using RobotNet.MapShares.Property;
using RobotNet.Shares;
using Serialize.Linq.Serializers;
using System.Linq.Expressions;

namespace RobotNet.MapManager.Controllers;

[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ScriptElementsController(MapEditorDbContext MapDb, LoggerController<ScriptElementsController> Logger) : ControllerBase
{
    private static readonly ExpressionSerializer expressionSerializer;

    static ScriptElementsController()
    {
        var jss = new Serialize.Linq.Serializers.JsonSerializer();
        expressionSerializer = new ExpressionSerializer(jss);

        expressionSerializer.AddKnownType(typeof(Script.Expressions.ElementProperties));
    }

    [HttpPost]
    public async Task<MessageResult<IEnumerable<ElementDto>>> GetElementsWithCondition([FromBody] ElementExpressionModel model)
    {
        try
        {
            var map = await MapDb.Maps.FirstOrDefaultAsync(m => m.Name == model.MapName);
            if (map is null) return new(false, $"Không tồn tại map tên = {model.MapName}");

            var elModel = await MapDb.ElementModels.FirstOrDefaultAsync(m => m.MapId == map.Id && m.Name == model.ModelName);
            if (elModel is null) return new(false, $"Không tồn tại element model tên = {model.ModelName}");

            var modelProperties = System.Text.Json.JsonSerializer.Deserialize<List<ElementProperty>>(elModel.Content, JsonOptionExtends.Read);
            if (modelProperties is null || modelProperties.Count == 0)
                return new(false, $"Không tồn tại property nào trong element model tên = {model.ModelName}");

            var expr = expressionSerializer.DeserializeText(model.Expression);
            var lambda = (Expression<Func<Script.Expressions.ElementProperties, bool>>)expr;

            // Compile và chạy:
            var func = lambda.Compile();

            var elements = await MapDb.Elements.Where(e => e.MapId == map.Id && e.ModelId == elModel.Id).ToListAsync();
            List<ElementDto> elementSatisfies = [];
            foreach (var element in elements)
            {
                var properties = MapManagerExtensions.GetElementProperties(element.IsOpen, element.Content);
                if (func.Invoke(properties))
                {
                    var elNode = await MapDb.Nodes.FindAsync(element.NodeId);
                    if (elNode is null) continue; // Bỏ qua nếu không tìm thấy node
                    elementSatisfies.Add(new ElementDto()
                    {
                        Id = element.Id,
                        Name = element.Name,
                        MapId = element.MapId,
                        IsOpen = element.IsOpen,
                        NodeId = element.NodeId,
                        OffsetX = element.OffsetX,
                        OffsetY = element.OffsetY,
                        ModelId = element.ModelId,
                        Content = element.Content,
                        NodeName = elNode.Name,
                        ModelName = elModel.Name,
                        X = elNode.X,
                        Y = elNode.Y,
                        Theta = elNode.Theta
                    });
                }
            }

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

    [HttpGet]
    [Route("{mapName}/node/{nodeName}")]
    public async Task<MessageResult<NodeDto>> GetNode([FromRoute] string mapName, [FromRoute] string nodeName)
    {
        try
        {
            var map = await MapDb.Maps.FirstOrDefaultAsync(m => m.Name == mapName);
            if (map is null) return new(false, $"Không tồn tại map tên = {mapName}");

            var node = await MapDb.Nodes.FirstOrDefaultAsync(n => n.MapId == map.Id && n.Name == nodeName);
            if (node is null) return new(false, $"Không tồn tại node {nodeName} trong map {mapName}");

            return new(true)
            {
                Data = new NodeDto()
                {
                    Id = node.Id,
                    Name = node.Name,
                    MapId = node.MapId,
                    X = node.X,
                    Y = node.Y,
                    Theta = node.Theta,
                    AllowedDeviationXy = node.AllowedDeviationXy,
                    AllowedDeviationTheta = node.AllowedDeviationTheta,
                    Actions = node.Actions,
                }
            };
        }
        catch (Exception ex)
        {
            Logger.Warning($"GetElement: Hệ thống có lỗi xảy ra - {ex.Message}");
            return new(false, $"GetElement: Hệ thống có lỗi xảy ra - {ex.Message}");
        }
    }

    [HttpGet]
    [Route("{mapName}/element/{elementName}")]
    public async Task<MessageResult<ElementDto>> GetElement([FromRoute] string mapName, [FromRoute] string elementName)
    {
        try
        {
            var map = await MapDb.Maps.FirstOrDefaultAsync(m => m.Name == mapName);
            if (map is null) return new(false, $"Không tồn tại map tên = {mapName}");

            var el = await MapDb.Elements.FirstOrDefaultAsync(e => e.MapId == map.Id && e.Name == elementName);
            if (el is null) return new(false, $"Không tồn tại element name = {elementName}");

            var elModel = await MapDb.ElementModels.FindAsync(el.ModelId);
            if (elModel == null) return new(false, $"Không tồn tại element model id = {el.ModelId}");

            var elNode = await MapDb.Nodes.FindAsync(el.NodeId);
            if (elNode is null) return new(false, $"Không tồn tại node id = {el.NodeId}");

            return new(true)
            {
                Data = new()
                {
                    Id = el.Id,
                    Name = el.Name,
                    MapId = el.MapId,
                    IsOpen = el.IsOpen,
                    NodeId = el.NodeId,
                    OffsetX = el.OffsetX,
                    OffsetY = el.OffsetY,
                    ModelId = el.ModelId,
                    Content = el.Content,
                    NodeName = elNode.Name,
                    ModelName = elModel.Name,
                    X = elNode.X,
                    Y = elNode.Y,
                    Theta = elNode.Theta
                }
            };
        }
        catch (Exception ex)
        {
            Logger.Warning($"GetElement: Hệ thống có lỗi xảy ra - {ex.Message}");
            return new(false, $"GetElement: Hệ thống có lỗi xảy ra - {ex.Message}");
        }
    }

    [HttpPatch]
    [Route("{mapName}/element/{elementName}")]
    public async Task<MessageResult> UpdateElementProperty([FromRoute] string mapName, [FromRoute] string elementName, [FromBody] ElementPropertyUpdateModel model)
    {
        try
        {
            var map = await MapDb.Maps.FirstOrDefaultAsync(m => m.Name == mapName);
            if (map is null) return new(false, $"Không tồn tại map tên = {mapName}");

            var element = await MapDb.Elements.FirstOrDefaultAsync(m => m.Name == elementName && m.MapId == map.Id);
            if (element == null) return new(false, $"Không tồn tại element tên = {elementName} trong map tên = {mapName}");

            var properties = System.Text.Json.JsonSerializer.Deserialize<List<ElementProperty>>(element.Content, JsonOptionExtends.Read);
            foreach (var property in model.Properties)
            {
                var existingProperty = properties?.FirstOrDefault(p => p.Name == property.Name);
                if (existingProperty != null)
                {
                    existingProperty.DefaultValue = property.DefaultValue;
                }
                else return new(false, $"Không tồn tại property name = {property.Name} trong element");
            }
            var content = System.Text.Json.JsonSerializer.Serialize(properties, JsonOptionExtends.Write);
            element.Content = content;
            await MapDb.SaveChangesAsync();

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

    [HttpPatch]
    [Route("{mapName}/element/{elementName}/IsOpen")]
    public async Task<MessageResult> UpdateOpenOfElement([FromRoute] string mapName, [FromRoute] string elementName, [FromQuery] bool isOpen)
    {
        try
        {
            var map = await MapDb.Maps.FirstOrDefaultAsync(m => m.Name == mapName);
            if (map is null) return new(false, $"Không tồn tại map tên = {mapName}");

            var element = await MapDb.Elements.FirstOrDefaultAsync(m => m.Name == elementName && m.MapId == map.Id);
            if (element == null) return new(false, $"Không tồn tại element tên = {elementName} trong map tên = {mapName}");

            element.IsOpen = isOpen;
            await MapDb.SaveChangesAsync();

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