using System.Reflection;
using System.Text.RegularExpressions;
using NodePipeline.Engine.CodeGeneration.Abstractions;
using NodePipeline.Engine.CodeGeneration.Abstractions.Models;

namespace NodePipeline.Engine.Tests.CodeGeneratorTests.Fixtures;

internal static class TestHelper
{
    private const string NullString = "null";

    internal static string ExtractMethod(string code, string methodName)
    {
        var pattern = $@"\b[\w\<\>

\[\]

\s\?]+?\s+{methodName}(\s*\<[^>]+\>)?\s*\([^)]*\)\s*(where[^{{\r\n]*)?\s*\{{";

        var regex = new Regex(pattern);
        var match = regex.Match(code);
        if (!match.Success)
            return string.Empty;

        var braceStart = code.IndexOf('{', match.Index + match.Length - 1);
        if (braceStart == -1)
            return string.Empty;

        var braceCount = 1;
        var pos = braceStart + 1;
        while (pos < code.Length && braceCount > 0)
        {
            switch (code[pos])
            {
                case '{': braceCount++; break;
                case '}': braceCount--; break;
            }

            pos++;
        }

        return braceCount != 0 ? string.Empty : code.Substring(braceStart, pos - braceStart);
    }

    internal static string ExtractSwitchCase(string methodCode, string caseLabel)
    {
        var caseIndex = methodCode.IndexOf(caseLabel, StringComparison.Ordinal);
        if (caseIndex == -1) return string.Empty;

        var switchStart = methodCode.LastIndexOf("switch", caseIndex, StringComparison.Ordinal);
        if (switchStart == -1) return string.Empty;

        var braceStart = methodCode.IndexOf('{', switchStart);
        if (braceStart == -1) return string.Empty;

        var braceCount = 1;
        var pos = braceStart + 1;
        while (pos < methodCode.Length && braceCount > 0)
        {
            switch (methodCode[pos])
            {
                case '{':
                    braceCount++;
                    break;
                case '}':
                    braceCount--;
                    break;
            }

            pos++;
        }

        var switchBlock = methodCode.Substring(braceStart + 1, pos - braceStart - 2);

        // Extract the specific case block
        var caseStart = switchBlock.IndexOf(caseLabel, StringComparison.Ordinal);
        if (caseStart == -1) return string.Empty;

        var casePos = caseStart + caseLabel.Length;
        var nextCaseIndex = switchBlock.IndexOf("case ", casePos, StringComparison.Ordinal);
        var defaultCaseIndex = switchBlock.IndexOf("default:", casePos, StringComparison.Ordinal);

        int caseEnd;
        switch (nextCaseIndex)
        {
            case -1 when defaultCaseIndex == -1:
                caseEnd = switchBlock.Length;
                break;
            case -1:
                caseEnd = defaultCaseIndex;
                break;
            default:
            {
                caseEnd = defaultCaseIndex == -1 ? nextCaseIndex : Math.Min(nextCaseIndex, defaultCaseIndex);
                break;
            }
        }

        var caseBlock = switchBlock.Substring(caseStart, caseEnd - caseStart).Trim();
        return caseBlock;
    }

    internal static NodeModelBuilder.NodesModel GetNodeModel(string factoryMethodName)
    {
        //TODO: add interface;
        var method = typeof(TestNodeModelFactory).GetMethod(factoryMethodName,
            BindingFlags.Static | BindingFlags.NonPublic,
            Type.EmptyTypes);
        if (method == null) throw new Exception($"Method {factoryMethodName} was not found in TestNodeModelFactory");
        return method.Invoke(null, null) as NodeModelBuilder.NodesModel
               ?? throw new Exception("Method result is not NodeModelBuilder.NodesModel");
    }

    internal static NodeDescriptor GetNodeDescriptorFromMockNode(Type mockNodeType)
    {
        //TODO: add interface;
        var method = mockNodeType.GetMethod("GetDescriptor", BindingFlags.Static | BindingFlags.NonPublic,
            Type.EmptyTypes);
        if (method == null) throw new Exception($"Method GetDescriptor was not found for type {mockNodeType.FullName}");
        return method.Invoke(null, null) as NodeDescriptor
               ?? throw new Exception("Method result is not GetDescriptor");
    }

    internal static string NumberToString(int? value)
    {
        return value?.ToString() ?? NullString;
    }

    internal static string NumberToString(decimal? value)
    {
        return value?.ToString() ?? NullString;
    }
}