#!/usr/bin/env nu

# Template Documentation Generator
#
# Reads an instructions.ncl file and renders it as:
#   --mode human  →  README.md   (editor-facing guidelines)
#   --mode agent  →  prompt.md   (AI agent system prompt)
#
# The instructions.ncl is the single source of truth for the content workflow.
# Same schema, different projections per actor.
#
# Usage:
#   nu generate-template-docs.nu [--type blog] [--mode human|agent] [--output PATH]
#   nu generate-template-docs.nu --instructions PATH [--mode human|agent] [--output PATH]
#
# Examples:
#   nu generate-template-docs.nu --type blog --mode human
#   nu generate-template-docs.nu --type blog --mode agent --output site/content/blog/_templates/prompt.md

def main [
    --type:         string = "blog"   # Content type: blog | recipes | projects | activities
    --instructions: string = ""       # Override: explicit path to instructions.ncl
    --mode:         string = "human"  # Output mode: human | agent
    --output:       string = ""       # Output file path (default: stdout)
] {
    let instr_path = if $instructions != "" {
        $instructions
    } else {
        $"site/reflection/content/($type)/mod.ncl"
    }

    if not ($instr_path | path exists) {
        error make { msg: $"Instructions file not found: ($instr_path)" }
    }

    if not ($mode in ["human", "agent"]) {
        error make { msg: $"--mode must be 'human' or 'agent', got: ($mode)" }
    }

    let data = nickel-export ($instr_path | path expand)

    let doc = if $mode == "human" {
        render_human $data
    } else {
        render_agent $data
    }

    if $output != "" {
        $doc | save --force $output
        print $"(ansi green)Written(ansi reset) → ($output)"
    } else {
        print $doc
    }
}

# ---------------------------------------------------------------------------
# Human-facing README
# ---------------------------------------------------------------------------

def render_human [d: record] {
    mut out = ""

    $out = $out + $"# ($d.content_type | str capitalize) Content Guide\n\n"
    $out = $out + $"Version ($d.version)\n\n"

    # Template files
    $out = $out + "## Template Files\n\n"
    $out = $out + $"| File | Purpose |\n|------|----------|\n"
    $out = $out + $"| `($d.files.metadata)` | Post metadata — validated by Nickel contracts |\n"
    $out = $out + $"| `($d.files.content)` | Post body — no frontmatter, pure content |\n"
    $out = $out + $"| `($d.files.index)` | Category index — auto-generated, do not edit |\n"
    $out = $out + "\n"

    # Fields reference
    $out = $out + "## Metadata Fields\n\n"
    $out = $out + "| Field | Type | Required | Editable | Description |\n"
    $out = $out + "|-------|------|----------|----------|-------------|\n"
    for f in $d.fields {
        let req = if $f.required { "✅" } else { "—" }
        let edt = if $f.editable { "✅" } else { "🔒 pipeline" }
        $out = $out + $"| `($f.name)` | `($f.type)` | ($req) | ($edt) | ($f.doc) |\n"
    }
    $out = $out + "\n"

    # Rules
    $out = $out + "## Rules\n\n"
    for rule_group in ($d.constraints | items { |k, v| { group: $k, items: $v } }) {
        $out = $out + $"### ($rule_group.group | str replace --all '_' ' ' | str capitalize)\n\n"
        for rule in $rule_group.items {
            $out = $out + $"- ($rule)\n"
        }
        $out = $out + "\n"
    }

    # Workflows
    $out = $out + "## Workflows\n\n"
    for mode_entry in ($d.modes | items { |k, v| { name: $k, mode: $v } }) {
        let title = $mode_entry.name | str replace --all '_' ' ' | str capitalize
        $out = $out + $"### ($title)\n\n"
        $out = $out + $"_($mode_entry.mode.trigger)_\n\n"
        mut step_n = 1
        for step in $mode_entry.mode.steps {
            let actor_tag = if $step.actor == "Agent" { " _(agent only)_" } else if $step.actor == "Human" { " _(human only)_" } else { "" }
            let step_id   = if ("id" in $step) { $" `($step.id)`" } else { "" }
            $out = $out + $"($step_n). **($step.action)**($step_id)($actor_tag)\n"
            let deps = if ("depends_on" in $step) { $step.depends_on } else { [] }
            if ($deps | length) > 0 {
                let dep_list = $deps | each { |d|
                    if $d.kind == "Always" { $d.step } else { $"($d.step):($d.kind)" }
                } | str join ", "
                $out = $out + $"   _Requires: ($dep_list)_\n"
            }
            if ("cmd" in $step) {
                $out = $out + $"   ```\n   ($step.cmd)\n   ```\n"
            }
            if ("verify" in $step) {
                $out = $out + $"   Verify: `($step.verify)`\n"
            }
            if ("on_error" in $step) {
                let oe = $step.on_error
                let retry_info = if ($oe.strategy == "Retry") { $" (max ($oe.max), backoff ($oe.backoff_s)s)" } else { "" }
                let target_info = if ("target" in $oe) { $" → ($oe.target)" } else { "" }
                $out = $out + $"   On error: **($oe.strategy)**($retry_info)($target_info)\n"
            }
            if ("note" in $step) {
                $out = $out + $"   > ($step.note)\n"
            }
            $step_n = $step_n + 1
        }
        $out = $out + "\n"
    }

    # Style
    $out = $out + "## Style Guide\n\n"
    for style_entry in ($d.style | items { |k, v| { key: $k, val: $v } }) {
        $out = $out + $"**($style_entry.key | str capitalize):** ($style_entry.val)\n\n"
    }

    $out
}

