using InnovEnergy.Lib.Utils;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace InnovEnergy.Lib.SrcGen;

public static class GenerateAttributes
{
    public static async Task Generate(Project project, Compilation compilation)
    {
        var syntaxTrees = await project
                               .Documents
                               .Where(d => d.Project == project)
                               .Where(d => d.SupportsSyntaxTree)
                               .Select(async d => await d.GetSyntaxRootAsync())
                               .WhenAll();

        syntaxTrees.NotNull()
                   .SelectMany(c => c.GetGenerateAttributes())
                   .ForEach(GenerateFile);

        String GenerateFile(AttributeSyntax attribute)
        {
            var derivedType   = attribute.GetAncestor<StructDeclarationSyntax>();
            var defineArgs    = attribute.ArgumentList?.Arguments ?? Enumerable.Empty<SyntaxNode>();
            var filePath      = derivedType.SyntaxTree.FilePath;
            var derivedName   = derivedType.Identifier.ValueText;
            var baseRef       = attribute.GetGenericArgument();
            var baseType      = compilation.GetDefinition<StructDeclarationSyntax>(baseRef);
            var baseName      = baseType.Identifier.ValueText;
            var baseFile      = baseType.GetAncestor<CompilationUnitSyntax>();
            var generatedPath = filePath.GetGeneratedPath(baseName);

            var defines = defineArgs
                         .Select(n => n.ToString())
                         .Select(s => s.Replace("\"", "").ToUpper())
                         .Select(s => $"#define {s}");

            Console.WriteLine($"Generating {generatedPath}");
         
            var code = GenerateCode();

            var fileContents = Utils.AutoGeneratedMessage(nameof(GenerateAttributes))
                             + defines.JoinLines() + "\n"
                             + code; 

            File.WriteAllText(generatedPath, fileContents);

            return generatedPath;
            
            
            String GenerateCode()
            {
                try
                {
                    return baseFile
                          .AddPartialModifier()
                          .ReplaceIdentifiers(baseName, derivedName)
                          .RemoveNotImplemented()
                          .GetText()
                          .ToString();
                }
                catch (Exception e)
                {
                    return $"Failed to generate source for {filePath}\n\n{e}"
                           .SplitLines()
                           .Select(l => $"// {l}")
                           .JoinLines();
                }
            }

        }
    }
}