"""roam skill-generate — emit an agent-readable skill manifest.

Phase 1 follow-on. Closes GitHub #14 + community PR #15. Where the PR
shipped a static SKILL.md, this command generates the skill DYNAMICALLY
from (a) the live capability registry and (b) optionally the indexed
repo's stats. Each emitter targets a specific agent runtime:

  - claude  — Claude Code SKILL.md (frontmatter + body)
  - cursor  — .cursor/rules/roam.mdc (Cursor rule format)
  - continue— ~/.continue/config.json snippet
  - aider   — .aiderrc snippet

Output formats: text (default), ``--json``. SARIF is deliberately NOT
emitted because skill-generate outputs are agent skill manifest
definitions — not per-location violations. SARIF is reserved for
findings with file:line coordinates; skill-generate's primary
deliverable is the agent skill manifest. See action.yml
_SUPPORTED_SARIF allowlist + W1175-RESEARCH Bucket C propagation plan +
W1148 audit memo.
"""

from __future__ import annotations

from datetime import datetime, timezone
from pathlib import Path
from typing import Iterable

import click

from roam.capability import REGISTRY, roam_capability
from roam.output.formatter import json_envelope, to_json


@roam_capability(
    name="skill-generate",
    category="workflow",
    summary="Generate an agent-runtime skill manifest from the capability registry",
    maturity="stable",
    mcp_expose=True,
    mcp_preset=("core",),
    side_effect=True,
    task_required=False,
    destructive=False,
    stale_sensitive=False,
    ai_safe=True,
    requires_index=False,
)
@click.command(name="skill-generate")
@click.option(
    "--target",
    type=click.Choice(["claude", "cursor", "continue", "aider"]),
    default="claude",
    show_default=True,
    help="Agent runtime to target. claude = SKILL.md, cursor = .mdc, continue/aider = config snippet.",
)
@click.option(
    "--output",
    "output_path",
    type=click.Path(dir_okay=False, writable=True),
    default=None,
    help="Write to file instead of stdout. Defaults to a sensible per-target path when omitted.",
)
@click.option(
    "--ai-safe-only/--all",
    default=True,
    show_default=True,
    help="Restrict the skill to capabilities marked ai_safe (recommended for autonomous agents).",
)
@click.pass_context
def skill_generate_cmd(ctx, target: str, output_path: str | None, ai_safe_only: bool) -> None:
    """Generate an agent-runtime skill manifest from the capability registry.

    Use this instead of hand-maintaining a skills/SKILL.md — when commands
    change shape, the manifest stays in sync because it derives from the
    @roam_capability decorators.
    """
    json_mode = ctx.obj.get("json") if ctx.obj else False

    # Force-load known capability modules
    from roam.commands.cmd_capabilities import _populate_registry

    _populate_registry()

    caps = REGISTRY.all()
    if ai_safe_only:
        caps = [c for c in caps if c.ai_safe]

    if target == "claude":
        text, _default_path, fmt = _emit_claude_skill(caps), "skills/roam/SKILL.md", "markdown"
    elif target == "cursor":
        text, _default_path, fmt = _emit_cursor_rule(caps), ".cursor/rules/roam.mdc", "markdown"
    elif target == "continue":
        text, _default_path, fmt = _emit_continue_snippet(caps), "continue-roam-snippet.json", "json"
    elif target == "aider":
        text, _default_path, fmt = _emit_aider_snippet(caps), ".aiderrc", "config"
    else:
        raise click.ClickException(f"unknown target: {target}")

    out = Path(output_path) if output_path else None
    if out:
        out.parent.mkdir(parents=True, exist_ok=True)
        out.write_text(text, encoding="utf-8")
        click.echo(f"wrote {len(text)} bytes to {out}")
    elif json_mode:
        click.echo(
            to_json(
                json_envelope(
                    "skill-generate",
                    summary={
                        "verdict": f"generated roam skill ({len(text)} chars)",
                        "state": "generated",
                    },
                    skill=text,
                    target=target,
                    format=fmt,
                )
            )
        )
    else:
        click.echo(text)


