using Infrastructure.Helper;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ZR.Common.DynamicApiSimple;

class ApiConvention : IApplicationModelConvention
{
    public void Apply(ApplicationModel application)
    {
        foreach (var controller in application.Controllers)
        {
            var type = controller.ControllerType;
            if (typeof(IDynamicApi).IsAssignableFrom(type) || type.IsDefined(typeof(DynamicApiAttribute), true))
            {
                ClearAction(controller);
                ConfigureApiExplorer(controller);
                ConfigureSelector(controller);
            }
        }
    }

    private void ClearAction(ControllerModel controller)
    {
        Type genericBaseType = AssemblyUtils.GetGenericTypeByName("BaseService`1");
        var needRemoveAction = controller.Actions
            .Where(action => !action.ActionMethod.DeclaringType.IsDerivedFromGenericBaseRepository(genericBaseType))
            .ToList();

        foreach (var actionModel in needRemoveAction)
        {
            controller.Actions.Remove(actionModel);
        }
    }


    private static void ConfigureApiExplorer(ControllerModel controller)
    {
        if (!controller.ApiExplorer.IsVisible.HasValue)
            controller.ApiExplorer.IsVisible = true;

        foreach (var action in controller.Actions)
        {
            if (!action.ApiExplorer.IsVisible.HasValue)
            {
                action.ApiExplorer.IsVisible = true;
            }
        }
    }

    private void ConfigureSelector(ControllerModel controller)
    {
        RemoveEmptySelectors(controller.Selectors);

        if (controller.Selectors.Any(selector => selector.AttributeRouteModel != null))
            return;

        foreach (var action in controller.Actions)
        {
            ConfigureSelector(action);
        }
    }

    private static void RemoveEmptySelectors(IList<SelectorModel> selectors)
    {
        for (var i = selectors.Count - 1; i >= 0; i--)
        {
            var selector = selectors[i];
            if (selector.AttributeRouteModel == null &&
               (selector.ActionConstraints == null || selector.ActionConstraints.Count <= 0) &&
               (selector.EndpointMetadata == null || selector.EndpointMetadata.Count <= 0))
            {
                selectors.Remove(selector);
            }
        }
    }

    private void ConfigureSelector(ActionModel action)
    {
        RemoveEmptySelectors(action.Selectors);

        if (action.Selectors.Count <= 0)
            AddServiceSelector(action);
        else
            NormalizeSelectorRoutes(action);
    }

    private void AddServiceSelector(ActionModel action)
    {
        var template = new Microsoft.AspNetCore.Mvc.RouteAttribute(GetRouteTemplate(action));
        var selector = new SelectorModel
        {
            AttributeRouteModel = new AttributeRouteModel(template)
        };
        selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { GetHttpMethod(action) }));
        action.Selectors.Add(selector);
    }

    private void NormalizeSelectorRoutes(ActionModel action)
    {
        foreach (var selector in action.Selectors)
        {
            var template = new Microsoft.AspNetCore.Mvc.RouteAttribute(GetRouteTemplate(action,selector));
            selector.AttributeRouteModel = new AttributeRouteModel(template);
            if (selector.ActionConstraints.OfType<HttpMethodActionConstraint>().FirstOrDefault()?.HttpMethods?.FirstOrDefault() == null)
                selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { GetHttpMethod(action) }));

        }
    }

    private string GetRouteTemplate(ActionModel action,SelectorModel selectorModel=null)
    {
        var routeTemplate = new StringBuilder();
        var names = action.Controller.ControllerType.Namespace.Split('.');
        if (names.Length > 2)
        {
            routeTemplate.Append(names[^2]);
        }

        // Controller
        var controllerName = action.Controller.ControllerName;
        if (controllerName.EndsWith("Service"))
            controllerName = controllerName[0..^7];

        if (selectorModel is { AttributeRouteModel: not null })
        {
            if (!string.IsNullOrWhiteSpace(selectorModel.AttributeRouteModel?.Template))
            {
                if (selectorModel.AttributeRouteModel.Template.StartsWith("/"))
                {
                    routeTemplate.Append(selectorModel.AttributeRouteModel.Template);
                }
                else
                {
                    routeTemplate.Append($"{BaseRoute}/{controllerName}/{selectorModel.AttributeRouteModel.Template}");
                }
            }
        }
        else
        {
            routeTemplate.Append($"{BaseRoute}/{controllerName}");

            // Action
            var actionName = action.ActionName;
            if (actionName.EndsWith("Async") || actionName.EndsWith("async"))
                actionName = actionName[..^"Async".Length];

            if (!string.IsNullOrEmpty(actionName))
            {
                routeTemplate.Append($"/{RemoveHttpMethodPrefix(actionName)}");
            }
        }
        

        return routeTemplate.ToString();
    }

    private static string GetHttpMethod(ActionModel action)
    {
        var actionName = action.ActionName.ToLower();
        string Method = string.Empty;
        if (!string.IsNullOrEmpty(actionName))
        {
            Method = GetName(actionName);
        }
        return Method;
    }

    private static string GetName(string actionName)
    {
        string result = "POST";
        foreach (string key in Methods.Keys)
        {
            if (actionName.Contains(key))
            {
                result = Methods[key];
                break;
            }

        }
        return result;
    }
    internal static Dictionary<string, string> Methods { get; private set; }
    internal static string BaseRoute { get; private set; } = "api";
    static ApiConvention()
    {
        Methods = new Dictionary<string, string>()
        {

            ["get"] = "GET",
            ["find"] = "GET",
            ["fetch"] = "GET",
            ["query"] = "GET",
            ["post"] = "POST",
            ["add"] = "POST",
            ["create"] = "POST",
            ["insert"] = "POST",
            ["submit"] = "POST",
            ["put"] = "POST",
            ["update"] = "POST",
            ["delete"] = "DELETE",
            ["remove"] = "DELETE",
            ["clear"] = "DELETE",
            ["patch"] = "PATCH"
        };

    }
    private static string RemoveHttpMethodPrefix(string actionName)
    {
        foreach (var method in Methods.Keys)
        {
            if (actionName.StartsWith(method, StringComparison.OrdinalIgnoreCase))
            {
                // 移除前缀并返回结果
                return actionName.Substring(method.Length);
            }
        }

        return actionName; // 如果没有找到前缀,返回原始名称
    }
}