#!/usr/bin/env python
import json
import re
import sys
from pathlib import Path

map_schema_type_to_python = {
    "object": "Dict",
    "array": "List",
    "integer": "int",
    "string": "str",
    "number": "int",
    "boolean": "bool",
    "any": "Any",
}


def create_dataclass(name):
    return dataclass(name)


def create_attribute(name, type, required):
    name = re.sub("([a-z0-9])([A-Z])", r"\1_\2", name).lower()

    type = map_schema_type_to_python[type]

    return attribute(name, type, required)


class dataclass:
    def __init__(self, name):
        self.name = name
        self.attrs = []

    def add_attr(self, attr):
        self.attrs.append(attr)

    def __str__(self):
        output = f"@dataclass\nclass {self.name}Payload:\n"

        if len(self.attrs) == 0:
            return output + "    pass\n"

        optional_attrs = ""
        for attr in self.attrs:
            if attr.required:
                output += str(attr)

            else:
                optional_attrs += str(attr)

        return output + optional_attrs


class attribute:
    def __init__(self, name, type, required):
        self.name = name
        self.type = type
        self.required = required

    def __str__(self):
        name = self.name
        if not re.match("^[a-zA-Z_]", self.name):
            name = "_" + self.name

        definition = f"    {name}: {self.type}"
        if self.required is True:
            definition += "\n"
        else:
            definition += " = None\n"

        return definition

    def __repr__(self):
        return f"<{self.name}, {self.type}, {self.required}> "


calls = []
call_results = []


def parse_schema(schema):
    with open(schema, "r") as f:
        schema = json.loads(f.read())

    name = schema["$id"].split(":")[-1]

    call = False
    call_result = False
    if name.endswith("Request"):
        call = True
        name = name[: -len("Request")]
    elif name.endswith("Response"):
        call_result = True
        name = name[: -len("Response")]

    dc = create_dataclass(name)
    try:
        properties = schema["properties"]
    except KeyError:
        if call:
            calls.append(dc)
        elif call_result:
            call_results.append(dc)
        return

    for property, definition in properties.items():
        if property == "customData":
            continue
        required = True
        try:
            required = property in schema["required"]
        except KeyError:
            required = False

        try:
            type = definition["type"]
        except KeyError:
            try:
                ref = definition["$ref"].split("/")[-1]
                type = schema["definitions"][ref]["type"]
            except KeyError:
                if definition == {}:
                    type = "any"

        attr = create_attribute(property, type, required)
        dc.add_attr(attr)

    if call:
        calls.append(dc)
    elif call_result:
        call_results.append(dc)


if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Pass path to folder with schemas")
        sys.exit(-1)

    p = Path(sys.argv[1])
    schemas = list(p.glob("*.json"))

    for schema in schemas:
        parse_schema(schema)

    with open("call.py", "wb+") as f:
        f.write(b"from typing import Any, Dict, List\n")
        f.write(b"from dataclasses import dataclass, field, Optional\n")

        for call in sorted(calls, key=lambda call: call.name):
            f.write(b"\n\n")
            f.write(str(call).encode("utf-8"))

    with open("call_result.py", "wb+") as f:
        f.write(b"from typing import Any, Dict, List\n")
        f.write(b"from dataclasses import dataclass, field\n")

        for call in sorted(call_results, key=lambda call: call.name):
            f.write(b"\n\n")
            f.write(str(call).encode("utf-8"))
