using System.Text.Json;

namespace Inspectron.Epson.TemplateEngine.DataBinding;

public class DataContext
{
    private readonly JsonElement _root;
    private readonly DataContext? _parent;
    private readonly Dictionary<string, JsonElement> _variables = new(StringComparer.OrdinalIgnoreCase);
    private readonly Dictionary<string, object> _loopVariables = new(StringComparer.OrdinalIgnoreCase);

    public DataContext(JsonElement root)
    {
        _root = root;
    }

    private DataContext(JsonElement root, DataContext parent)
    {
        _root = root;
        _parent = parent;
    }

    public DataContext CreateChildScope()
    {
        return new DataContext(_root, this);
    }

    public void SetVariable(string name, JsonElement value)
    {
        _variables[name] = value;
    }

    public void SetLoopVariable(string name, object value)
    {
        _loopVariables[name] = value;
    }

    public object? GetLoopVariable(string name)
    {
        if (_loopVariables.TryGetValue(name, out var value))
            return value;
        return _parent?.GetLoopVariable(name);
    }

    public JsonElement? Resolve(string expression)
    {
        if (string.IsNullOrWhiteSpace(expression))
            return null;

        var parts = expression.Split('.');
        var firstPart = parts[0];

        // Check local variables first
        JsonElement? current = null;

        if (_variables.TryGetValue(firstPart, out var varValue))
        {
            current = varValue;
        }
        else if (_parent != null)
        {
            // Walk up scope chain for variables
            var ctx = _parent;
            while (ctx != null)
            {
                if (ctx._variables.TryGetValue(firstPart, out var parentVar))
                {
                    current = parentVar;
                    break;
                }
                ctx = ctx._parent;
            }
        }

        // If not found in variables, try root
        if (current == null)
        {
            current = GetProperty(_root, firstPart);
        }

        if (current == null)
            return null;

        // Navigate remaining parts
        for (int i = 1; i < parts.Length; i++)
        {
            current = GetProperty(current.Value, parts[i]);
            if (current == null)
                return null;
        }

        return current;
    }

    private static JsonElement? GetProperty(JsonElement element, string propertyName)
    {
        if (element.ValueKind != JsonValueKind.Object)
            return null;

        // Case-insensitive property lookup
        foreach (var prop in element.EnumerateObject())
        {
            if (string.Equals(prop.Name, propertyName, StringComparison.OrdinalIgnoreCase))
                return prop.Value;
        }

        return null;
    }

    public static string GetStringValue(JsonElement element)
    {
        return element.ValueKind switch
        {
            JsonValueKind.String => element.GetString() ?? "",
            JsonValueKind.Number => element.TryGetInt64(out var l) ? l.ToString() : element.GetDouble().ToString(),
            JsonValueKind.True => "true",
            JsonValueKind.False => "false",
            JsonValueKind.Null => "",
            JsonValueKind.Undefined => "",
            _ => element.GetRawText()
        };
    }
}
