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

using System;
using System.Collections.Generic;
using System.Linq;

namespace Microsoft.AspNetCore.Razor.Language
{
	internal class DefaultTagMatchingRuleDescriptorBuilder : TagMatchingRuleDescriptorBuilder
	{
		private readonly DefaultTagHelperDescriptorBuilder _parent;
		private List<DefaultRequiredAttributeDescriptorBuilder> _requiredAttributeBuilders;
		private RazorDiagnosticCollection _diagnostics;

		internal DefaultTagMatchingRuleDescriptorBuilder( DefaultTagHelperDescriptorBuilder parent )
		{
			_parent = parent;
		}

		public override string TagName { get; set; }

		public override string ParentTag { get; set; }

		public override TagStructure TagStructure { get; set; }

		internal bool CaseSensitive => _parent.CaseSensitive;

		public override RazorDiagnosticCollection Diagnostics
		{
			get
			{
				if ( _diagnostics == null )
				{
					_diagnostics = new RazorDiagnosticCollection();
				}

				return _diagnostics;
			}
		}

		public override IReadOnlyList<RequiredAttributeDescriptorBuilder> Attributes
		{
			get
			{
				EnsureRequiredAttributeBuilders();

				return _requiredAttributeBuilders;
			}
		}

		public override void Attribute( Action<RequiredAttributeDescriptorBuilder> configure )
		{
			if ( configure == null )
			{
				throw new ArgumentNullException( nameof( configure ) );
			}

			EnsureRequiredAttributeBuilders();

			var builder = new DefaultRequiredAttributeDescriptorBuilder( this );
			configure( builder );
			_requiredAttributeBuilders.Add( builder );
		}

		public TagMatchingRuleDescriptor Build()
		{
			var diagnostics = Validate();
			if ( _diagnostics != null )
			{
				diagnostics ??= new();
				diagnostics.UnionWith( _diagnostics );
			}

			var requiredAttributes = Array.Empty<RequiredAttributeDescriptor>();
			if ( _requiredAttributeBuilders != null )
			{
				var requiredAttributeSet = new HashSet<RequiredAttributeDescriptor>( RequiredAttributeDescriptorComparer.Default );
				for ( var i = 0; i < _requiredAttributeBuilders.Count; i++ )
				{
					requiredAttributeSet.Add( _requiredAttributeBuilders[i].Build() );
				}

				requiredAttributes = requiredAttributeSet.ToArray();
			}

			var rule = new DefaultTagMatchingRuleDescriptor(
				TagName,
				ParentTag,
				TagStructure,
				CaseSensitive,
				requiredAttributes,
				diagnostics?.ToArray() ?? Array.Empty<RazorDiagnostic>() );

			return rule;
		}

		private HashSet<RazorDiagnostic> Validate()
		{
			HashSet<RazorDiagnostic> diagnostics = null;

			if ( string.IsNullOrWhiteSpace( TagName ) )
			{
				var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedTagNameNullOrWhitespace();

				diagnostics ??= new();
				diagnostics.Add( diagnostic );
			}
			else if ( TagName != TagHelperMatchingConventions.ElementCatchAllName )
			{
				foreach ( var character in TagName )
				{
					if ( char.IsWhiteSpace( character ) || HtmlConventions.IsInvalidNonWhitespaceHtmlCharacters( character ) )
					{
						var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedTagName( TagName, character );

						diagnostics ??= new();
						diagnostics.Add( diagnostic );
					}
				}
			}

			if ( ParentTag != null )
			{
				if ( string.IsNullOrWhiteSpace( ParentTag ) )
				{
					var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedParentTagNameNullOrWhitespace();

					diagnostics ??= new();
					diagnostics.Add( diagnostic );
				}
				else
				{
					foreach ( var character in ParentTag )
					{
						if ( char.IsWhiteSpace( character ) || HtmlConventions.IsInvalidNonWhitespaceHtmlCharacters( character ) )
						{
							var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedParentTagName( ParentTag, character );

							diagnostics ??= new();
							diagnostics.Add( diagnostic );
						}
					}
				}
			}

			return diagnostics;
		}

		private void EnsureRequiredAttributeBuilders()
		{
			if ( _requiredAttributeBuilders == null )
			{
				_requiredAttributeBuilders = new List<DefaultRequiredAttributeDescriptorBuilder>();
			}
		}
	}
}
