using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Text;

namespace Blahblah.FlowerStory.Server.Controller;

/// <inheritdoc/>
[Route("")]
public class SwaggerController : ControllerBase
{
    private readonly SwaggerGenerator generator;

    /// <inheritdoc/>
    public SwaggerController(SwaggerGenerator generator)
    {
        this.generator = generator;
    }

    /// <inheritdoc/>
    [Route("")]
    [Produces("text/html")]
    [HttpGet]
    public ActionResult Get()
    {
        return Content($@"<!DOCTYPE html>
<html>
  <head>
    <title>Redoc</title>
    <!-- needed for adaptive design -->
    <meta charset=""utf-8""/>
    <meta name=""viewport"" content=""width=device-width, initial-scale=1"">

    <!--
    Redoc doesn't change outer page styles
    -->
    <style>
      body {{
        margin: 0;
        padding: 0;
      }}
    </style>
  </head>
  <body>
    <redoc spec-url=""/swagger/{Program.Version}/swagger.json""></redoc>
    <script src=""/js/redoc.standalone.js""> </script>
  </body>
</html>", "text/html");
    }

    /// <inheritdoc/>
    [Route("apidoc/{version}")]
    [Produces("text/html")]
    [HttpGet]
    [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
    public ActionResult GetApi(string version)
    {
        var model = generator.GetSwagger(version);
        var builder = new StringBuilder();
        builder.Append($@"<!DOCTYPE html>
<html lang=""zh-cn"">
<head>
    <meta charset=""UTF-8"">
    <title>Flower Story - API 接口文档</title>
    <style type=""text/css"">
        body {{ background-color: rgb(243,244,246) }}
        pre {{
            padding: 1rem;
            background-color: rgb(107 114 128);
            color: #fff;
        }}
        table,
        table td,
        table th {{
            margin: 0;
            border: 1px solid #e5e7eb;
            border-collapse: collapse;
        }}
        table {{
            table-layout: fixed;
            word-break: break-all;
        }}
        tr {{
            height: 20px;
            font-size: 12px;
        }}
        td {{ padding: .75rem }}

        .red {{ color: #f00 }}
        .box {{
            background-color: #fff;
            box-shadow: rgba(0, 0, 0, 0) 0px 0px 0px 0px, rgba(0, 0, 0, 0) 0px 0px 0px 0px, rgba(0, 0, 0, 0.04) 0px 4px 6px -1px;
            padding: .75rem;
            border-radius: .375rem;
            margin-bottom: 2.5rem;
        }}
        .method-get {{ color: #61affe }}
        .method-delete {{ color: #f93e3e }}
        .method-post {{ color: #49cc90 }}
        .method-put {{ color: #fca130 }}
        .header {{ color: rbg(107 114 128) }}
        .navigator {{
            position: fixed;
            width: 400px;
            height: 100%;
            top: 0;
            left: 0;
            font-size: .875rem;
            background-color: #fff;
        }}
        .navigator ul {{
            list-style: none;
            padding-left: 20px;
            line-height: 24px;
        }}
        .navigator ol {{
            list-style: none;
            padding-left: 30px;
        }}
        .navigator a, .navigator a:visited {{
            text-decoration: none;
            color: #333;
        }}
        .navigator a:hover {{
            color: #000;
            text-decoration: underline;
        }}
        .wrapper {{ margin-left: 400px }}
        .operation {{ width: 100% }}
        .bg {{ background-color: rgb(84, 127, 177) }}
    </style>
</head>
<body>
    <div class=""navigator"">
        <ul>");
        foreach (var tag in model.Tags)
        {
            if (tag == null)
            {
                continue;
            }
            builder.Append($@"
            <li>
                <a href=""#tag-{tag.Name}"">{tag.Description}</a>
                <ol>");
            foreach (var item in model.Paths)
            {
                if (item.Value.Operations != null)
                {
                    foreach (var operation in item.Value.Operations)
                    {
                        if (!operation.Value.Tags.Any(t => t.Name == tag.Name))
                        {
                            continue;
                        }
                        builder.Append($@"
                    <li><a href=""#op-{operation.Value.OperationId}"">{operation.Value.Summary}</a></li>");
                    }
                }
            }
            builder.Append($@"
                </ol>
            </li>");
        }
        builder.Append($@"
        </ul>
    </div>
    <div class=""wrapper"">
        <h1>{model.Info.Title}</h1>
        <h3>接口文档 {model.Info.Version}</h3>
        <div class=""box"">
            <p>{model.Info.Description}</p>
        </div>");
        foreach (var tag in model.Tags)
        {
            if (tag == null)
            {
                continue;
            }
            builder.Append($@"
        <h3 id=""tag-{tag.Name}"">{tag.Description}</h3>
        <div class=""box"">");
            foreach (var item in model.Paths)
            {
                if (item.Value.Operations != null)
                {
                    foreach (var operation in item.Value.Operations)
                    {
                        if (!operation.Value.Tags.Any(t => t.Name == tag.Name))
                        {
                            continue;
                        }
                        var method = operation.Key.ToString();
                        builder.Append($@"
            <h4 id=""op-{operation.Value.OperationId}"">{operation.Value.Summary}</h4>
            <pre><span class=""method-{method.ToLower()}"">{method.ToUpper()}</span> {item.Key}</pre>");
                        if (operation.Value.Parameters?.Count > 0)
                        {
                            builder.Append(@"
            <p>请求参数</p>
            <table class=""operation"">
                <tr class=""header""><td>参数</td><td>位置</td><td>类型</td><td>说明</td></tr>");
                            foreach (var param in operation.Value.Parameters)
                            {
                                var required = param.Required ? "<span class=\"red\">*</span>" : string.Empty;
                                builder.Append($@"
                <tr>
                    <td>{required}{param.Name}</td>
                    <td>{param.In}</td>
                    <td>{param.Schema.Type}</td>
                    <td>{param.Description}</td>
                </tr>");
                            }
                            builder.Append(@"
            </table>");
                        }
                        if (operation.Value.RequestBody?.Content != null)
                        {
                            foreach (var content in operation.Value.RequestBody.Content)
                            {
                                builder.Append($@"
            <p>请求实体 ({content.Key})</p>
            <table class=""operation"">
                <tr class=""header""><td>字段</td><td>类型</td><td>说明</td></tr>");
                                if (content.Value.Schema.Type == "array")
                                {
                                    builder.Append($@"
                <tr>
                    <td>.</td>
                    <td>{content.Value.Schema.Items.Type}[]</td>
                    <td>{operation.Value.RequestBody.Description}</td>
                </tr>");
                                }
                                else
                                {
                                    IDictionary<string, OpenApiSchema> properties;
                                    if (content.Value.Schema.Reference != null && model.Components.Schemas.TryGetValue(content.Value.Schema.Reference.Id, out var schema))
                                    {
                                        properties = schema.Properties;
                                    }
                                    else
                                    {
                                        properties = content.Value.Schema.Properties;
                                    }
                                    foreach (var prop in properties)
                                    {
                                        var required = content.Value.Schema.Required.Contains(prop.Key) ? "<span class=\"red\">*</span>" : string.Empty;
                                        var type = prop.Value.Type;
                                        if (type == "string" && prop.Value.Format == "binary")
                                        {
                                            type = "file";
                                        }
                                        else if (type == "array")
                                        {
                                            type = prop.Value.Items.Type;
                                            if (type == "string" && prop.Value.Items.Format == "binary")
                                            {
                                                type = "file";
                                            }
                                            type += "[]";
                                        }
                                        builder.Append($@"
                <tr>
                    <td>{required}{prop.Key}</td>
                    <td>{type}</td>
                    <td>{prop.Value.Description}</td>
                </tr>");
                                    }
                                }
                                builder.Append(@"
            </table>");
                            }
                        }
                        if (operation.Value.Responses?.Count > 0)
                        {
                            builder.Append(@"
            <p>响应 HTTP 状态</p>
            <table class=""operation"">
                <tr class=""header""><td>状态码</td><td>说明</td></tr>");
                            foreach (var response in operation.Value.Responses)
                            {
                                builder.Append($@"
                <tr>
                    <td>{response.Key}</td>
                    <td>{response.Value.Description}</td>
                </tr>");
                                if (response.Key == "200")
                                {
                                    foreach (var content in response.Value.Content)
                                    {
                                        builder.Append($@"
                <tr>
                    <td colspan=""2"">
                        <p>{content.Key}</p>
                        <table class=""operation"">
                            <tr><td>字段</td><td>类型</td><td>说明</td></tr>");
                                        OpenApiSchema schema;
                                        if (content.Value.Schema.Type == "array")
                                        {
                                            schema = content.Value.Schema.Items;
                                            builder.Append($@"
                            <tr><td>.</td><td>{schema.Reference?.Id ?? schema.Type}[]</td><td></td></tr>");
                                        }
                                        else
                                        {
                                            schema = content.Value.Schema;
                                        }
                                        if (schema.Reference != null && model.Components.Schemas.TryGetValue(schema.Reference.Id, out var s))
                                        {
                                            schema = s;
                                        }
                                        if (schema.Properties.Count > 0)
                                        {
                                            foreach (var p in schema.Properties)
                                            {
                                                bool isArray = p.Value.Type == "array";
                                                string type = isArray ? $"{p.Value.Items.Reference?.Id ?? p.Value.Items.Type}[]" : p.Value.Type;
                                                builder.Append($@"
                            <tr>
                                <td>{p.Key}</td>
                                <td>{type}</td>
                                <td>{p.Value.Description}</td>
                            </tr>");
                                                if (isArray && p.Value.Items.Reference != null && model.Components.Schemas.TryGetValue(p.Value.Items.Reference.Id, out s))
                                                {
                                                    foreach (var p2 in s.Properties)
                                                    {
                                                        builder.Append($@"
                            <tr>
                                <td>&nbsp;&nbsp;{p2.Key}</td>
                                <td>{p2.Value.Type}</td>
                                <td>{p2.Value.Description}</td>
                            </tr>");
                                                    }
                                                }
                                            }
                                        }
                                        else
                                        {
                                            builder.Append($@"
                            <tr>
                                <td></td>
                                <td>{schema.Type}</td>
                                <td></td>
                            </tr>");
                                        }
                                        builder.Append(@"
                        </table>
                    </td>
                </tr>");
                                    }
                                }
                            }
                            builder.Append(@"
            </table>");
                        }
                    }
                }
            }
            builder.Append(@"
        </div>");
        }
        builder.Append(@"
    </div>
</body>
</html>");
        return Content(builder.ToString(), "text/html");
    }
}
