using Knoks.Api.Controllers.Base;
using Knoks.Api.Entities;
using Knoks.Core.Entities;
using Knoks.Core.Logic.Interfaces;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;

namespace Knoks.Api
{
    public static class Extensions
    {
        public static T Load<T>(this IConfiguration config) where T : new()
        {
            return config.Load(typeof(T).Name, new T());
        }

        public static T Load<T>(this IConfiguration config, string key) where T : new()
        {
            return config.Load(key, new T());
        }

        public static T Load<T>(this IConfiguration config, T instance)
        {
            return config.Load(typeof(T).Name, instance);
        }

        public static T Load<T>(this IConfiguration config, string key, T instance)
        {
            config.GetSection(key).Bind(instance);
            return instance;
        }

        public static HttpRequestInfo HttpRequestInfo(this HttpRequest request)
        {
            return new HttpRequestInfo
            {
                Url = request.GetDisplayUrl(),
                Headers = request.Headers,
                Cookies = request.Cookies,
                Method = request.Method
            };
        }

        public static T Init<T>(this IServiceProvider request) where T : IInitializable
        {
            var obj = request.NewInstance<T>();
            ((IInitializable)obj).Initialize().Wait();
            return obj;
        }

        public static T NewInstance<T>(this IServiceProvider request)
        {
            var type = typeof(T);
            var parameters = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault()?
                .GetParameters().Select(param => request.GetRequiredService(param.ParameterType)).ToArray();

            return (T)Activator.CreateInstance(type, parameters);
        }

        public static ApiBaseController GetApiBaseController(this ActionExecutingContext context)
        {
            return GetApiBaseController(context.Controller);
        }

        public static ApiBaseController GetApiBaseController(this ActionExecutedContext context)
        {
            return GetApiBaseController(context.Controller);
        }

        private static ApiBaseController GetApiBaseController(object controller)
        {
            var apiCaseController = controller as ApiBaseController;
            if (apiCaseController == null)
                throw new ApiResponseException(ApiErrorType.Undefined, $"The controller {controller.GetType().Name} does not inherit from '{nameof(ApiBaseController)}'");

            return apiCaseController;
        }

        public static Type GetTypeArgumentIfGenericTypeIs(this Type type, Type genericType, int index = 0)
        {
            return type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == genericType ?
                 type.GenericTypeArguments[index] : null;
        }

        public static string Value(this XElement element, params string[] elementNames)
        {
            var namespaceName = element.Document.Root.Name.Namespace.NamespaceName;
            foreach (var name in elementNames)
                element = element.Element(XName.Get(name, namespaceName));

            return element?.Value;
        }
    }
}
