using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Mvc;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Http;
using AppLibs.Libs.Pages;
using System.Security.Cryptography;

namespace AppLibs.Libs
{
    public static class ControllerExtensions
    {
        public static string AbsoluteContent(this IUrlHelper url, string contentPath)
        {
            HttpRequest request = url.ActionContext.HttpContext.Request;
            return new Uri(new Uri(request.Scheme + "://" + request.Host.Value), url.Content(contentPath)).ToString();
        }

        private static async Task RenderLayoutAsync(Controller c, IPage a, string layN)
        {
            string nLay = "";
            switch (layN)
            {
                case "None":
                    return;
                case "":
                    nLay = "_LayoutAsync";
                    break;
                default:
                    nLay = "_Layout" + layN;
                    break;
            }
            var t3 = ControllerExtensions.RenderViewAsync(c, nLay, null, true);
            var t4 = ControllerExtensions.RenderSectionScript(c, nLay);
            await Task.WhenAll(t3, t4);
            a.Content = t3.Result;
            a.Scripts = t4.Result;
        }
        public static async Task<IActionResult> ViewAsync(this Controller controller)
        {
            string vr = controller.HttpContext.Request.Query["vr"].ToString();
            string layName = GetViewData<string>(controller, "LayoutName", "");
            if (layName == "") {
                layName = "Async";
                controller.ViewData["LayoutName"] = layName;
            };
            switch (vr)
            {
                case "cAsync":
                    APage a = new APage();
                    var t1 = ControllerExtensions.RenderViewAsync(controller, true);
                    var t2 = ControllerExtensions.RenderSectionScript(controller);
                    await Task.WhenAll(t1, t2);
                    a.Title = GetViewData<string>(controller, "Title");
                    a.PageId = GetViewData<string>(controller, "PageID");
                    a.FlexPageId = GetViewData<string>(controller, "FlexPage", "None");
                    a.LayoutName = layName;
                    a.Content = t1.Result;
                    a.Scripts = t2.Result;
                    return a.Json();
                case "lAsync":
                    APageLayout a1 = new APageLayout();
                    var t = ControllerExtensions.RenderLayoutAsync(controller, a1, layName);
                    await t;
                    return a1.Json();
                default:
                    return await Task.FromResult(controller.View());
            }
        }
        private static T GetViewData<T>(Controller controller, string key, T defaultValue = default!)
        {
            if (!controller.ViewData.ContainsKey(key))
                return defaultValue;

            object? rawValue = controller.ViewData[key];
            if (rawValue == null)
                return defaultValue;

            try
            {
                // Trả lại trực tiếp nếu đã đúng kiểu
                if (rawValue is T tVal)
                    return tVal;

                // Ép kiểu từ chuỗi
                string? strValue = rawValue.ToString();
                if (string.IsNullOrWhiteSpace(strValue))
                    return defaultValue;

                if (typeof(T).IsEnum)
                {
                    return (T)Enum.Parse(typeof(T), strValue, ignoreCase: true);
                }

                object? converted = Convert.ChangeType(strValue, typeof(T));
                return converted is T result ? result : defaultValue;
            }
            catch
            {
                return defaultValue;
            }
        }

        public static async Task<string> RenderSectionScript(this Controller controller)
        {
            controller.ViewData["Layout"] = "SectionScript";
            return await RenderViewAsync(controller);
        }

        public static async Task<string> RenderSectionScript(this Controller controller, string viewName)
        {
            controller.ViewData["Layout"] = "SectionScript";
            return await RenderViewAsync(controller, viewName, null);
        }

        public static async Task<string> RenderViewAsync(this Controller controller, bool partial = false)
        {
            return await RenderViewAsync(controller, null, partial);
        }

        public static async Task<string> RenderViewAsync(this Controller controller, object? model, bool partial = false)
        {
            return await RenderViewAsync(controller, controller.ControllerContext.ActionDescriptor.ActionName, model, partial);
        }

        public static async Task<string> RenderViewAsync(this Controller controller, string viewName, object? model, bool partial = false)
        {

            controller.ViewData.Model = model;

            using (var writer = new StringWriter())
            {
                IViewEngine? viewEngine = controller.HttpContext.RequestServices.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine;
                ViewEngineResult viewResult = viewEngine == null ? throw new ArgumentNullException("View Engigne Result not null") : viewEngine.FindView(controller.ControllerContext, viewName, !partial);

                if (viewResult.Success == false)
                {
                    return $"A view with the name {viewName} could not be found";
                }


                ViewContext viewContext = new ViewContext(
                    controller.ControllerContext,
                    viewResult.View,
                    controller.ViewData,
                    controller.TempData,
                    writer,
                    new HtmlHelperOptions()
                );
                await viewResult.View.RenderAsync(viewContext);
                return Regex.Replace(writer.ToString(), @"(?>[^\S ]\s*| \s{2,})", "").Trim();
            }
        }
    }
}
