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

namespace NodePipeline.Engine.DependencyInjection.CodeGeneration;

internal static class NodeValidatorGenerator
{
    public static string Generate(NodeModelBuilder.NodesModel model)
    {
        var sb = new StringBuilder();
        sb.AppendLine("using Microsoft.Extensions.DependencyInjection;");
        sb.AppendLine("using System.Globalization;");
        sb.AppendLine("using NodePipeline.Abstractions;");
        sb.AppendLine("using NodePipeline.Abstractions.Interfaces;");
        sb.AppendLine("using NodePipeline.Abstractions.Interfaces.Validation;");
        sb.AppendLine("using NodePipeline.Abstractions.Models.Validation;");
        sb.AppendLine("using NodePipeline.Configuration.Abstractions.Models.Execute;");
        sb.AppendLine("using NodePipeline.Engine.Abstractions;");
        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 DiNodeValidator : IPipelineNodeValidator");
        sb.AppendLine("{");
        sb.AppendLine("    private readonly IServiceProvider _serviceProvider;");
        sb.AppendLine("    private readonly IPipelineNodeValidator _validator;");
        sb.AppendLine();
        sb.AppendLine(
            "    public IPipelineLocalizationProvider PipelineLocalizationProvider { get; set; } = new PipelineLocalizationProvider();");
        sb.AppendLine("    public Dictionary<Type, INodeValidator> NodeValidators { get; set; } = [];");
        sb.AppendLine("    public Dictionary<Type, INodeFieldValidator> NodeFieldValidators { get; set; } = [];");
        sb.AppendLine();
        sb.AppendLine(
            "    public DiNodeValidator(IServiceProvider serviceProvider, IPipelineNodeValidator inner, IPipelineLocalizationProvider? validationLocalizationProvider = null)");
        sb.AppendLine("    {");
        sb.AppendLine("        _serviceProvider = serviceProvider;");
        sb.AppendLine("        _validator = inner;");
        sb.AppendLine("        if (validationLocalizationProvider != null)");
        sb.AppendLine("        {");
        sb.AppendLine("            PipelineLocalizationProvider = validationLocalizationProvider;");
        sb.AppendLine("            _validator.PipelineLocalizationProvider = PipelineLocalizationProvider;");
        sb.AppendLine("        }");
        sb.AppendLine("        InitializeNodeValidators();");
        sb.AppendLine("        InitializeNodeFieldValidators();");
        sb.AppendLine("    }");
        sb.AppendLine();
        sb.AppendLine(BuildInitializeNodeValidatorsMethod(model));
        sb.AppendLine();
        sb.AppendLine(BuildInitializeNodeFieldValidatorsMethod(model));
        sb.AppendLine();
        sb.AppendLine(BuildValidateNodeMethod());
        sb.AppendLine("}");
        sb.AppendLine("#nullable restore");
        return sb.ToString();
    }

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

        var nodeValidators = model.Nodes.SelectMany(q => q.CustomValidators);
        foreach (var validator in nodeValidators)
        {
            if (validator.CanBeInitialized) continue;
            sb.AppendLine($"        _validator.NodeValidators.TryAdd(typeof({validator.ValidatorType}),");
            sb.AppendLine($"            _serviceProvider.GetRequiredService<{validator.ValidatorType}>());");
        }

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

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

        var nodeFieldValidators = model.Nodes.SelectMany(q => q.Fields.Where(x => x.Metadata != null)
                .Select(x => x.Metadata!.CustomValidators))
            .SelectMany(q => q);
        foreach (var validator in nodeFieldValidators)
        {
            if (validator.CanBeInitialized) continue;
            sb.AppendLine($"        _validator.NodeFieldValidators.TryAdd(typeof({validator.ValidatorType}),");
            sb.AppendLine($"            _serviceProvider.GetRequiredService<{validator.ValidatorType}>());");
        }

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

    private static string BuildValidateNodeMethod()
    {
        var sb = new StringBuilder();
        sb.AppendLine(
            "    public NodeValidationResult ValidateNode(string pipelineId, INodeFactory nodeFactory, NodeConfig nodeConfig, List<NodeConfig> allNodes, Dictionary<string, object?> parameters, CultureInfo cultureInfo)");
        sb.AppendLine("    {");
        sb.AppendLine(
            "        return _validator.ValidateNode(pipelineId, nodeFactory, nodeConfig, allNodes, parameters, cultureInfo);");
        sb.AppendLine("    }");
        return sb.ToString();
    }
}