@namespace Oqtane.UI
@using System.Net
@inject IJSRuntime JSRuntime
@inject NavigationManager NavigationManager
@inject SiteState SiteState

@DynamicComponent

@code {
    [CascadingParameter] PageState PageState { get; set; }

    RenderFragment DynamicComponent { get; set; }

    protected override void OnParametersSet()
    {
        // handle page redirection
        if (!string.IsNullOrEmpty(PageState.Page.Url))
        {
            NavigationManager.NavigateTo(PageState.Page.Url);
            return;
        }

        // set page title
        if (!string.IsNullOrEmpty(PageState.Page.Title))
        {
            SiteState.Properties.PageTitle = PageState.Page.Title;
        }
        else
        {
            SiteState.Properties.PageTitle = PageState.Page.Name + " - " + PageState.Site.Name;
        }

        // set page head content
        var headcontent = "";

        // favicon
        if (PageState.Site.FaviconFileId != null)
        {
            headcontent += $"<link id=\"app-favicon\" rel=\"icon\" href=\"{Utilities.FileUrl(PageState.Alias, PageState.Site.FaviconFileId.Value)}\" />\n";
        }
        else
        {
            headcontent += $"<link id=\"app-favicon\" rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n";
        }

        // head content
        AddHeadContent(headcontent, PageState.Site.HeadContent);
        if (!string.IsNullOrEmpty(PageState.Site.HeadContent))
        {
            headcontent = AddHeadContent(headcontent, PageState.Site.HeadContent);
        }
        if (!string.IsNullOrEmpty(PageState.Page.HeadContent))
        {
            headcontent = AddHeadContent(headcontent, PageState.Page.HeadContent);
        }
        SiteState.Properties.HeadContent = headcontent;

        DynamicComponent = builder =>
        {
            builder.OpenComponent(0, Type.GetType(PageState.Page.ThemeType));
            builder.CloseComponent();
        };
    }

    private string AddHeadContent(string headcontent, string content)
    {
        if (!string.IsNullOrEmpty(content))
        {
            if (PageState.RenderMode == RenderModes.Interactive)
            {
                // remove scripts
                var index = content.IndexOf("<script");
                while (index >= 0)
                {
                    content = content.Remove(index, content.IndexOf("</script>") + 9 - index);
                    index = content.IndexOf("<script");
                }
            }
            headcontent += content + "\n";
        }
        return headcontent;
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        // force authenticated user to provide email address (email may be missing if using external login)
        if (PageState.User != null && PageState.User.IsAuthenticated && string.IsNullOrEmpty(PageState.User.Email) && PageState.Route.PagePath != "profile")
        {
            NavigationManager.NavigateTo(Utilities.NavigateUrl(PageState.Alias.Path, "profile", "returnurl=" + WebUtility.UrlEncode(PageState.Route.PathAndQuery)));
            return;
        }

        if (!firstRender)
        {
            if (!string.IsNullOrEmpty(PageState.Page.HeadContent) && PageState.Page.HeadContent.Contains("<script"))
            {
                await InjectScripts(PageState.Page.HeadContent, ResourceLocation.Head);
            }
            if (!string.IsNullOrEmpty(PageState.Page.BodyContent) && PageState.Page.BodyContent.Contains("<script"))
            {
                await InjectScripts(PageState.Page.BodyContent, ResourceLocation.Body);
            }
        }

        // style sheets
        if (PageState.Page.Resources != null && PageState.Page.Resources.Exists(item => item.ResourceType == ResourceType.Stylesheet))
        {
            var interop = new Interop(JSRuntime);
            string batch = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff");
            var links = new List<object>();
            foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
            {
                var prefix = "app-stylesheet-" + resource.Level.ToString().ToLower();
                var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
                links.Add(new { id = prefix + "-" + batch + "-" + (links.Count + 1).ToString("00"), rel = "stylesheet", href = url, type = "text/css", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", insertbefore = prefix });
            }
            if (links.Any())
            {
                await interop.IncludeLinks(links.ToArray());
            }
            await interop.RemoveElementsById("app-stylesheet-page-", "", "app-stylesheet-page-" + batch + "-00");
            await interop.RemoveElementsById("app-stylesheet-module-", "", "app-stylesheet-module-" + batch + "-00");
        }
    }

    private async Task InjectScripts(string content, ResourceLocation location)
    {
        // inject scripts into page dynamically
        var interop = new Interop(JSRuntime);
        var scripts = new List<object>();
        var count = 0;
        var index = content.IndexOf("<script");
        while (index >= 0)
        {
            var script = content.Substring(index, content.IndexOf("</script>", index) + 9 - index);
            // get script attributes
            var attributes = script.Substring(0, script.IndexOf(">")).Replace("\"", "").Split(" ");
            string id = "";
            string src = "";
            string integrity = "";
            string crossorigin = "";
            string type = "";
            var dataAttributes = new Dictionary<string, string>();
            foreach (var attribute in attributes)
            {
                if (attribute.Contains("="))
                {
                    var value = attribute.Split("=");
                    switch (value[0])
                    {
                        case "id":
                            id = value[1];
                            break;
                        case "src":
                            src = value[1];
                            break;
                        case "integrity":
                            integrity = value[1];
                            break;
                        case "crossorigin":
                            crossorigin = value[1];
                            break;
                        case "type":
                            type = value[1];
                            break;
                        default:
                            if(!string.IsNullOrWhiteSpace(value[0]) && value[0].StartsWith("data-"))
                            {
                                dataAttributes.Add(value[0], value[1]);
                            }
                            break;
                    }
                }
            }
            // inject script
            if (!string.IsNullOrEmpty(src))
            {
                src = (src.Contains("://")) ? src : PageState.Alias.BaseUrl + src;
                scripts.Add(new { href = src, bundle = "", integrity = integrity, crossorigin = crossorigin, es6module = (type == "module"), location = location.ToString().ToLower(), dataAttributes = dataAttributes });
            }
            else
            {
                // inline script must have an id attribute
                if (id == "")
                {
                    count += 1;
                    id = $"page{PageState.Page.PageId}-script{count}";
                }
                index = script.IndexOf(">") + 1;
                await interop.IncludeScript(id, "", "", "", "", script.Substring(index, script.IndexOf("</script>") - index), location.ToString().ToLower());
            }
            index = content.IndexOf("<script", index + 1);
        }
        if (scripts.Any())
        {
            await interop.IncludeScripts(scripts.ToArray());
        }
    }

    private string ManageStyleSheets(List<Resource> resources, Alias alias)
    {
        var stylesheets = "";
        if (resources != null)
        {
            string batch = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff");
            int count = 0;
            foreach (var resource in resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
            {
                if (resource.Url.StartsWith("~"))
                {
                    resource.Url = resource.Url.Replace("~", "/Themes/" + Utilities.GetTypeName(resource.Namespace) + "/").Replace("//", "/");
                }
                if (!resource.Url.Contains("://") && alias.BaseUrl != "" && !resource.Url.StartsWith(alias.BaseUrl))
                {
                    resource.Url = alias.BaseUrl + resource.Url;
                }

                if (!stylesheets.Contains(resource.Url, StringComparison.OrdinalIgnoreCase))
                {
                    count++;
                    string id = "id=\"app-stylesheet-" + ResourceLevel.Page.ToString().ToLower() + "-" + batch + "-" + count.ToString("00") + "\" ";
                    stylesheets += "<link " + id + "rel=\"stylesheet\" href=\"" + resource.Url + "\"" + (!string.IsNullOrEmpty(resource.Integrity) ? " integrity=\"" + resource.Integrity + "\"" : "") + (!string.IsNullOrEmpty(resource.CrossOrigin) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") + " type=\"text/css\"/>" + Environment.NewLine;
                }
            }
        }
        return stylesheets;
    }

    private string ManageScripts(List<Resource> resources, Alias alias)
    {
        var scripts = "";
        if (resources != null)
        {
            foreach (var resource in resources.Where(item => item.ResourceType == ResourceType.Script && item.Location == ResourceLocation.Head))
            {
                var script = CreateScript(resource, alias);
                if (!scripts.Contains(script, StringComparison.OrdinalIgnoreCase))
                {
                    scripts += script + Environment.NewLine;
                }
            }
        }
        return scripts;
    }

    private string CreateScript(Resource resource, Alias alias)
    {
        if (!string.IsNullOrEmpty(resource.Url))
        {
            var url = (resource.Url.Contains("://")) ? resource.Url : alias.BaseUrl + resource.Url;
            return "<script src=\"" + url + "\"" +
                ((!string.IsNullOrEmpty(resource.Integrity)) ? " integrity=\"" + resource.Integrity + "\"" : "") +
                ((!string.IsNullOrEmpty(resource.CrossOrigin)) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") +
                "></script>";
        }
        else
        {
            // inline script
            return "<script>" + resource.Content + "</script>";
        }
    }
}
