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

using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Language.Extensions;
using Microsoft.AspNetCore.Razor.Language.Intermediate;

namespace Microsoft.AspNetCore.Razor.Language.Components
{
	internal class ComponentTemplateDiagnosticPass : ComponentIntermediateNodePassBase, IRazorOptimizationPass
	{
		// Runs after components/eventhandlers/ref/bind. We need to check for templates in all of those
		// places.
		public override int Order => 150;

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

			var visitor = new Visitor();
			visitor.Visit( documentNode );

			for ( var i = 0; i < visitor.Candidates.Count; i++ )
			{
				var candidate = visitor.Candidates[i];
				candidate.Parent.Diagnostics.Add( ComponentDiagnosticFactory.Create_TemplateInvalidLocation( candidate.Node.Source ) );

				// Remove the offending node since we don't know how to render it. This means that the user won't get C#
				// completion at this location, which is fine because it's inside an HTML attribute.
				candidate.Remove();
			}
		}

		private class Visitor : IntermediateNodeWalker, IExtensionIntermediateNodeVisitor<TemplateIntermediateNode>
		{
			public List<IntermediateNodeReference> Candidates { get; } = new List<IntermediateNodeReference>();

			public void VisitExtension( TemplateIntermediateNode node )
			{
				// We found a template, let's check where it's located.
				for ( var i = 0; i < Ancestors.Count; i++ )
				{
					var ancestor = Ancestors[i];

					if (
						// Inside markup attribute
						ancestor is HtmlAttributeIntermediateNode ||

						// Inside component attribute
						ancestor is ComponentAttributeIntermediateNode ||

						// Inside malformed ref attribute
						ancestor is TagHelperPropertyIntermediateNode ||

						// Inside a directive attribute
						ancestor is TagHelperDirectiveAttributeIntermediateNode )
					{
						Candidates.Add( new IntermediateNodeReference( Parent, node ) );
					}
				}
			}
		}
	}
}
