using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;

using System.Text;

namespace ExtensiblePortfolioSite
{
    [HtmlTargetElement("cache")]
    public class InvalidableCacheTagHelper : TagHelper
    {
        [HtmlAttributeName("expires-on")]
        public DateTime ExpiresOn { get; set; }

        [HtmlAttributeName("name")]
        public String? Name { get; set; } = null;

        [HtmlAttributeName("invalidate-events")]
        public String? Event { get; set; } = null;

        private static readonly SortedDictionary<String, HashSet<String>> InvalidateTriggers = new();
        private static readonly SortedDictionary<String, String> CachedContent = new();

        public static void TriggerInvalidateEvent(String EventName)
        {
            lock (InvalidateTriggers)
                if (InvalidateTriggers.TryGetValue(EventName, out HashSet<String>? Caches))
                    foreach (String Cache in Caches)
                        InvalidateCachedTag(Cache);
        }

        public static void InvalidateCachedTag(String NameOrUniqueId) => CachedContent.Remove(NameOrUniqueId);

        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            String Key = Name ?? context.UniqueId;

            output.TagName = "div";
            output.TagMode = TagMode.StartTagAndEndTag;

            if (DateTime.Now < ExpiresOn && CachedContent.TryGetValue(Key, out String? Content))
                output.Content.SetHtmlContent(Content);
            else
            {
                TagHelperContent content = await output.GetChildContentAsync(true);
                Content = content.GetContent();
                CachedContent[Key] = Content;
                output.Content.SetHtmlContent(Content);

                // ensure Invalidate Triggers
                if (Event != null)
                    lock (InvalidateTriggers)
                        foreach (String Event in Event.Split(';', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries))
                        {
                            if (!InvalidateTriggers.TryGetValue(Event, out HashSet<String>? Caches))
                            {
                                Caches = new HashSet<string>();
                                InvalidateTriggers.Add(Event, Caches);
                            }
                            Caches.Add(Key);
                        }
            }
        }
        public override void Process(TagHelperContext context, TagHelperOutput output) => this.ProcessAsync(context, output).ConfigureAwait(false).GetAwaiter().GetResult();
    }
}