# ---------------------------------------------------------------------------
# Agent system prompt
# ---------------------------------------------------------------------------

def render_agent [d: record] {
    mut out = ""

    $out = $out + $"# Agent Instructions — ($d.content_type | str capitalize) Content\n\n"
    $out = $out + "## Persona\n\n"
    $out = $out + $"($d.agent.persona)\n\n"

    $out = $out + "## Constraints\n\n"
    for c in $d.agent.rules {
        $out = $out + $"- ($c)\n"
    }
    $out = $out + "\n"

    # Metadata schema
    $out = $out + $"## Metadata Schema \(`($d.files.metadata)`\)\n\n"
    $out = $out + "Required fields (must always be present):\n\n"
    for f in ($d.fields | where required == true) {
        let example = if ("example" in $f) { $" Example: `($f.example)`" } else { "" }
        $out = $out + $"- **($f.name)** `($f.type)` — ($f.doc)($example)\n"
    }
    $out = $out + "\nOptional fields (include when known):\n\n"
    for f in ($d.fields | where required == false) {
        let locked = if not $f.editable { " **[PIPELINE ONLY — do not set]**" } else { "" }
        let example = if ("example" in $f) { $" Example: `($f.example)`" } else { "" }
        $out = $out + $"- **($f.name)** `($f.type)` — ($f.doc)($locked)($example)\n"
    }
    $out = $out + "\n"

    # Rules condensed
    $out = $out + "## Rules\n\n"
    for rule_group in ($d.constraints | items { |k, v| { group: $k, items: $v } }) {
        for rule in $rule_group.items {
            $out = $out + $"- ($rule)\n"
        }
    }
    $out = $out + "\n"

    # Workflows — agent steps only
    $out = $out + "## Action Flows\n\n"
    for mode_entry in ($d.modes | items { |k, v| { name: $k, mode: $v } }) {
        let title = $mode_entry.name | str replace --all '_' ' ' | str capitalize
        $out = $out + $"### ($title)\n"
        $out = $out + $"Trigger: ($mode_entry.mode.trigger)\n\n"
        let agent_steps = $mode_entry.mode.steps | where { |s| $s.actor != "Human" }
        mut step_n = 1
        for step in $agent_steps {
            let step_id = if ("id" in $step) { $" [($step.id)]" } else { "" }
            $out = $out + $"($step_n). ($step.action)($step_id)\n"
            let deps = if ("depends_on" in $step) { $step.depends_on } else { [] }
            if ($deps | length) > 0 {
                let dep_list = $deps | each { |d|
                    if $d.kind == "Always" { $d.step } else { $"($d.step):($d.kind)" }
                } | str join ", "
                $out = $out + $"   Depends on: ($dep_list)\n"
            }
            if ("cmd" in $step) {
                $out = $out + $"   Command: `($step.cmd)`\n"
            }
            if ("verify" in $step) {
                $out = $out + $"   Verify: `($step.verify)`\n"
            }
            if ("on_error" in $step) {
                let oe = $step.on_error
                if $oe.strategy != "Stop" {
                    let retry_info = if ($oe.strategy == "Retry") { $" max=($oe.max) backoff=($oe.backoff_s)s" } else { "" }
                    let target_info = if ("target" in $oe) { $" → ($oe.target)" } else { "" }
                    $out = $out + $"   On error: ($oe.strategy)($retry_info)($target_info)\n"
                }
            }
            $step_n = $step_n + 1
        }
        $out = $out + "\n"
    }

    # Style
    $out = $out + "## Style Requirements\n\n"
    for style_entry in ($d.style | items { |k, v| { key: $k, val: $v } }) {
        $out = $out + $"- **($style_entry.key | str capitalize):** ($style_entry.val)\n"
    }
    $out = $out + "\n"

    # File templates
    $out = $out + "## File Templates\n\n"
    $out = $out + $"### `($d.files.metadata)` structure\n\n"
    $out = $out + "```nickel\n"
    $out = $out + "let schema = import \"content/metadata/post_metadata.ncl\" in\n\n"
    $out = $out + "schema.make_post {\n"
    for f in ($d.fields | where { |f| $f.required == true or $f.name in ["subtitle", "excerpt", "tags", "read_time"] }) {
        let example = if ("example" in $f) { $"\"($f.example)\"" } else if $f.type == "bool" { "false" } else if $f.type == "number" { "0" } else { "\"\"" }
        $out = $out + $"  ($f.name) = ($example),\n"
    }
    $out = $out + "}\n```\n\n"

    $out = $out + $"### `($d.files.content)` structure\n\n"
    $out = $out + "```markdown\n<!-- hero -->\n\n# Post Title\n\nContent body.\n```\n"

    $out
}
