using System.Text;
using NodePipeline.Engine.CodeGeneration.Abstractions;

namespace NodePipeline.Engine.DependencyInjection.CodeGeneration;

internal static class NodeFactoryGenerator
{
    public static string Generate(NodeModelBuilder.NodesModel model)
    {
        var sb = new StringBuilder();
        sb.AppendLine("using Microsoft.Extensions.DependencyInjection;");
        sb.AppendLine("using NodePipeline.Abstractions;");
        sb.AppendLine("using NodePipeline.Abstractions.Interfaces;");
        sb.AppendLine("using NodePipeline.Abstractions.Interfaces.Nodes;");
        sb.AppendLine("using NodePipeline.Configuration.Abstractions.Models.Execute;");
        sb.AppendLine("using NodePipeline.Engine.Abstractions;");
        sb.AppendLine("using NodePipeline.Engine.Abstractions.Validation;");
        sb.AppendLine();
        sb.AppendLine("// ReSharper disable CheckNamespace");
        sb.AppendLine("// ReSharper disable NotAccessedField.Local");
        sb.AppendLine("#nullable enable");
        sb.AppendLine();
        sb.AppendLine("namespace NodePipeline.Engine.Generated;");
        sb.AppendLine();
        sb.AppendLine("public class DiNodeFactory : INodeFactory");
        sb.AppendLine("{");
        sb.AppendLine("    private readonly INodeFactory _nodeFactory;");
        sb.AppendLine("    private readonly IServiceProvider _serviceProvider;");
        sb.AppendLine();
        sb.AppendLine(
            "    public IPipelineLocalizationProvider PipelineLocalizationProvider { get; set; } = new PipelineLocalizationProvider();");
        sb.AppendLine("    public Dictionary<string, Func<INode>> NodeFactories { get; set; } = [];");
        sb.AppendLine();
        sb.AppendLine(
            "    public DiNodeFactory(IServiceProvider serviceProvider, INodeFactory inner, IPipelineLocalizationProvider? validationLocalizationProvider = null)");
        sb.AppendLine("    {");
        sb.AppendLine("        _serviceProvider = serviceProvider;");
        sb.AppendLine("        _nodeFactory = inner;");
        sb.AppendLine("        if (validationLocalizationProvider != null)");
        sb.AppendLine("        {");
        sb.AppendLine("            PipelineLocalizationProvider = validationLocalizationProvider;");
        sb.AppendLine("            _nodeFactory.PipelineLocalizationProvider = PipelineLocalizationProvider;");
        sb.AppendLine("        }");
        sb.AppendLine("        InitializeNodeFactories();");
        sb.AppendLine("    }");
        sb.AppendLine();
        sb.AppendLine(BuildInitializeNodeFactoriesMethod(model));
        sb.AppendLine();
        sb.AppendLine(BuildCreateNodeMethod());
        sb.AppendLine();
        sb.AppendLine(BuildGetParameterCodesMethod());
        sb.AppendLine();
        sb.AppendLine(BuildGetAllPortsMethod());
        sb.AppendLine();
        sb.AppendLine(BuildGetNodeInputPortsMethod());
        sb.AppendLine();
        sb.AppendLine(BuildReadParameterValueMethod());
        sb.AppendLine();
        sb.AppendLine(BuildGetParameterDefaultValueMethod());
        sb.AppendLine();
        sb.AppendLine(BuildSetNodeParametersValuesMethod());
        sb.AppendLine();
        sb.AppendLine(BuildConnectPortsMethod());
        sb.AppendLine("}");
        sb.AppendLine("#nullable restore");
        return sb.ToString();
    }

    private static string BuildInitializeNodeFactoriesMethod(NodeModelBuilder.NodesModel model)
    {
        var sb = new StringBuilder();
        sb.AppendLine("    private void InitializeNodeFactories()");
        sb.AppendLine("    {");

        foreach (var n in model.Nodes)
        {
            if (n.HasParameterlessConstructor) continue;

            sb.AppendLine("        _nodeFactory.NodeFactories.Add(");
            sb.AppendLine($"            {NodeGeneratorHelper.GetNodeName(n)},");
            sb.AppendLine($"            () => _serviceProvider.GetRequiredService<{n.TypeNameFull}>());");
        }

        sb.AppendLine("    }");
        return sb.ToString();
    }

