"""scaffold_extension — generate a standalone ACE extension package.

Copies the shipped reference extension (``extensions/reference/``) and
rewrites identifiers, so the template can NEVER drift from the worked
example: if the reference changes, the scaffold changes with it.

Usage::

    python -m scripts.scaffold_extension my_domain [--dir /path/to/projects]

Produces ``ace-ext-my-domain/`` containing a ``my_domain_extension`` package
whose ``MyDomainExtension`` registers via the ``ace.extensions`` entry point.
"""

from __future__ import annotations

import argparse
import re
import shutil
from pathlib import Path

REFERENCE = Path(__file__).resolve().parents[1] / "extensions" / "reference"

_NAME_RE = re.compile(r"[a-z][a-z0-9_]*\Z")

_PYPROJECT = """[build-system]
requires = ["setuptools>=69"]
build-backend = "setuptools.build_meta"

[project]
name = "ace-ext-{dash}"
version = "0.1.0"
description = "{name} extension for ACE"
requires-python = ">=3.12"

[tool.setuptools.packages.find]
include = ["{pkg}*"]

[project.entry-points."ace.extensions"]
{name} = "{pkg}.extension:{cls}"
"""

_README = """# ace-ext-{dash}

An ACE extension — a new arm for the octopus.

Generated by ``scaffold_extension`` from ACE's reference extension. Every
extension point is a worked example: edit ``{pkg}/extension.py`` and follow
``docs/build-your-first-extension.md`` in the ACE repo
(github.com/augmented-cognition-engine/core).

Install (``pip install -e .``) and ACE discovers it via the
``ace.extensions`` entry point. For unpackaged development:
``ACE_EXTENSIONS="{pkg}.extension:{cls}"``.

API stability: see ``docs/extension-api.md`` in the ACE repo.
"""


def _camel(name: str) -> str:
    return "".join(part.capitalize() for part in name.split("_"))


def scaffold(name: str, target_root: Path) -> Path:
    """Generate ``ace-ext-<name>/`` under ``target_root``; return its path."""
    if not _NAME_RE.match(name):
        raise SystemExit(f"extension name must be snake_case: {name!r}")
    pkg = f"{name}_extension"
    cls = f"{_camel(name)}Extension"
    dash = name.replace("_", "-")
    root = Path(target_root) / f"ace-ext-{dash}"
    if root.exists():
        raise SystemExit(f"target already exists: {root}")
    pkg_dir = root / pkg
    shutil.copytree(
        REFERENCE,
        pkg_dir,
        ignore=shutil.ignore_patterns("__pycache__", "grounded_state_adapter.py"),
    )
    # Rename the MCP tool module with the extension: the kernel MCP server
    # registers tools by function name, and a duplicate name replaces
    # silently (FastMCP's default duplicate policy warns and REPLACES) — an
    # un-namespaced ace_product_pulse would shadow, or be shadowed by, the
    # built-in product extension's tool, and any two scaffolds would shadow
    # each other. The fn-name/title/module-path renames are in `replacements`.
    (pkg_dir / "tools" / "product_pulse.py").rename(pkg_dir / "tools" / f"{name}_pulse.py")
    title = " ".join(part.capitalize() for part in name.split("_"))
    replacements = (
        (
            "        from extensions.reference.grounded_state_adapter import OLCStyleReferenceAdapter\n",
            "",
        ),
        (
            '        reg.register_grounded_state_adapter("olc-style-reference", OLCStyleReferenceAdapter())\n',
            "",
        ),
        ("extensions.reference", pkg),
        ("extensions/reference", pkg),  # slash-form paths in comments/docstrings
        ("ProductExtension", cls),
        ('name = "product"', f'name = "{name}"'),
        # The docstring's entry-point example KEY (`product = "..."`) — must
        # match the key the generated pyproject actually declares.
        ('product = "', f'{name} = "'),
        # Below: the reference extension's own registry keys (recipe slug,
        # discipline route, sentinel name, instrument slugs) are literal
        # "product"-prefixed strings, not derived from `name = "product"`.
        # Left unrenamed, a scaffolded extension collides with the real
        # `product` reference extension the moment both are loaded in the
        # same process (e.g. this repo's own dev env, where `product` ships
        # as a built-in `ace.extensions` entry point): register_recipe and
        # register_sentinel raise on a duplicate key, and register_instrument
        # silently overwrites the reference extension's instrument slugs with
        # the scaffold's copies (shadow routing, no exception). Rename them
        # to the new extension's namespace so a scaffolded package can be
        # loaded standalone or alongside the reference extension.
        ("product_decision_intelligence", f"{name}_decision_intelligence"),
        ("product-framing", f"{name}-framing"),
        ("multi-voice-engage", f"{name}-multi-voice-engage"),
        ("product_heartbeat", f"{name}_heartbeat"),
        ("product.product-check-outcome-v1", f"{name}.{name}-check-outcome-v1"),
        ("product-check", f"{name}-check"),
        ("prepare_product_check", f"prepare_{name}_check"),
        ("project_product_check", f"project_{name}_check"),
        (
            'domain_intelligences=["product", "product_strategy"]',
            f'domain_intelligences=["{name}", "{name}_strategy"]',
        ),
        ('disciplines=["product"]', f'disciplines=["{name}"]'),
        # MCP tool: fn name, module path, and display title. Ordered before
        # the bare module-name pair so `ace_product_pulse` gets the exact
        # rename; `tools.product_pulse` covers the import in extension.py.
        ("ace_product_pulse", f"ace_{name}_pulse"),
        ("tools.product_pulse", f"tools.{name}_pulse"),
        ("Product Pulse", f"{title} Pulse"),
    )
    for py in pkg_dir.rglob("*.py"):
        text = py.read_text(encoding="utf-8")
        for old, new in replacements:
            text = text.replace(old, new)
        py.write_text(text, encoding="utf-8")
    fields = {"name": name, "pkg": pkg, "cls": cls, "dash": dash}
    (root / "pyproject.toml").write_text(_PYPROJECT.format(**fields), encoding="utf-8")
    (root / "README.md").write_text(_README.format(**fields), encoding="utf-8")
    return root


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("name", help="snake_case extension name, e.g. green_energy")
    parser.add_argument("--dir", default=".", help="directory to create the project in")
    args = parser.parse_args(argv)
    root = scaffold(args.name, Path(args.dir))
    print(f"scaffolded: {root}")
    print(f"next: cd {root} && pip install -e .  (or set ACE_EXTENSIONS for dev)")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
