using RobotNet.MapShares;
using RobotNet.MapShares.Dtos;

namespace RobotNet.ScriptManager.Models;

public class ScriptMapElement(string mapName, 
    ElementDto element,
    Func<string, string, bool, Task> UpdateIsOpenFunc,
    Func<string, string, ElementPropertyUpdateModel, Task>? UpdatePropertiesFunc) : Script.IElement
{
    public Guid Id { get; } = element.Id;

    public Guid ModelId { get; } = element.ModelId;

    public string ModelName { get; } = element.ModelName ?? throw new ArgumentNullException(nameof(element), "Model name cannot be null");

    public Guid NodeId { get; } = element.NodeId;

    public string MapName { get; } = mapName ?? throw new ArgumentNullException(nameof(mapName), "Map name cannot be null");

    public string Name { get; } = element.Name ?? throw new ArgumentNullException(nameof(element), "Element name cannot be null");

    public double OffsetX { get; } = element.OffsetX;

    public double OffsetY { get; } = element.OffsetY;

    public string NodeName { get; } = element.NodeName ?? throw new ArgumentNullException(nameof(element), "Node name cannot be null");

    public double X { get; } = element.X;

    public double Y { get; } = element.Y;

    public double Theta { get; } = element.Theta;

    public Script.Expressions.ElementProperties Properties { get; } = MapManagerExtensions.GetElementProperties(element.IsOpen, element.Content);

    private Script.Expressions.ElementProperties StoredProperties = MapManagerExtensions.GetElementProperties(element.IsOpen, element.Content);

    public async Task SaveChangesAsync()
    {
        if (Properties.IsOpen != StoredProperties.IsOpen && UpdateIsOpenFunc != null)
        {
            await UpdateIsOpenFunc.Invoke(MapName, Name, Properties.IsOpen);
            StoredProperties.IsOpen = Properties.IsOpen;
        }

        if (UpdatePropertiesFunc != null)
        {
            var changedProperties = new List<MapShares.Property.ElementProperty>();
            foreach (var prop in StoredProperties.Bool)
            {
                if (Properties.Bool.TryGetValue(prop.Key, out var value) && value != prop.Value)
                {
                    changedProperties.Add(new MapShares.Property.ElementProperty()
                    {
                        Name = prop.Key,
                        DefaultValue = value.ToString()
                    });
                }
            }

            foreach (var prop in StoredProperties.Double)
            {
                if (Properties.Double.TryGetValue(prop.Key, out var value) && value != prop.Value)
                {
                    changedProperties.Add(new MapShares.Property.ElementProperty()
                    {
                        Name = prop.Key,
                        DefaultValue = value.ToString()
                    });
                }
            }

            foreach (var prop in StoredProperties.Int)
            {
                if (Properties.Int.TryGetValue(prop.Key, out var value) && value != prop.Value)
                {
                    changedProperties.Add(new MapShares.Property.ElementProperty()
                    {
                        Name = prop.Key,
                        DefaultValue = value.ToString()
                    });
                }
            }

            foreach (var prop in StoredProperties.String)
            {
                if (Properties.String.TryGetValue(prop.Key, out var value) && value != prop.Value)
                {
                    changedProperties.Add(new MapShares.Property.ElementProperty()
                    {
                        Name = prop.Key,
                        DefaultValue = value
                    });
                }
            }

            if (changedProperties.Count != 0)
            {
                var updateModel = new ElementPropertyUpdateModel
                {
                    Properties = [.. changedProperties]
                };
                await UpdatePropertiesFunc.Invoke(MapName, Name, updateModel);
                StoredProperties = Properties; // Update stored properties after saving
            }
        }
    }
}
