using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using VCommon.Json;
using VCommon.Reflection;
using VCommon.VApplication.Authorization;

namespace VCommon.VOpenApi.Docgen
{
    public static class DocGenerator
    {
        public static string Generate(string name, string version, string basePath, IEnumerable<ApiBind> binds)
        {
            var doc = new SwaggerDocument
            {
                info = new Info
                {
                    title = name,
                    version = version,
                },
                basePath = basePath,
                schemes = new[] { "http" },
                host = "",
                paths = new Dictionary<string, PathItem>(),
            };

            var modelsToGenerateDefine = new HashSet<Type>();

            //生成paths,并收集入参/返参模型

            foreach (var bind in binds)
            {
                var hasReturnModel = bind.ApiMethod.ReturnType != typeof(void);

                if (hasReturnModel) modelsToGenerateDefine.Add(bind.ApiMethod.ReturnType);

                var docEntry = doc.paths[bind.RawRoute] = new PathItem();

                docEntry.post = new Operation
                {
                    tags = new List<string> { bind.ResourceName },
                    parameters = new List<Parameter>(),
                    responses = new Dictionary<string, Response>
                    {
                        {
                            "200", new Response
                            {
                                description = "OK",
                                schema = hasReturnModel
                                    ? new Schema {@ref = "#/definitions/" + bind.ApiMethod.ReturnType.GetFriendlyTypeName()}
                                    : null
                            }
                        }
                        ,
                        {
                            "400",new Response
                            {
                                description ="Argument error"
                            }
                        }
                    }
                };

                var authAttrOnType = bind.ApiMethod.DeclaringType.GetCustomAttributeIncludeInterface<VServiceAuthorizeAttribute>();
                if (0 != authAttrOnType.Length || bind.ApiMethod.IsDefined(typeof(VServiceAuthorizeAttribute), true))
                {
                    docEntry.post.responses.Add("401", new Response { description = "Authorization required" });
                    docEntry.post.responses.Add("403", new Response { description = "Access denied" });
                    docEntry.post.parameters.Add(new Parameter
                    {
                        @in = "header",
                        name = "Authorization",
                        schema = new Schema { type = "string" }
                    });

                    string[] clsPermission = null;
                    string[] metPermission = null;

                    if (0 != authAttrOnType.Length) clsPermission = authAttrOnType.SelectMany(p => p.AnyPermissionsRequired).ToArray();
                    if (bind.ApiMethod.IsDefined(typeof(VServiceAuthorizeAttribute), true)) metPermission = bind.ApiMethod.GetCustomAttributes<VServiceAuthorizeAttribute>().SelectMany(p => p.AnyPermissionsRequired).ToArray();

                    var sb = new StringBuilder();

                    if (0 < clsPermission?.Length || 0 < metPermission?.Length)
                    {
                        sb.AppendLine("Permission required:");
                        if (0 < clsPermission?.Length && 0 < metPermission?.Length)
                        {
                            sb.AppendLine("A:" + string.Join(",", clsPermission));
                            sb.AppendLine("B:" + string.Join(",", metPermission));
                        }
                        else
                        {
                            if (0 < clsPermission?.Length) sb.AppendLine(string.Join(",", clsPermission));
                            if (0 < metPermission?.Length) sb.AppendLine(string.Join(",", metPermission));
                        }
                    }

                    docEntry.post.description = sb.ToString();
                }

                var inputParamType = bind.InputParamType;
                if (null != inputParamType)
                {
                    modelsToGenerateDefine.Add(inputParamType);
                    docEntry.post.parameters.Add(new Parameter
                    {
                        name = "input",
                        @in = "body",
                        required = true,
                        schema = new Schema
                        {
                            @ref = "#/definitions/" + inputParamType.GetFriendlyTypeName()
                        }
                    });
                }
            }

            //生成模型定义

            doc.definitions = SchemaCrawler.Crawl(modelsToGenerateDefine);

            return VJsonSerializer.Serialize(doc);
        }
    }
}