#!/usr/bin/env dotnet-script

{
    const string header =
@"// Autogenerated
using InitableService;

namespace Microsoft.Extensions.DependencyInjection;

public static class InitableExtensions
{
";

    const string footer = "}";
    using var initableWriter = File.CreateText("InitableExtensions.cs");
    initableWriter.WriteLine(header);
    const int generateForParams = 15;

    for (var i = 0; i <= generateForParams; i++)
    {
        WriteInitableExtension(initableWriter, false, i);
        initableWriter.WriteLine();
    }

    for (var i = 0; i <= generateForParams; i++)
    {
        WriteInitableExtension(initableWriter, true, i);
        initableWriter.WriteLine();
    }

    initableWriter.WriteLine(footer);

    for (var i = 0; i <= generateForParams; i++)
    {
        WriteInitableResolver(initableWriter, false, i);
        initableWriter.WriteLine();
    }

    for (var i = 0; i <= generateForParams; i++)
    {
        WriteInitableResolver(initableWriter, true, i);
        initableWriter.WriteLine();
    }

    initableWriter.Flush();
}

void WriteInitableExtension(StreamWriter initableWriter, bool isAsync, int i)
{
    initableWriter.Write($"\tpublic static {(isAsync ? "Async" : "")}Resolver");
    if (i > 0)
    {
        initableWriter.Write("<");
        initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => "T" + c)));
        initableWriter.Write(">");
    }
    initableWriter.Write($" Get{(isAsync ? "Async" : "")}InitableResolver");
    if (i > 0)
    {
        initableWriter.Write("<");
        initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => "T" + c)));
        initableWriter.Write(">");
    }

    initableWriter.Write("(");
    var thisParameter = "this IServiceProvider serviceProvider";

    if (i > 0)
    {
        initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => $"T{c} obj{c}").Prepend(thisParameter)));
    }
    else
    {
        initableWriter.Write(thisParameter);
    }

    initableWriter.WriteLine(")");
    initableWriter.WriteLine("\t{");
    initableWriter.Write($"\t\treturn new {(isAsync ? "Async" : "")}Resolver");

    if (i > 0)
    {
        initableWriter.Write("<");
        initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => "T" + c)));
        initableWriter.Write(">");
    }

    initableWriter.Write("(");
    if (i > 0)
    {
        initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => $"obj{c}").Prepend("serviceProvider")));
    }
    else
    {
        initableWriter.Write("serviceProvider");
    }
    initableWriter.WriteLine(");");

    initableWriter.WriteLine("\t}");
}

void WriteInitableResolver(StreamWriter initableWriter, bool isAsync, int i)
{
    initableWriter.Write($"public class {(isAsync ? "Async" : "")}Resolver");
    if (i > 0)
    {
        initableWriter.Write("<");
        initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => "T" + c)));
        initableWriter.Write(">");
    }
    initableWriter.WriteLine();
    initableWriter.WriteLine("{");
    initableWriter.WriteLine("\tIServiceProvider _serviceProvider;");
    if (i > 0)
    {
        for (var j = 1; j <= i; j++)
        {
            initableWriter.WriteLine($"\tprivate T{j} _obj{j};");
        }
    }

    initableWriter.Write($"\tpublic {(isAsync ? "Async" : "")}Resolver(");
    if (i > 0)
    {
        initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => $"T{c} obj{c}").Prepend("IServiceProvider serviceProvider")));
    }
    else
    {
        initableWriter.Write("IServiceProvider serviceProvider");
    }

    initableWriter.WriteLine(")");
    initableWriter.WriteLine("\t{");
    initableWriter.WriteLine("\t\t_serviceProvider = serviceProvider;");
    if (i > 0)
    {
        for (var j = 1; j <= i; j++)
        {
            initableWriter.WriteLine($"\t\t_obj{j} = obj{j};");
        }
    }
    initableWriter.WriteLine("\t}");
    initableWriter.WriteLine("");

    var returnType = isAsync ? "async Task<TResult>" : "TResult";
    initableWriter.WriteLine($"\tpublic {returnType} GetRequiredService{(isAsync ? "Async" : "")}<TResult>()");



    initableWriter.Write($"\t\twhere TResult : class, I{(isAsync ? "Async" : "")}Initable");
    if (i > 0)
    {
        initableWriter.Write("<");
        initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => "T" + c)));
        initableWriter.Write(">");
    }
    initableWriter.WriteLine("");
    initableWriter.WriteLine("\t{");
    initableWriter.WriteLine("\t\tvar initableService = _serviceProvider.GetRequiredService<TResult>()!;");
    if (isAsync)
    {
        initableWriter.Write("\t\tawait initableService.InitAsync");
    }
    else
    {
        initableWriter.Write("\t\tinitableService.Init");
    }

    initableWriter.Write("(");

    if (i > 0)
    {
        initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => $"_obj{c}")));
    }
    initableWriter.WriteLine(");");
    initableWriter.WriteLine("\t\treturn initableService;");

    initableWriter.WriteLine("\t}");
    initableWriter.WriteLine("}");
}