from __future__ import annotations

from dataclasses import dataclass
from typing import Any


def normalize_type(value: str) -> str:
    return " ".join(value.replace("\t", " ").split())


def render_params(parameters: list[dict[str, Any]]) -> str:
    if not parameters:
        return "void"
    parts: list[str] = []
    for index, param in enumerate(parameters):
        name = str(param.get("name") or f"arg{index}")
        c_type = normalize_type(str(param.get("c_type") or "void"))
        if c_type == "...":
            parts.append("...")
            continue
        parts.append(f"{c_type} {name}")
    return ", ".join(parts)


def render_arg_list(parameters: list[dict[str, Any]]) -> str:
    if not parameters:
        return ""
    names: list[str] = []
    for index, param in enumerate(parameters):
        c_type = str(param.get("c_type") or "void")
        if c_type == "...":
            continue
        name = str(param.get("name") or f"arg{index}")
        names.append(name)
    return ", ".join(names)


@dataclass(frozen=True)
class NativeExportRenderOptions:
    header_include: str
    impl_header_include: str
    api_macro: str
    call_macro: str
    impl_prefix: str
    symbol_prefix: str


def build_impl_name(function_name: str, options: NativeExportRenderOptions) -> str:
    if options.symbol_prefix and function_name.startswith(options.symbol_prefix):
        return options.impl_prefix + function_name[len(options.symbol_prefix):]
    return options.impl_prefix + function_name


def render_impl_header(functions: list[dict[str, Any]], options: NativeExportRenderOptions) -> str:
    lines: list[str] = []
    lines.append("#pragma once")
    lines.append("")
    lines.append(f"#include \"{options.header_include}\"")
    lines.append("")
    lines.append("#ifdef __cplusplus")
    lines.append("extern \"C\" {")
    lines.append("#endif")
    lines.append("")
    for item in functions:
        name = str(item.get("name") or "")
        if not name:
            continue
        return_type = normalize_type(str(item.get("c_return_type") or "void"))
        parameters = item.get("parameters")
        if not isinstance(parameters, list):
            parameters = []
        signature = render_params(parameters)
        impl_name = build_impl_name(name, options)
        lines.append(f"{return_type} {options.call_macro} {impl_name}({signature});")
    lines.append("")
    lines.append("#ifdef __cplusplus")
    lines.append("}")
    lines.append("#endif")
    lines.append("")
    return "\n".join(lines)


def render_exports(
    functions: list[dict[str, Any]],
    options: NativeExportRenderOptions,
    generator_path: str,
) -> str:
    lines: list[str] = []
    lines.append("// <auto-generated />")
    lines.append(f"// Generated by {generator_path}")
    lines.append(f"#include \"{options.header_include}\"")
    lines.append(f"#include \"{options.impl_header_include}\"")
    lines.append("")
    lines.append("#ifdef __cplusplus")
    lines.append("extern \"C\" {")
    lines.append("#endif")
    lines.append("")
    for item in functions:
        name = str(item.get("name") or "")
        if not name:
            continue
        return_type = normalize_type(str(item.get("c_return_type") or "void"))
        parameters = item.get("parameters")
        if not isinstance(parameters, list):
            parameters = []
        signature = render_params(parameters)
        args = render_arg_list(parameters)
        impl_name = build_impl_name(name, options)
        lines.append(f"{options.api_macro} {return_type} {options.call_macro} {name}({signature}) {{")
        if return_type == "void":
            if args:
                lines.append(f"    {impl_name}({args});")
            else:
                lines.append(f"    {impl_name}();")
            lines.append("}")
        else:
            if args:
                lines.append(f"    return {impl_name}({args});")
            else:
                lines.append(f"    return {impl_name}();")
            lines.append("}")
        lines.append("")
    lines.append("#ifdef __cplusplus")
    lines.append("}")
    lines.append("#endif")
    lines.append("")
    return "\n".join(lines)
