using System.Text;
using Mono.Cecil;
using Serilog;

public static class AssemblyRedirectionSourceGenerator
{
    public static void Generate(string assembliesFolderPath, string generatedFilePath)
    {
        Log.Debug("Generating assembly redirection file {0}", generatedFilePath);
        var assemblies = new SortedDictionary<string, AssemblyNameDefinition>();
        foreach (var fileName in Directory.EnumerateFiles(assembliesFolderPath))
        {
            try
            {
                using var moduleDef = ModuleDefinition.ReadModule(fileName);
                var assemblyDef = moduleDef.Assembly.Name!;
                if (assemblyDef.Name == "netstandard")
                {
                    // Skip netstandard, since it doesn't need redirection.
                    continue;
                }

                assemblies[assemblyDef.Name] = assemblyDef;
                Log.Debug("Adding {0} assembly to the redirection map. Targeted version {1}", assemblyDef.Name, assemblyDef.Version);
            }
            catch (BadImageFormatException)
            {
                Log.Debug("Skipping \"{0}\" couldn't open it as a managed assembly", fileName);
            }
        }

        var sourceContents = GenerateSourceContents(assemblies);

        File.WriteAllText(generatedFilePath, sourceContents);
        Log.Information("Assembly redirection source generated {0}", generatedFilePath);
    }

    private static string GenerateSourceContents(SortedDictionary<string, AssemblyNameDefinition> assemblies)
    {
        #pragma warning disable format
        return
        $$"""
        /*
         * Copyright The OpenTelemetry Authors
         * SPDX-License-Identifier: Apache-2.0
         */

        // Auto-generated file, do not change it - generated by the {{nameof(AssemblyRedirectionSourceGenerator)}} type

        #include "cor_profiler.h"

        #ifdef _WIN32
        #define STR(Z1) #Z1
        #define AUTO_MAJOR STR(OTEL_AUTO_VERSION_MAJOR) 
        
        namespace trace
        {
        void CorProfiler::InitNetFxAssemblyRedirectsMap()
        {
            const USHORT auto_major = atoi(AUTO_MAJOR);

            assembly_version_redirect_map_.insert({
                {{GenerateEntries(assemblies)}}
            });
        }
        }
        #endif

        """;
        #pragma warning restore format
    }

    private static string GenerateEntries(SortedDictionary<string, AssemblyNameDefinition> assemblies)
    {
        var longLineLength = 80;
        var sb = new StringBuilder(assemblies.Count * longLineLength);

        foreach (var kvp in assemblies)
        {
            var v = kvp.Value.Version!;
            if (kvp.Key != "OpenTelemetry.AutoInstrumentation")
            {
                sb.AppendLine($"        {{ L\"{kvp.Key}\", {{{v.Major}, {v.Minor}, {v.Build}, {v.Revision}}} }},");
            }
            else
            {
                sb.AppendLine($"        {{ L\"{kvp.Key}\", {{auto_major, 0, 0, 0}} }},");
            }
        }

        return sb.ToString()
            .AsSpan() // Optimisation for following string manipulations
            .Trim() // Remove whitespaces
            .TrimEnd(',') // Remove trailing comma
            .ToString();
    }
}
