"""
script for generating files that involve repetitive updates for zmq constants.

Run as `python3 buildutils/constants.py`

Run this after updating utils/constant_names

Currently generates the following files from templates:

- constant_enums.pxi
- constants.pyi
"""

# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.

import enum
import os
import sys
from subprocess import run

pjoin = os.path.join

buildutils = os.path.abspath(os.path.dirname(__file__))
root = pjoin(buildutils, os.path.pardir)

sys.path.insert(0, pjoin(root, 'zmq'))
import constants  # noqa: E402

all_names = []
for name in constants.__all__:
    item = getattr(constants, name)
    if isinstance(item, enum.Enum):
        all_names.append(name)

ifndef_t = """#ifndef {0}
    #define {0} (_PYZMQ_UNDEFINED)
#endif
"""


def no_prefix(name):
    """does the given constant have a ZMQ_ prefix?"""
    return name.startswith('E') and not name.startswith('EVENT')


def cython_enums():
    """generate `enum: ZMQ_CONST` block for constant_enums.pxi"""
    lines = []
    for name in all_names:
        if no_prefix(name):
            lines.append(f'enum: ZMQ_{name} "{name}"')
        else:
            lines.append(f'enum: ZMQ_{name}')

    return dict(ZMQ_ENUMS='\n    '.join(lines))


def ifndefs():
    """generate `#ifndef ZMQ_CONST` block for zmq_constants.h"""
    lines = ['#define _PYZMQ_UNDEFINED (-9999)']
    for name in all_names:
        if not no_prefix(name):
            name = f'ZMQ_{name}'
        lines.append(ifndef_t.format(name))
    return dict(ZMQ_IFNDEFS='\n'.join(lines))


def promoted_constants():
    """Generate CONST: int for mypy"""
    original_lines = []
    with open(constants.__file__) as f:
        for line in f.readlines():
            original_lines.append(line)
            if "AUTOGENERATED_BELOW_HERE" in line:
                original_file = "".join(original_lines)
                break
        else:
            raise ValueError("Never encountered AUTOGENERATED_BELOW_HERE")

    global_assignments = []
    all_lines = ["__all__: list[str] = ["]
    for cls_name in sorted(dir(constants)):
        if cls_name.startswith("_"):
            continue
        cls = getattr(constants, cls_name)
        if not isinstance(cls, type) or not issubclass(cls, enum.Enum):
            continue

        get_global_name = getattr(cls, "_global_name", lambda name: name)
        all_lines.append(f'    "{cls_name}",')
        for key in cls.__members__:
            global_name = get_global_name(key)
            all_lines.append(f'    "{global_name}",')
            global_assignments.append(f"{global_name}: int = {cls_name}.{key}")
    all_lines.append("]")

    return dict(
        original_file=original_file,
        global_assignments="\n".join(global_assignments),
        __all__="\n".join(all_lines),
    )


def generate_file(fname, ns_func, dest_dir="."):
    """generate a constants file from its template"""
    with open(pjoin(root, 'buildutils', 'templates', f'{fname}')) as f:
        tpl = f.read()
    out = tpl.format(**ns_func())
    dest = pjoin(dest_dir, fname)
    print(f"generating {dest} from template")
    with open(dest, 'w') as f:
        f.write(out)
    if fname.endswith(".py"):
        run(["ruff", "format", dest])


def render_constants():
    """render generated constant files from templates"""
    generate_file(
        "constant_enums.pxi", cython_enums, pjoin(root, 'zmq', 'backend', 'cython')
    )
    generate_file("constants.py", promoted_constants, pjoin(root, 'zmq'))


if __name__ == '__main__':
    render_constants()
