import keyword
import re
from pathlib import Path
from typing import Optional

from tarina.lang import lang
from tarina.lang.schema import _Subtypes, _TemplateDict, get_template
from tarina.tools import pascal_case


class LangItem:
    def __init__(self, scope: str, type: str):
        self.scope = scope
        self.type = type

    def set(self, content: str, locale: Optional[str] = None):
        lang.set(self.scope, self.type, content, locale)

    def __call__(self, locale: Optional[str] = None, **format_kwargs):
        return lang.require(self.scope, self.type, locale).format(**format_kwargs)

    def __iter__(self):
        return iter([self.scope, self.type])


class LangModel:
    pass


MODEL_TEMPLATE = """\
# This file is @generated by tarina.lang CLI tool
# It is not intended for manual editing.

from tarina.lang.model import LangItem, LangModel


{scope_classes}class Lang(LangModel):
{scopes}\
"""

SCOPE_TEMPLATE = """\
class {scope}:
{types}

"""

TYPE_TEMPLATE = """\
    {name}: LangItem = LangItem("{scope}", "{type}")
"""


def generate_model(root: Path):
    template = get_template(root)
    if "scopes" not in template:
        raise KeyError("Template file must have a 'scopes' key")
    scopes: list[_TemplateDict] = template["scopes"]
    scopes_classes = ""
    scopes_str = ""
    for s in scopes:
        scope = re.sub(r"[\W\s]+", "_", s["scope"])
        if scope in keyword.kwlist:
            scope += "_"
        scope_class = pascal_case(scope)
        if scope_class == "Lang":
            scope_class += "_"

        def visit_types(_scope: str, _types: "list[str | _Subtypes]", prefix: str = ""):

            types_str = ""
            for t in _types:
                if isinstance(t, dict):
                    subtype = re.sub(r"[\W\s]+", "_", t["subtype"])
                    if subtype in keyword.kwlist:
                        subtype += "_"
                    yield from visit_types(
                        f"{_scope}{pascal_case(subtype)}", t["types"], prefix=f"{prefix}{t['subtype']}."
                    )
                    types_str += f"    {subtype} = {_scope}{pascal_case(subtype)}\n"
                else:
                    name = re.sub(r"[\W\s]+", "_", t)
                    if name in keyword.kwlist:
                        name += "_"
                    types_str += TYPE_TEMPLATE.format(name=name, scope=s["scope"], type=f"{prefix}{t}")
            yield SCOPE_TEMPLATE.format(scope=_scope, types=types_str)

        scopes_classes += "\n".join(visit_types(scope_class, s["types"]))
        scopes_str += f"    {scope} = {scope_class}\n"
    return MODEL_TEMPLATE.format(scope_classes=scopes_classes, scopes=scopes_str)


def write_model(root: Path):
    model = generate_model(root)
    with (root / "model.py").open("w", encoding="utf-8") as f:
        f.write(model)
