namespace Kit.Helpers.Routes
{
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Routing;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;

    public partial class ControllerRouteFluent<TController> : IEndPointFluent<ControllerRouteFluent<TController>>
        where TController : Controller
    {
        private readonly IRouteBuilder _routeTable;
        private readonly List<RouteFluent> _routes;

        public ControllerRouteFluent(IRouteBuilder routeTable)
        {
            _routeTable = routeTable;
            _routes = new List<RouteFluent>();
        }

        #region Generic Result Actions

        private ControllerRouteFluent<TController> AddRouteLambda(string url, LambdaExpression actionSelector)
        {
            // получаем имя контроллера
            string controllerName = typeof(TController).Name;
            if (controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) && controllerName.Length > "Controller".Length)
            {
                controllerName = controllerName.Remove(controllerName.Length - "Controller".Length, "Controller".Length);
            }

            // получаем имя действия
            var unaryExpression = (UnaryExpression)actionSelector.Body;
            var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
            var constantExpression = (ConstantExpression)methodCallExpression.Object;
            var methodInfo = (MethodInfo)constantExpression.Value;
            string actionName = methodInfo.Name;

            _routes.Add(_routeTable.AddRoute($"{controllerName}_{actionName}_{Guid.NewGuid()}", url, controllerName, actionName, methodInfo));
            return this;
        }

        #endregion

        #region IRouteFluent<ControllerRouteFluent<TController>>

        public ControllerRouteFluent<TController> NeedHttpGet(bool required = true)
        {
            if (_routes.Any()) _routes.Last().NeedHttpGet(required);
            return this;
        }

        public ControllerRouteFluent<TController> NeedHttpPost(bool required = true)
        {
            if (_routes.Any()) _routes.Last().NeedHttpPost(required);
            return this;
        }

        public ControllerRouteFluent<TController> NeedHttpPut(bool required = true)
        {
            if (_routes.Any()) _routes.Last().NeedHttpPut(required);
            return this;
        }

        public ControllerRouteFluent<TController> NeedHttpDelete(bool required = true)
        {
            if (_routes.Any()) _routes.Last().NeedHttpDelete(required);
            return this;
        }

        public ControllerRouteFluent<TController> WithBody(bool required = true)
        {
            if (_routes.Any()) _routes.Last().WithBody(required);
            return this;
        }

        public ControllerRouteFluent<TController> WithDefaults(object defaults)
        {
            if (_routes.Any()) _routes.Last().WithDefaults(defaults);
            return this;
        }

        public ControllerRouteFluent<TController> WithConstraints(object constraints)
        {
            if (_routes.Any()) _routes.Last().WithConstraints(constraints);
            return this;
        }

        public ControllerRouteFluent<TController> WithNamespaces(string[] namespaces)
        {
            if (_routes.Any()) _routes.Last().WithNamespaces(namespaces);
            return this;
        }

        public ControllerRouteFluent<TController> NeedSecurityKey(string securityKeyName)
        {
            if (_routes.Any()) _routes.Last().NeedSecurityKey(securityKeyName);
            return this;
        }

        public ControllerRouteFluent<TController> WithDataTokens(object dataTokens)
        {
            if (_routes.Any()) _routes.Last().WithDataTokens(dataTokens);
            return this;
        }

        public ControllerRouteFluent<TController> WithDelegate(Action<RouteFluent> routeFluentAction)
        {
            if (_routes.Any()) routeFluentAction(_routes.Last());
            return this;
        }

        public ControllerRouteFluent<TController> ForbidForReadonly(bool value = true)
        {
            if (_routes.Any()) _routes.Last().ForbidForReadonly(value);
            return this;
        }
        #endregion

        #region IRouteFluent<ControllerRouteFluent<TController>> for ALL

        public ControllerRouteFluent<TController> NeedHttpGet_All(bool required = true)
        {
            _routes.ForEach(x => x.NeedHttpGet(required));
            return this;
        }

        public ControllerRouteFluent<TController> NeedHttpPost_All(bool required = true)
        {
            _routes.ForEach(x => x.NeedHttpPost(required));
            return this;
        }

        public ControllerRouteFluent<TController> WithDefaults_All(object defaults)
        {
            _routes.ForEach(x => x.WithDefaults(defaults));
            return this;
        }

        public ControllerRouteFluent<TController> WithConstraints_All(object constraints)
        {
            _routes.ForEach(x => x.WithConstraints(constraints));
            return this;
        }

        public ControllerRouteFluent<TController> WithNamespaces_All(string[] namespaces)
        {
            _routes.ForEach(x => x.WithNamespaces(namespaces));
            return this;
        }

        public ControllerRouteFluent<TController> NeedSecurityKey_All(string securityKeyName)
        {
            _routes.ForEach(x => x.NeedSecurityKey(securityKeyName));
            return this;
        }

        public ControllerRouteFluent<TController> WithDataTokens_All(object dataTokens)
        {
            _routes.ForEach(x => x.WithDataTokens(dataTokens));
            return this;
        }

        public ControllerRouteFluent<TController> WithDelegate_All(Action<RouteFluent> routeFluentAction)
        {
            _routes.ForEach(x => routeFluentAction(x));
            return this;
        }

        #endregion
    }
}