from __future__ import annotations

import json
from dataclasses import dataclass
from pathlib import Path

from brimley.core.models import SqlFunction, TemplateFunction
from brimley.discovery.scanner import Scanner


@dataclass(frozen=True)
class BuildResult:
    output_file: Path
    sql_functions: int
    template_functions: int
    diagnostics_count: int


def _slug(name: str) -> str:
    return "".join(char if char.isalnum() or char == "_" else "_" for char in name)


def _format_kwargs(kwargs: dict[str, object]) -> str:
    parts: list[str] = []
    for key, value in kwargs.items():
        parts.append(f"{key}={repr(value)}")
    return ", ".join(parts)


def _function_kwargs_for_sql(func: SqlFunction) -> dict[str, object]:
    kwargs: dict[str, object] = {
        "name": func.name,
        "type": "sql_function",
        "content": func.sql_body,
        "connection": func.connection,
        "return_shape": func.return_shape,
    }

    if func.description:
        kwargs["description"] = func.description

    if func.arguments is not None:
        kwargs["arguments"] = func.arguments

    if func.mcp is not None and func.mcp.type == "tool":
        kwargs["mcpType"] = "tool"

    return kwargs


def _function_kwargs_for_template(func: TemplateFunction) -> dict[str, object]:
    kwargs: dict[str, object] = {
        "name": func.name,
        "type": "template_function",
        "content": func.template_body or "",
        "template_engine": func.template_engine,
        "return_shape": func.return_shape,
    }

    if func.description:
        kwargs["description"] = func.description

    if func.arguments is not None:
        kwargs["arguments"] = func.arguments

    if func.mcp is not None and func.mcp.type == "tool":
        kwargs["mcpType"] = "tool"

    return kwargs


def _emit_shim_function(index: int, decorator_kwargs: dict[str, object], source_type: str, source_name: str) -> str:
    safe_name = _slug(source_name)
    shim_name = f"_asset_{source_type}_{index}_{safe_name}"
    kwargs_str = _format_kwargs(decorator_kwargs)

    return (
        f"@function({kwargs_str})\n"
        f"def {shim_name}():\n"
        f"    return None\n"
    )


def _render_assets_module(sql_functions: list[SqlFunction], template_functions: list[TemplateFunction]) -> str:
    lines: list[str] = [
        "# Auto-generated by `brimley build`. Do not edit manually.",
        "from brimley import function",
        "",
    ]

    index = 0
    for func in sql_functions:
        index += 1
        lines.append(_emit_shim_function(index, _function_kwargs_for_sql(func), "sql", func.name))
        lines.append("")

    for func in template_functions:
        index += 1
        lines.append(_emit_shim_function(index, _function_kwargs_for_template(func), "template", func.name))
        lines.append("")

    if index == 0:
        lines.append("# No SQL or template assets discovered.")
        lines.append("")

    return "\n".join(lines).rstrip() + "\n"


def compile_assets(root_dir: Path, output_file: Path | None = None) -> BuildResult:
    resolved_root = root_dir.expanduser().resolve()
    target_file = output_file.expanduser().resolve() if output_file else resolved_root / "brimley_assets.py"

    scan_result = Scanner(resolved_root).scan()

    sql_functions = [func for func in scan_result.functions if isinstance(func, SqlFunction)]
    template_functions = [func for func in scan_result.functions if isinstance(func, TemplateFunction)]

    target_file.parent.mkdir(parents=True, exist_ok=True)
    target_file.write_text(_render_assets_module(sql_functions, template_functions), encoding="utf-8")

    return BuildResult(
        output_file=target_file,
        sql_functions=len(sql_functions),
        template_functions=len(template_functions),
        diagnostics_count=len(scan_result.diagnostics),
    )