def _emit_claude_skill(caps: list) -> str:
    """Claude Code SKILL.md format — YAML frontmatter + markdown body."""
    description = (
        "Codebase comprehension via roam-code CLI. Use when exploring codebases, "
        "planning modifications, debugging failures, assessing PR risk, or checking "
        "architecture health. Triggers on: understanding project structure, pre-change "
        "safety checks, finding symbols/files, blast radius analysis, affected tests, "
        "health scoring, refactoring guidance, code review. Requires roam-code installed "
        "(`pip install roam-code`) and an indexed project (`roam init`)."
    )
    lines: list[str] = [
        "---",
        "name: roam",
        "description: >",
    ]
    for chunk in _wrap(description, 78, prefix="  "):
        lines.append(chunk)
    lines.extend(
        [
            "---",
            "",
            "# Roam — Codebase Comprehension Skill",
            "",
            f"_Generated by `roam skill-generate --target claude` on {_today()}._",
            "",
            "**Repository:** <https://github.com/Cranot/roam-code>",
            "",
            "Roam pre-indexes codebases into a semantic graph (symbols, dependencies, "
            "call graphs, architecture layers, git history) stored in a local SQLite DB. "
            "Query it via CLI instead of repeatedly grepping files and guessing structure.",
            "",
            "## Setup",
            "",
            "```bash",
            "pip install roam-code      # or: pipx install roam-code",
            "cd <project-root>",
            "roam init                  # indexes the codebase, creates .roam/index.db",
            "```",
            "",
            "After `git pull` or major changes, run `roam reindex` (incremental, near-instant). "
            "After large refactors: `roam reindex --force`.",
            "",
            "## When to use which command",
            "",
            "| Situation | Command |",
            "|-----------|---------|",
            "| Orient yourself in a new codebase | `roam understand` then `roam tour` |",
            "| Find a symbol | `roam search <pattern>` |",
            '| Free-form task ("trace login flow") | `roam retrieve "<task>"` |',
            "| Before changing a symbol | `roam preflight <symbol>` |",
            "| Files to read for context | `roam context <symbol>` |",
            "| Verify a patch | `git diff \\| roam critique` |",
            "| Pre-commit gate / agent verdict | `git diff --cached \\| roam permit` |",
            "| Replay current detectors against past commits | `roam postmortem HEAD~30..HEAD` |",
            "| EU AI Act Article 12 readiness | `roam article-12-check` |",
            "",
            "## Core capabilities",
            "",
            "These are the capabilities marked safe for autonomous agent calls. "
            "See `roam capabilities` for the full list.",
            "",
        ]
    )
    by_cat: dict[str, list] = {}
    for c in caps:
        by_cat.setdefault(c.category, []).append(c)
    for cat in sorted(by_cat):
        lines.append(f"### {cat.title()}")
        lines.append("")
        for cap in by_cat[cat]:
            lines.append(f"- **`roam {cap.name}`** — {cap.summary}")
            if cap.examples:
                lines.append(f"  - Example: `{cap.examples[0]}`")
        lines.append("")
    lines.extend(
        [
            "## Output modes",
            "",
            "- Text (default) — human-readable; safe to feed back into your context.",
            "- `--json` — structured envelope with `verdict`, `summary`, schema_version. Parse this in code.",
            "- `--sarif` — SARIF 2.1.0 for CI integration (GitHub Code Scanning).",
            "",
            "## Exit codes",
            "",
            "- 0 — success / ALLOW",
            "- 5 — high-severity finding / BLOCK / gate failure",
            "- 6 — REVIEW / partial",
            "",
            "All other non-zero codes are unexpected; report as an issue.",
            "",
        ]
    )
    return "\n".join(lines) + "\n"


def _emit_cursor_rule(caps: list) -> str:
    """Cursor .mdc rule — YAML frontmatter + markdown."""
    lines: list[str] = [
        "---",
        "description: Use roam-code CLI to explore the codebase before editing",
        "globs: ['**/*']",
        "alwaysApply: false",
        "---",
        "",
        f"<!-- Generated by `roam skill-generate --target cursor` on {_today()} -->",
        "",
        "# Roam codebase comprehension",
        "",
        "Before making non-trivial code changes, use the roam-code CLI to understand what you're about to touch.",
        "",
        "1. `roam preflight <symbol>` — see what changing this affects.",
        "2. `roam context <symbol>` — get the exact files and line ranges to read.",
        "3. After editing, `git diff | roam critique` — verify the patch.",
        "",
        "## Available commands (ai-safe subset)",
        "",
    ]
    for cap in caps:
        lines.append(f"- `roam {cap.name}` — {cap.summary}")
    lines.append("")
    return "\n".join(lines) + "\n"


def _emit_continue_snippet(caps: list) -> str:
    """Continue config.json snippet — slash commands."""
    import json

    snippet = {
        "slashCommands": [
            {
                "name": cap.name,
                "description": cap.summary,
                "step": f"Run `roam {cap.name}`",
            }
            for cap in caps[:10]  # Continue's UI gets crowded past ~10
        ],
        "_generatedBy": "roam skill-generate --target continue",
        "_generatedAt": _today(),
    }
    return json.dumps(snippet, indent=2)


def _emit_aider_snippet(caps: list) -> str:
    """Aider .aiderrc snippet — defines aliases that shell out to roam."""
    lines: list[str] = [
        f"# Generated by `roam skill-generate --target aider` on {_today()}",
        "# Append these to your .aiderrc to expose roam commands as aliases.",
        "",
        "[aider]",
        "# Run roam preflight before any edit",
        "auto-test = true",
        "test-cmd = roam preflight $(git diff --name-only | head -1)",
        "",
        "[aliases]",
    ]
    for cap in caps:
        lines.append(f"# {cap.summary}")
        lines.append(f"{cap.name.replace('-', '_')} = !roam {cap.name}")
        lines.append("")
    return "\n".join(lines) + "\n"


def _wrap(text: str, width: int, prefix: str = "") -> Iterable[str]:
    """Word-wrap with a constant prefix on each line."""
    words = text.split()
    line = prefix
    for w in words:
        candidate = line + (" " if line.strip() else "") + w
        if len(candidate) > width and line.strip():
            yield line
            line = prefix + w
        else:
            line = candidate
    if line.strip():
        yield line


def _today() -> str:
    return datetime.now(timezone.utc).strftime("%Y-%m-%d")