    private static string BuildCreateNodeMethod()
    {
        var sb = new StringBuilder();
        sb.AppendLine("    public INode CreateNode(string pipelineId, NodeConfig config)");
        sb.AppendLine("    {");
        sb.AppendLine("        return _nodeFactory.CreateNode(pipelineId, config);");
        sb.AppendLine("    }");
        return sb.ToString();
    }

    private static string BuildGetParameterCodesMethod()
    {
        var sb = new StringBuilder();
        sb.AppendLine("    public IEnumerable<string> GetParameterCodes(string nodeType)");
        sb.AppendLine("    {");
        sb.AppendLine("        return _nodeFactory.GetParameterCodes(nodeType);");
        sb.AppendLine("    }");
        return sb.ToString();
    }

    private static string BuildGetAllPortsMethod()
    {
        var sb = new StringBuilder();
        sb.AppendLine(
            "    public Dictionary<NodePortKey, NodePortInfo> GetAllPorts(string pipelineId, HashSet<string> nodeTypes, Dictionary<string, IEnumerable<string>> nodeList)");
        sb.AppendLine("    {");
        sb.AppendLine("        return _nodeFactory.GetAllPorts(pipelineId, nodeTypes, nodeList);");
        sb.AppendLine("    }");
        return sb.ToString();
    }

    private static string BuildGetNodeInputPortsMethod()
    {
        var sb = new StringBuilder();
        sb.AppendLine(
            "    public IEnumerable<NodePortInfo> GetNodeInputPorts(string pipelineId, string nodeType, string nodeId)");
        sb.AppendLine("    {");
        sb.AppendLine("        return _nodeFactory.GetNodeInputPorts(pipelineId, nodeType, nodeId);");
        sb.AppendLine("    }");
        return sb.ToString();
    }

    private static string BuildReadParameterValueMethod()
    {
        var sb = new StringBuilder();
        sb.AppendLine(
            "    public object? ReadParameterValue(string pipelineId, string nodeId, string nodeType, string parameterName, string valueString)");
        sb.AppendLine("    {");
        sb.AppendLine(
            "        return _nodeFactory.ReadParameterValue(pipelineId, nodeId, nodeType, parameterName, valueString);");
        sb.AppendLine("    }");
        return sb.ToString();
    }

    private static string BuildGetParameterDefaultValueMethod()
    {
        var sb = new StringBuilder();
        sb.AppendLine("    public object? GetParameterDefaultValue(string nodeType, string parameterName)");
        sb.AppendLine("    {");
        sb.AppendLine("        return _nodeFactory.GetParameterDefaultValue(nodeType, parameterName);");
        sb.AppendLine("    }");
        return sb.ToString();
    }

    private static string BuildSetNodeParametersValuesMethod()
    {
        var sb = new StringBuilder();
        sb.AppendLine(
            "    public void SetNodeParametersValues<TNode>(string pipelineId, TNode node, string nodeId, Dictionary<string, object?> parameters) where TNode : INode");
        sb.AppendLine("    {");
        sb.AppendLine("        _nodeFactory.SetNodeParametersValues(pipelineId, node, nodeId, parameters);");
        sb.AppendLine("    }");
        return sb.ToString();
    }

    private static string BuildConnectPortsMethod()
    {
        var sb = new StringBuilder();
        sb.AppendLine(
            "    public void ConnectPorts<TNode>(string pipelineId, TNode node, string nodeId, Dictionary<string, InputSource> inputs, IReadOnlyDictionary<string, INode> createdNodes) where TNode : INode");
        sb.AppendLine("    {");
        sb.AppendLine("        _nodeFactory.ConnectPorts(pipelineId, node, nodeId, inputs, createdNodes);");
        sb.AppendLine("    }");
        return sb.ToString();
    }
}