import dataclasses
import os
import pathlib
import sys
from typing import List

import jinja2

from chaotic import cpp_format
from chaotic import jinja_env
from chaotic_openapi.back.cpp_client import types

PARENT_DIR = os.path.dirname(__file__)

TEMPLATE_NAMES = [
    'client.hpp',
    'client.cpp',
    'client_impl.hpp',
    'client_impl.cpp',
    'component.hpp',
    'component.cpp',
    'requests.hpp',
    'requests.cpp',
    'responses.hpp',
    'responses.cpp',
    'exceptions.hpp',
]

THIS_FILE_IS_AUTOGENERATED = "/* THIS FILE IS AUTOGENERATED, DON'T EDIT! */\n"


@dataclasses.dataclass
class Context:
    generate_path: pathlib.Path
    clang_format_bin: str


@dataclasses.dataclass
class CppOutput:
    rel_path: str
    content: str

    @staticmethod
    def save(outputs: List['CppOutput'], prefix: str) -> None:
        for output in outputs:
            path = os.path.join(prefix, output.rel_path)
            content = THIS_FILE_IS_AUTOGENERATED + output.content
            content = content.strip() + '\n'

            os.makedirs(os.path.dirname(path), exist_ok=True)
            with open(path, 'w') as ofile:
                ofile.write(content)


def cpp_comment(string: str) -> str:
    comment = string.replace('\r', '').strip().replace('\n', '\n/// ')
    if not comment:
        return ''

    # Not using .capitalize() to avoid lowercasing the second sentence.
    return '/// ' + comment[0].upper() + comment[1:]


def make_env() -> jinja2.Environment:
    env = jinja_env.make_env(
        'chaotic-openapi/chaotic_openapi/back/cpp_client',
        os.path.join(PARENT_DIR),
    )
    env.filters['cpp_comment'] = cpp_comment

    return env


JINJA_ENV = make_env()


def render(spec: types.ClientSpec, context: Context) -> List[CppOutput]:
    env = {
        'spec': spec,
        'namespace': spec.cpp_namespace,
        'name': spec.client_name,
        'base_url': 'http://example.com',  # TODO
        'operations': spec.operations,
    }

    output = []
    for name in TEMPLATE_NAMES:
        print(name, file=sys.stderr)
        tpl = JINJA_ENV.get_template(f'templates/{name}.jinja')
        pp = tpl.render(**env)
        pp = cpp_format.format_pp(pp, binary=context.clang_format_bin)

        if name.endswith('.hpp'):
            rel_path = f'include/client/{spec.client_name}/{name}'
        else:
            rel_path = f'src/client/{spec.client_name}/{name}'

        output.append(CppOutput(rel_path=rel_path, content=pp))
    return output
