import re
from enum import Enum
from itertools import groupby
from pathlib import Path

import slugify
from fastapi import FastAPI
from humps import camelize
from jinja2 import Template
from mealie.app import app
from pydantic import BaseModel

CWD = Path(__file__).parent
OUT_DIR = CWD / "output"
OUT_FILE = OUT_DIR / "app_routes.py"

JS_DIR = OUT_DIR / "javascriptAPI"
JS_OUT_FILE = JS_DIR / "apiRoutes.js"
TEMPLATES_DIR = CWD / "templates"

PYTEST_TEMPLATE = TEMPLATES_DIR / "pytest_routes.j2"
JS_REQUESTS = TEMPLATES_DIR / "js_requests.j2"
JS_ROUTES = TEMPLATES_DIR / "js_routes.j2"
JS_INDEX = TEMPLATES_DIR / "js_index.j2"

JS_DIR.mkdir(exist_ok=True, parents=True)


class RouteObject:
    def __init__(self, route_string) -> None:
        self.prefix = "/" + route_string.split("/")[1]
        self.route = route_string.replace(self.prefix, "")
        self.js_route = self.route.replace("{", "${")
        self.parts = route_string.split("/")[1:]
        self.var = re.findall(r"\{(.*?)\}", route_string)
        self.is_function = "{" in self.route
        self.router_slug = slugify.slugify("_".join(self.parts[1:]), separator="_")
        self.router_camel = camelize(self.router_slug)

    def __repr__(self) -> str:
        return f"""Route: {self.route}
Parts: {self.parts}
Function: {self.is_function}
Var: {self.var}
Slug: {self.router_slug}
"""


class RequestType(str, Enum):
    get = "get"
    put = "put"
    post = "post"
    patch = "patch"
    delete = "delete"


class HTTPRequest(BaseModel):
    request_type: RequestType
    description: str = ""
    summary: str
    tags: list[str]

    @property
    def summary_camel(self):
        return camelize(self.summary)

    @property
    def js_docs(self):
        return self.description.replace("\n", "  \n  * ")


class PathObject(BaseModel):
    route_object: RouteObject
    http_verbs: list[HTTPRequest]

    class Config:
        arbitrary_types_allowed = True


def get_path_objects(app: FastAPI):
    paths = []

    for key, value in app.openapi().items():
        if key == "paths":
            for key, value in value.items():

                paths.append(
                    PathObject(
                        route_object=RouteObject(key),
                        http_verbs=[HTTPRequest(request_type=k, **v) for k, v in value.items()],
                    )
                )

    return paths


def read_template(file: Path):
    with open(file, "r") as f:
        return f.read()


def generate_template(app):
    paths = get_path_objects(app)

    static_paths = [x.route_object for x in paths if not x.route_object.is_function]
    function_paths = [x.route_object for x in paths if x.route_object.is_function]

    static_paths.sort(key=lambda x: x.router_slug)
    function_paths.sort(key=lambda x: x.router_slug)

    template = Template(read_template(PYTEST_TEMPLATE))
    content = template.render(paths={"prefix": "/api", "static_paths": static_paths, "function_paths": function_paths})
    with open(OUT_FILE, "w") as f:
        f.write(content)

    template = Template(read_template(JS_ROUTES))
    content = template.render(
        paths={"prefix": "/api", "static_paths": static_paths, "function_paths": function_paths, "all_paths": paths}
    )
    with open(JS_OUT_FILE, "w") as f:
        f.write(content)

    all_tags = []
    for k, g in groupby(paths, lambda x: x.http_verbs[0].tags[0]):
        template = Template(read_template(JS_REQUESTS))
        content = template.render(paths={"all_paths": list(g), "export_name": camelize(k)})

        all_tags.append(camelize(k))

        with open(JS_DIR.joinpath(camelize(k) + ".js"), "w") as f:
            f.write(content)

    template = Template(read_template(JS_INDEX))
    content = template.render(files={"files": all_tags})

    with open(JS_DIR.joinpath("index.js"), "w") as f:
        f.write(content)


if __name__ == "__main__":
    generate_template(app)
