#!/usr/bin/env python
# -*- coding: utf-8 -*-

""" License
    Copyright (C) 2013 YunoHost
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.
    You should have received a copy of the GNU Affero General Public License
    along with this program; if not, see http://www.gnu.org/licenses
"""

"""
    Generate JSON specification files API
"""
import os
import sys
import yaml
import json
import requests


def main():
    """ """
    with open("../share/actionsmap.yml") as f:
        action_map = yaml.safe_load(f)

    try:
        with open("/etc/yunohost/current_host", "r") as f:
            domain = f.readline().rstrip()
    except IOError:
        domain = requests.get("http://ip.yunohost.org").text
    with open("../debian/changelog") as f:
        top_changelog = f.readline()
    api_version = top_changelog[top_changelog.find("(") + 1 : top_changelog.find(")")]

    csrf = {
        "name": "X-Requested-With",
        "in": "header",
        "required": True,
        "schema": {"type": "string", "default": "Swagger API"},
    }

    resource_list = {
        "openapi": "3.0.3",
        "info": {
            "title": "YunoHost API",
            "description": "This is the YunoHost API used on all YunoHost instances. This API is essentially used by YunoHost Webadmin.",
            "version": api_version,
        },
        "servers": [
            {
                "url": "https://{domain}/yunohost/api",
                "variables": {
                    "domain": {
                        "default": "demo.yunohost.org",
                        "description": "Your yunohost domain",
                    }
                },
            }
        ],
        "tags": [{"name": "public", "description": "Public route"}],
        "paths": {
            "/login": {
                "post": {
                    "tags": ["public"],
                    "summary": "Logs in and returns the authentication cookie",
                    "parameters": [csrf],
                    "requestBody": {
                        "required": True,
                        "content": {
                            "multipart/form-data": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "credentials": {
                                            "type": "string",
                                            "format": "password",
                                        }
                                    },
                                    "required": ["credentials"],
                                }
                            }
                        },
                    },
                    "security": [],
                    "responses": {
                        "200": {
                            "description": "Successfully login",
                            "headers": {"Set-Cookie": {"schema": {"type": "string"}}},
                        }
                    },
                }
            },
            "/installed": {
                "get": {
                    "tags": ["public"],
                    "summary": "Test if the API is working",
                    "parameters": [],
                    "security": [],
                    "responses": {
                        "200": {
                            "description": "Successfully working",
                        }
                    },
                }
            },
        },
    }

    def convert_categories(categories, parent_category=""):
        for category, category_params in categories.items():
            if parent_category:
                category = f"{parent_category} {category}"
            if "subcategory_help" in category_params:
                category_params["category_help"] = category_params["subcategory_help"]

            if "category_help" not in category_params:
                category_params["category_help"] = ""
            resource_list["tags"].append(
                {"name": category, "description": category_params["category_help"]}
            )

            for action, action_params in category_params["actions"].items():
                if "action_help" not in action_params:
                    action_params["action_help"] = ""
                if "api" not in action_params:
                    continue
                if not isinstance(action_params["api"], list):
                    action_params["api"] = [action_params["api"]]

                for i, api in enumerate(action_params["api"]):
                    print(api)
                    method, path = api.split(" ")
                    method = method.lower()
                    key_param = ""
                    if "{" in path:
                        key_param = path[path.find("{") + 1 : path.find("}")]
                    resource_list["paths"].setdefault(path, {})

                    notes = ""

                    operationId = f"{category}_{action}"
                    if i > 0:
                        operationId += f"_{i}"
                    operation = {
                        "tags": [category],
                        "operationId": operationId,
                        "summary": action_params["action_help"],
                        "description": notes,
                        "responses": {"200": {"description": "successful operation"}},
                    }
                    if action_params.get("deprecated"):
                        operation["deprecated"] = True

                    operation["parameters"] = []
                    if method == "post":
                        operation["parameters"] = [csrf]

                    if "arguments" in action_params:
                        if method in ["put", "post", "patch"]:
                            operation["requestBody"] = {
                                "required": True,
                                "content": {
                                    "multipart/form-data": {
                                        "schema": {
                                            "type": "object",
                                            "properties": {},
                                            "required": [],
                                        }
                                    }
                                },
                            }
                        for arg_name, arg_params in action_params["arguments"].items():
                            if "help" not in arg_params:
                                arg_params["help"] = ""
                            param_type = "query"
                            allow_multiple = False
                            required = True
                            allowable_values = None
                            name = str(arg_name).replace("-", "_")
                            if name[0] == "_":
                                required = False
                                if "full" in arg_params:
                                    name = arg_params["full"][2:]
                                else:
                                    name = name[2:]
                                name = name.replace("-", "_")

                            if "choices" in arg_params:
                                allowable_values = arg_params["choices"]
                            _type = "string"
                            if "type" in arg_params:
                                types = {"open": "file", "int": "int"}
                                _type = types[arg_params["type"]]
                            if (
                                "action" in arg_params
                                and arg_params["action"] == "store_true"
                            ):
                                _type = "boolean"

                            if "nargs" in arg_params:
                                if arg_params["nargs"] == "*":
                                    allow_multiple = True
                                    required = False
                                    _type = "array"
                                if arg_params["nargs"] == "+":
                                    allow_multiple = True
                                    required = True
                                    _type = "array"
                                if arg_params["nargs"] == "?":
                                    allow_multiple = False
                                    required = False
                            else:
                                allow_multiple = False

                            if name == key_param:
                                param_type = "path"
                                required = True
                                allow_multiple = False

                            if method in ["put", "post", "patch"]:
                                schema = operation["requestBody"]["content"][
                                    "multipart/form-data"
                                ]["schema"]
                                schema["properties"][name] = {
                                    "type": _type,
                                    "description": arg_params["help"],
                                }
                                if required:
                                    schema["required"].append(name)
                                prop_schema = schema["properties"][name]
                            else:
                                parameters = {
                                    "name": name,
                                    "in": param_type,
                                    "description": arg_params["help"],
                                    "required": required,
                                    "schema": {
                                        "type": _type,
                                    },
                                    "explode": allow_multiple,
                                }
                                prop_schema = parameters["schema"]
                                operation["parameters"].append(parameters)

                            if allowable_values is not None:
                                prop_schema["enum"] = allowable_values
                            if "default" in arg_params:
                                prop_schema["default"] = arg_params["default"]
                            if arg_params.get("metavar") == "PASSWORD":
                                prop_schema["format"] = "password"
                            if arg_params.get("metavar") == "MAIL":
                                prop_schema["format"] = "mail"
                            # Those lines seems to slow swagger ui too much
                            # if 'pattern' in arg_params.get('extra', {}):
                            #    prop_schema['pattern'] = arg_params['extra']['pattern'][0]

                    resource_list["paths"][path][method.lower()] = operation

            # Includes subcategories
            if "subcategories" in category_params:
                convert_categories(category_params["subcategories"], category)

    del action_map["_global"]
    convert_categories(action_map)

    openapi_json = json.dumps(resource_list)
    # Save the OpenAPI json
    with open(os.getcwd() + "/openapi.json", "w") as f:
        f.write(openapi_json)

    openapi_js = f"var openapiJSON = {openapi_json}"
    with open(os.getcwd() + "/openapi.js", "w") as f:
        f.write(openapi_js)


if __name__ == "__main__":
    sys.exit(main())