// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable disable

using Microsoft.AspNetCore.Razor.Language.Intermediate;

namespace Microsoft.AspNetCore.Razor.Language.Components;

internal class ComponentReferenceCaptureLoweringPass : ComponentIntermediateNodePassBase, IRazorOptimizationPass
{
    // Run after component lowering pass
    public override int Order => 50;

    protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
    {
        if (!IsComponentDocument(documentNode))
        {
            return;
        }

        var @namespace = documentNode.FindPrimaryNamespace();
        var @class = documentNode.FindPrimaryClass();
        if (@namespace == null || @class == null)
        {
            // Nothing to do, bail. We can't function without the standard structure.
            return;
        }

        var references = documentNode.FindDescendantReferences<TagHelperDirectiveAttributeIntermediateNode>();
        for (var i = 0; i < references.Count; i++)
        {
            var reference = references[i];
            var node = (TagHelperDirectiveAttributeIntermediateNode)reference.Node;

            if (node.TagHelper.IsRefTagHelper())
            {
                reference.Replace(RewriteUsage(@class, reference.Parent, node));
            }
        }
    }

    private IntermediateNode RewriteUsage(ClassDeclarationIntermediateNode classNode, IntermediateNode parent, TagHelperDirectiveAttributeIntermediateNode node)
    {
        // If we can't get a nonempty attribute name, do nothing because there will
        // already be a diagnostic for empty values
        var identifierToken = DetermineIdentifierToken(node);
        if (identifierToken == null)
        {
            return node;
        }

        // Determine whether this is an element capture or a component capture, and
        // if applicable the type name that will appear in the resulting capture code
        var componentTagHelper = (parent as ComponentIntermediateNode)?.Component;
        if (componentTagHelper != null)
        {
            return new ReferenceCaptureIntermediateNode(identifierToken, componentTagHelper.GetTypeName());
        }
        else
        {
            return new ReferenceCaptureIntermediateNode(identifierToken);
        }
    }

    private IntermediateToken DetermineIdentifierToken(TagHelperDirectiveAttributeIntermediateNode attributeNode)
    {
        IntermediateToken foundToken = null;

        if (attributeNode.Children.Count == 1)
        {
            if (attributeNode.Children[0] is IntermediateToken token)
            {
                foundToken = token;
            }
            else if (attributeNode.Children[0] is CSharpExpressionIntermediateNode csharpNode)
            {
                if (csharpNode.Children.Count == 1)
                {
                    foundToken = csharpNode.Children[0] as IntermediateToken;
                }
            }
        }

        return !string.IsNullOrWhiteSpace(foundToken?.Content) ? foundToken : null;
    }
}
