using Apewer.Network;
using Apewer.Source;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;

using static Apewer.Internals.ApiHelper;

namespace Apewer.Web
{

    internal class ApiProcessor
    {

        internal ApiInvoker Invoker;
        internal ApiEntries Entries;
        internal ApiProvider Provider;
        ApiOptions Options;

        Stopwatch Stopwatch;

        Uri Url;
        HttpMethod Method;
        ApiRequest ApiRequest;
        ApiResponse ApiResponse;

        internal ApiProcessor()
        {
        }

        /// <summary>执行处理程序,返回错误信息。</summary>
        public string Run()
        {
            if (Options == null) Options = new ApiOptions();
            if (Options.WithDuration)
            {
                Stopwatch = new Stopwatch();
                Stopwatch.Start();
            }
            var error = Flow();
            if (Stopwatch != null)
            {
                Stopwatch.Stop();
                Stopwatch = null;
            }
            return error;
        }

        string Flow()
        {
            try
            {
                // 传入字段。
                if (Provider == null) return "服务程序无效。";
                if (Invoker == null) return "调用器无效。";
                if (Entries == null) return "入口无效。";
                Options = Invoker.Options ?? new ApiOptions();
                Provider.Options = Options;

                // 检查执行的前提条件,获取 Method 和 URL。
                var check = Check();
                if (!string.IsNullOrEmpty(check)) return check;

                // 准备请求和响应模型。
                ApiRequest = GetRequest(Provider, Options, Method, Url);
                ApiResponse = new ApiResponse();
                ApiResponse.Random = ApiRequest.Random;
                ApiResponse.Application = ApiRequest.Application;
                ApiResponse.Function = ApiRequest.Function;

                // 调用 API。
                var invoke = Invoke();
                if (!string.IsNullOrEmpty(invoke)) return invoke;

                // 输出。
                if (Stopwatch != null)
                {
                    Stopwatch.Stop();
                    ApiResponse.Duration = Stopwatch.ElapsedMilliseconds;
                    Stopwatch = null;
                }
                Output(Provider, Options, ApiResponse, ApiRequest, Method);
                return null;
            }
            catch (Exception ex)
            {
                if (Stopwatch != null)
                {
                    Stopwatch.Stop();
                    Stopwatch = null;
                }
                return ex.Message;
            }
        }

        string Check()
        {
            // 服务程序检查。
            var check = Provider.PreInvoke();
            if (!string.IsNullOrEmpty(check)) return check;

            // URL
            Url = Provider.GetUrl();
            if (Url == null) return "URL 无效。";

            Method = Provider.GetMethod();
            if (Method == HttpMethod.NULL) return "HTTP 方法无效。";
            if (Method == HttpMethod.OPTIONS) return null;

            // favicon.ico
            var lowerPath = TextUtility.AssureStarts(TextUtility.Lower(Url.AbsolutePath), "/");
            if (!Options.AllowFavIcon)
            {
                if (lowerPath.StartsWith("/favicon.ico"))
                {
                    Output(Provider, Options, null, null);
                    return "已取消对 favicon.ico 的请求。";
                }
            }

            // robots.txt
            if (!Options.AllowRobots)
            {
                if (lowerPath.StartsWith("/robots.txt"))
                {
                    const string text = "User-agent: *\nDisallow: / \n";
                    Output(Provider, Options, "text/plain", TextUtility.Bytes(text));
                    return "已取消对 robots.txt 的请求。";
                }
            }

            return null;
        }

        // 寻找入口。
        string Invoke()
        {
            var appName = ApiRequest.Application;
            var funcName = ApiRequest.Function;
            var random = ApiRequest.Random;

            Invoke(Entries.Get(appName));

            if (Stopwatch != null) ApiResponse.Duration = Stopwatch.ElapsedMilliseconds;
            ApiResponse.Application = appName;
            ApiResponse.Function = funcName;
            ApiResponse.Random = random;

            return null;
        }

