"""Inspect ask workflow recipes without running their command steps.

Output formats: text (default), ``--json``. SARIF is deliberately NOT
emitted because cmd_workflow is a recipe-composer / inspector (lists
the ask-workflow recipes and their command-step DAG without executing
them). When a workflow IS executed via ``roam ask``, the composed
sub-commands emit their own ``--sarif`` when applicable; cmd_workflow
itself returns an invocation-scoped recipe-metadata enumeration —
not per-location violations. See ``cmd_report`` for the parallel
composer disclosure pattern (W1221) + action.yml _SUPPORTED_SARIF
allowlist + W1145 / W1085 composer audit + W1224-audit memo.
"""

from __future__ import annotations

import click

from roam.ask.recipes import RECIPES, Recipe, by_name
from roam.ask.runner import extract_recipe_symbol, fill_args
from roam.ask.workflow import recipe_workflow_metadata
from roam.capability import roam_capability
from roam.output.formatter import json_envelope, to_json


def _recipe_payload(recipe: Recipe, query: str = "") -> dict:
    symbol = extract_recipe_symbol(query)
    return {
        **recipe_workflow_metadata(recipe, query=query, render_followups=bool(query)),
        "examples": list(recipe.examples),
        "commands": [
            {
                "cmd": cmd,
                "args": fill_args(args, query, symbol) if query else list(args),
            }
            for cmd, args in recipe.commands
        ],
    }


def _emit_recipe_list(json_mode: bool) -> None:
    payloads = [_recipe_payload(recipe) for recipe in RECIPES]
    if json_mode:
        # CONSTRAINT 12: populate next_commands so agents reading
        # `agent_contract.next_commands` get a copy-paste-executable
        # follow-up. The first three recipes are a representative sample;
        # `roam workflow --list` is always offered as the broad followup.
        sample_recipes = [p["recipe"] for p in payloads[:3]]
        next_commands = ["roam workflow --list"]
        next_commands.extend(f"roam workflow {name}" for name in sample_recipes)
        click.echo(
            to_json(
                json_envelope(
                    "workflow",
                    summary={
                        "verdict": f"{len(payloads)} workflow recipe(s) available",
                        "recipe_count": len(payloads),
                    },
                    recipes=payloads,
                    agent_contract={
                        "facts": [
                            f"{len(payloads)} workflow recipes available",
                        ],
                        "next_commands": next_commands,
                    },
                )
            )
        )
        return

    click.echo(f"VERDICT: {len(payloads)} workflow recipe(s) available")
    click.echo()
    for item in payloads:
        click.echo(f"  {item['recipe']:<20s} {item['phase']}")
        click.echo(f"     lenses: {', '.join(item['perspectives'])}")
    click.echo()
    click.echo("Run `roam workflow <recipe>` to inspect a recipe.")


def _emit_recipe_detail(recipe: Recipe, query: str, json_mode: bool) -> None:
    payload = _recipe_payload(recipe, query)
    if json_mode:
        # CONSTRAINT 12: surface the recipe's commands + followups via
        # `agent_contract.next_commands` so the canonical follow-up field
        # is non-empty. Steps appear first (the ordered DAG to execute),
        # then the recipe's `followups` after.
        step_invocations = [f"roam {item['cmd']} {' '.join(item['args'])}".rstrip() for item in payload["commands"]]
        followups = list(payload.get("followups") or [])
        next_commands: list[str] = []
        for cmd in step_invocations + followups:
            if cmd and cmd not in next_commands:
                next_commands.append(cmd)
        click.echo(
            to_json(
                json_envelope(
                    "workflow",
                    summary={
                        "verdict": f"workflow recipe '{recipe.name}'",
                        "recipe": recipe.name,
                        "phase": recipe.phase,
                    },
                    agent_contract={
                        "facts": [
                            f"recipe '{recipe.name}' has {len(step_invocations)} commands",
                        ],
                        "next_commands": next_commands,
                    },
                    **payload,
                )
            )
        )
        return

    click.echo(f"VERDICT: workflow recipe '{recipe.name}'")
    click.echo(f"PHASE: {recipe.phase}")
    click.echo(f"INTENT: {recipe.intent}")
    click.echo(f"PERSPECTIVES: {', '.join(recipe.perspectives)}")
    if recipe.gates:
        click.echo(f"GATES: {'; '.join(recipe.gates)}")
    click.echo()
    click.echo("COMMANDS:")
    for item in payload["commands"]:
        click.echo(f"  roam {item['cmd']} {' '.join(item['args'])}".rstrip())
    if payload["followups"]:
        click.echo()
        click.echo("NEXT:")
        for item in payload["followups"]:
            click.echo(f"  {item}")


