"""Code generation utilities for KIESSCLAW v1.2."""

from __future__ import annotations

import re
from pathlib import Path
from typing import Any

from kiessclaw.usecases.registry import get_usecase, validate_usecase

TEMPLATE_ROOT = Path(__file__).resolve().parent / "templates"


def scaffold_usecase(
    usecase_id: str,
    target_dir: str,
    inputs: dict[str, Any],
    overwrite: bool = False,
) -> list[dict[str, str]]:
    """Scaffold a use case package from built-in templates."""
    manifest = build_usecase_manifest(usecase_id=usecase_id, inputs=inputs)
    return _write_manifest(manifest=manifest, target_dir=Path(target_dir), overwrite=overwrite)


def scaffold_agent(name: str, target_dir: str, overwrite: bool = False) -> list[dict[str, str]]:
    """Scaffold one standalone agent module and test."""
    context = _context_from_name(name=name, description=f"Generated agent scaffold for {name}")
    manifest = [
        _manifest_item(
            path=f"{context['module_name']}_agent.py",
            content=_render_template("agent.py.tmpl", context),
        ),
        _manifest_item(
            path=f"tests/test_{context['module_name']}_agent.py",
            content=_render_template("test.py.tmpl", context),
        ),
    ]
    return _write_manifest(manifest=manifest, target_dir=Path(target_dir), overwrite=overwrite)


def scaffold_workflow(name: str, target_dir: str, overwrite: bool = False) -> list[dict[str, str]]:
    """Scaffold one standalone workflow module and test."""
    context = _context_from_name(name=name, description=f"Generated workflow scaffold for {name}")
    manifest = [
        _manifest_item(
            path=f"{context['module_name']}_workflow.py",
            content=_render_template("workflow.py.tmpl", context),
        ),
        _manifest_item(
            path=f"tests/test_{context['module_name']}_workflow.py",
            content=_render_template("test.py.tmpl", context),
        ),
    ]
    return _write_manifest(manifest=manifest, target_dir=Path(target_dir), overwrite=overwrite)


def scaffold_skill(name: str, target_dir: str, overwrite: bool = False) -> list[dict[str, str]]:
    """Scaffold one standalone skill module and test."""
    context = _context_from_name(name=name, description=f"Generated skill scaffold for {name}")
    manifest = [
        _manifest_item(
            path=f"{context['module_name']}_skill.py",
            content=_render_template("skill.py.tmpl", context),
        ),
        _manifest_item(
            path=f"tests/test_{context['module_name']}_skill.py",
            content=_render_template("test.py.tmpl", context),
        ),
    ]
    return _write_manifest(manifest=manifest, target_dir=Path(target_dir), overwrite=overwrite)


def scaffold_tests(kind: str, target_dir: str, overwrite: bool = False) -> list[dict[str, str]]:
    """Scaffold one deterministic test stub file."""
    context = _context_from_name(name=kind, description=f"Generated test scaffold for {kind}")
    manifest = [
        _manifest_item(
            path=f"tests/test_{context['module_name']}.py",
            content=_render_template("test.py.tmpl", context),
        )
    ]
    return _write_manifest(manifest=manifest, target_dir=Path(target_dir), overwrite=overwrite)


def build_usecase_manifest(usecase_id: str, inputs: dict[str, Any]) -> list[dict[str, str]]:
    """Build a file manifest for a use case without writing to disk."""
    spec = get_usecase(usecase_id)
    validate_usecase(spec)
    context = _context_from_usecase(spec_id=spec.id, title=spec.title, description=spec.description)
    context["required_inputs"] = ", ".join(spec.required_inputs)
    context["outputs"] = ", ".join(spec.outputs)

    if isinstance(inputs, dict):
        for key, value in sorted(inputs.items()):
            context[str(key)] = str(value)

    mapping = {
        "agent.py.tmpl": f"{spec.id}/{spec.id}_agent.py",
        "skill.py.tmpl": f"{spec.id}/{spec.id}_skill.py",
        "workflow.py.tmpl": f"{spec.id}/{spec.id}_workflow.py",
        "test.py.tmpl": f"{spec.id}/tests/test_{spec.id}.py",
        "docs.md.tmpl": f"{spec.id}/README.md",
    }

    manifest: list[dict[str, str]] = []
    for template_name in spec.scaffold_templates:
        if template_name not in mapping:
            continue
        content = _render_template(template_name, context)
        manifest.append(_manifest_item(path=mapping[template_name], content=content))
    return manifest


def _write_manifest(
    manifest: list[dict[str, str]],
    target_dir: Path,
    overwrite: bool,
) -> list[dict[str, str]]:
    """Write manifest files safely with no-overwrite defaults."""
    results: list[dict[str, str]] = []
    for item in manifest:
        path = target_dir / item["path"]
        path.parent.mkdir(parents=True, exist_ok=True)
        if path.exists() and not overwrite:
            results.append({"path": str(path), "status": "skipped", "reason": "exists"})
            continue
        path.write_text(item["content"], encoding="utf-8")
        results.append({"path": str(path), "status": "created"})
    return results


def _render_template(template_name: str, context: dict[str, str]) -> str:
    """Render one template file by replacing `{{key}}` placeholders."""
    template_path = TEMPLATE_ROOT / template_name
    template = template_path.read_text(encoding="utf-8")
    rendered = template
    for key, value in sorted(context.items(), key=lambda item: item[0]):
        rendered = rendered.replace(f"{{{{{key}}}}}", value)
    return rendered


def _context_from_name(name: str, description: str) -> dict[str, str]:
    """Build template context from a free-text scaffold name."""
    module_name = _to_snake(name)
    class_name = _to_pascal(name)
    return {
        "usecase_id": module_name,
        "title": name.strip() or module_name,
        "description": description,
        "module_name": module_name,
        "class_name": class_name,
        "required_inputs": "none",
        "outputs": "module + tests",
    }


def _context_from_usecase(spec_id: str, title: str, description: str) -> dict[str, str]:
    """Build template context from a registered use case."""
    return {
        "usecase_id": spec_id,
        "title": title,
        "description": description,
        "module_name": _to_snake(spec_id),
        "class_name": _to_pascal(spec_id),
    }


def _manifest_item(path: str, content: str) -> dict[str, str]:
    """Create one manifest entry."""
    return {"path": path, "content": content}


def _to_snake(value: str) -> str:
    """Normalize a value to snake_case."""
    normalized = re.sub(r"[^a-zA-Z0-9]+", "_", value.strip())
    normalized = re.sub(r"_+", "_", normalized).strip("_")
    return normalized.lower() or "generated"


def _to_pascal(value: str) -> str:
    """Normalize a value to PascalCase."""
    chunks = _to_snake(value).split("_")
    return "".join(chunk.capitalize() for chunk in chunks if chunk) or "Generated"