        // 创建控制器。
        void Invoke(ApiApplication application)
        {
            var request = ApiRequest;
            var response = ApiResponse;
            var function = null as ApiFunction;
            var controller = null as ApiController;

            // Application 无效,尝试默认控制器和枚举。
            if (application == null)
            {
                var @default = Options.Default;
                if (@default == null)
                {
                    // 没有指定默认控制器,尝试枚举。
                    response.Error("Invalid Application");
                    if (Options.AllowEnumerate) response.Data = Enumerate(Entries.Enumerate(), Options);
                    return;
                }
                else
                {
                    // 创建默认控制器。
                    try { controller = CreateController(@default, request, response, Options); }
                    catch (Exception ex) { ApiUtility.Exception(response, ex.InnerException); }
                }
            }
            else
            {
                // 创建控制器时候会填充 Controller.Request 属性,可能导致 Request.Function 被篡改,所以在创建之前获取 Function。
                function = application.Get(request.Function);
                try { controller = CreateController(application.Type, request, response, Options); }
                catch (Exception ex) { ApiUtility.Exception(response, ex.InnerException); }
            }
            if (controller == null) response.Error("创建控制器实例失败。");
            else Invoke(controller, application, function, Options, request, response);
            RuntimeUtility.Dispose(controller);
        }

        // 调用 Function。
        static void Invoke(ApiController controller, ApiApplication application, ApiFunction function, ApiOptions options, ApiRequest request, ApiResponse response)
        {
            // 没有 ApiApplication,使用了 Options 中指定的默认控制器。
            if (application == null)
            {
                application = new ApiApplication();
                application.Independent = true;
                application.Hidden = true;
            }

            try
            {
                // 控制器初始化。
                var initializer = ApiUtility.GetInitialier(controller);
                var match = initializer == null ? true : initializer.Invoke(controller);
                if (!match) return;
                if (application.Independent) return;

                if (function != null)
                {
                    var result = function.Method.Invoke(controller, ReadParameters(request, function));
                    if (result == null || function.Returnable == null) return;
                    var returnable = function.Returnable;

                    // 已明确字符串类型,视为提示错误。
                    if (returnable.Equals(typeof(string)))
                    {
                        var error = result as string;
                        if (!string.IsNullOrEmpty(error)) response.Error(error);
                        return;
                    }

                    // 已明确 Exception 类型,视为提示错误。
                    if (result is Exception)
                    {
                        ApiUtility.Exception(response, result as Exception);
                        return;
                    }

                    // 已明确 Json 类型。
                    if (result is Json)
                    {
                        response.Data = result as Json;
                        return;
                    }

                    // 已明确 Model 类型。
                    if (result is ApiModel)
                    {
                        response.Model = result as ApiModel;
                        return;
                    }

                    // 类型未知,尝试 ToJson 方法。
                    var tojson = result as IToJson;
                    if (tojson != null)
                    {
                        response.Data = tojson.ToJson();
                        return;
                    }

                    // 类型未知,尝试 Record 模型。
                    var record = result as IRecord;
                    if (record != null)
                    {
                        response.Data = Json.From(record);
                        return;
                    }

                    // 未知类型,尝试 Json 类型。
                    var json = result as Json;
                    if (json != null)
                    {
                        response.Data = json;
                        return;
                    }

                    // 未知返回类型,无法明确输出格式,忽略。
                }
                else
                {
                    // 未匹配到 Function,尝试 Default。
                    var @default = ApiUtility.GetDefault(controller);
                    if (@default != null)
                    {
                        @default.Invoke(controller);
                        return;
                    }

                    // 没有执行任何 Function,尝试枚举。
                    if (application.Hidden)
                    {
                        response.Error("Invalid Application");
                    }
                    else
                    {
                        response.Error("Invalid Function");
                        if (options.AllowEnumerate) response.Data = Enumerate(application.Items, options);
                    }
                }
            }
            catch (Exception exception)
            {
                if (exception == null) response.Error("发生了未定义的异常。");
                else ApiUtility.Exception(response, exception);
            }
        }

        static ApiController CreateController(Type type, ApiRequest request, ApiResponse response, ApiOptions options)
        {
            var controller = (ApiController)Activator.CreateInstance(type);
            ApiUtility.SetProperties(controller, request, response, options);
            return controller;
        }

    }

}