_NEXT_HINTS: dict[str, list[str]] = {
    "preflight": ["roam context <symbol>", "roam impact <symbol>", "roam diff"],
    "context": ["roam preflight <symbol>", "roam diff"],
    "diff": ["git diff | roam critique", "roam pr-prep", "roam pr-risk"],
    "critique": ["roam pr-prep", "roam diff"],
    "pr-risk": ["roam pr-prep", "roam fitness"],
    "health": ["roam debt", "roam complexity", "roam hotspots --danger"],
    "search": ["roam context <symbol>", "roam impact <symbol>"],
    "retrieve": ["roam context <symbol>", "roam preflight <symbol>"],
    "understand": ["roam tour", "roam stats", "roam health"],
    "tour": ["roam health", "roam minimap"],
    "init": ["roam understand", "roam tour", "roam stats"],
    "index": ["roam health", "roam diagnose <symbol>"],
    "stats": ["roam health", "roam tour"],
    "impact": ["roam preflight <symbol>", "roam affected-tests <symbol>"],
    "diagnose": ["roam timeline <symbol>", "roam recommend <symbol>"],
}


def _emit_next_after(next_after: str, json_mode: bool) -> None:
    suggestions = _NEXT_HINTS.get(next_after.lower(), [])
    verdict = (
        f"{len(suggestions)} suggestion(s) after `roam {next_after}`"
        if suggestions
        else f"no canned next-command for `roam {next_after}`"
    )
    if json_mode:
        next_commands = list(suggestions) if suggestions else ["roam workflow --list"]
        click.echo(
            to_json(
                json_envelope(
                    "workflow",
                    summary={"verdict": verdict, "after": next_after},
                    suggestions=suggestions,
                    agent_contract={
                        "facts": [verdict],
                        "next_commands": next_commands,
                    },
                )
            )
        )
        return
    click.echo(f"VERDICT: {verdict}")
    for suggestion in suggestions:
        click.echo(f"  {suggestion}")


def _unknown_recipe_error(recipe_name: str, json_mode: bool) -> click.ClickException:
    from roam.output.errors import UNKNOWN_RECIPE, structured_usage_error
    from roam.output.structured_unknowns import (
        structured_unknown_filter,
        to_summary_payload,
    )

    known_names = sorted(r.name for r in RECIPES)
    frag = structured_unknown_filter(
        requested=recipe_name,
        known=known_names,
        state="unknown_recipe",
        requested_field="requested_recipe",
        known_field="known_recipes",
        fact_anchor="recipes",
    )
    assert frag is not None
    base_msg = f"unknown workflow recipe: {recipe_name!r}. Run `roam workflow --list`.{frag['verdict_suffix']}"
    if json_mode:
        verdict_unknown = f"unknown workflow recipe {recipe_name!r}"
        click.echo(
            to_json(
                json_envelope(
                    "workflow",
                    summary={
                        "verdict": verdict_unknown + frag["verdict_suffix"],
                        **to_summary_payload(frag),
                        "error_code": UNKNOWN_RECIPE,
                    },
                    agent_contract={
                        "facts": frag["facts"],
                        "next_commands": ["roam workflow --list"],
                    },
                )
            )
        )
    return structured_usage_error(UNKNOWN_RECIPE, base_msg)


@roam_capability(
    name="workflow",
    category="getting-started",
    summary="Inspect a workflow recipe DAG, review lenses, and next commands",
    maturity="stable",
    mcp_expose=True,
    mcp_preset=("core",),
    side_effect=False,
    task_required=False,
    destructive=False,
    stale_sensitive=True,
    ai_safe=True,
    requires_index=True,
)
@click.command("workflow")
@click.argument("recipe_name", required=False)
@click.option("--list", "list_recipes", is_flag=True, help="List available workflow recipes.")
@click.option(
    "--query",
    default="",
    help="Render {symbol}/{task} placeholders using this query without running commands.",
)
@click.option(
    "--next",
    "next_after",
    type=str,
    default=None,
    help="given the previously-run command, suggest what to run next.",
)
@click.pass_context
def workflow(ctx, recipe_name, list_recipes, query, next_after):
    """Inspect a workflow recipe DAG, review lenses, and next commands."""
    json_mode = ctx.obj.get("json") if ctx.obj else False

    if next_after:
        _emit_next_after(next_after, json_mode)
        return

    if list_recipes or not recipe_name:
        _emit_recipe_list(json_mode)
        return

    recipe = by_name(recipe_name)
    if recipe is None:
        raise _unknown_recipe_error(recipe_name, json_mode)

    _emit_recipe_detail(recipe, query, json_mode)
