"""Jinja2-based Markdown rendering for the 'nodeforge docs' command."""

from __future__ import annotations

from jinja2 import Environment

from nodeforge_core.plan.models import Plan, StepScope

# ---------------------------------------------------------------------------
# Templates stored as inline string constants — no external .j2 files needed.
# ---------------------------------------------------------------------------

_GUIDE_TEMPLATE = """\
# {{ plan.spec_name }}

> Generated by nodeforge on {{ plan.created_at[:19] }} UTC
> Spec kind: `{{ plan.spec_kind }}` | Target: `{{ plan.target_host }}`
> Plan hash: `{{ plan.plan_hash[:16] }}…`

## Summary

This guide was generated from the execution plan and describes exactly what
`nodeforge apply` will perform. Every command shown here is the same command
that will be executed remotely or locally.

{% if local_prep_steps %}
---

## Local Preparation

{% for step in local_prep_steps %}
### Step {{ step.index }}: {{ step.description }}

{% if step.command and not step.command.startswith(('ssh_check:', 'check:', 'http_check:')) %}
```bash
{{ step.command }}
```
{% endif %}
{% if step.rollback_hint %}
> **Recovery:** {{ step.rollback_hint }}
{% endif %}

{% endfor %}
{% endif %}

---

## Remote {{ 'Bootstrap' if plan.spec_kind == 'bootstrap' else 'Operations' }}

{% for step in remote_steps %}
### Step {{ step.index }}: {{ step.description }}
{% if step.gate %}
> ⚠️ **GATE** — Failure here will abort the plan to prevent SSH lockout.
{% endif %}
{% if step.depends_on %}
> Depends on steps: {{ step.depends_on | join(', ') }}
{% endif %}

{% if step.kind == 'ssh_upload' %}
**File:** `{{ step.target_path }}`

```
{{ step.file_content or '(file content from spec)' }}
```
{% elif step.command and not step.command.startswith(('ssh_check:', 'check:', 'http_check:', 'echo ')) %}
```bash
{{ step.command }}
```
{% elif step.command and step.command.startswith('ssh_check:') %}
```bash
# Verify new SSH login
ssh -p {{ step.command.split(':')[2] }} {{ step.command.split(':')[3] }}@{{ step.command.split(':')[1] }}
```
{% endif %}
{% if step.rollback_hint %}
> **Recovery:** {{ step.rollback_hint }}
{% endif %}

{% endfor %}

---

## Local Finalization

{% for step in local_final_steps %}
### Step {{ step.index }}: {{ step.description }}

{% if step.command and not step.command.startswith(('backup_ssh_config', 'ensure_include', 'init_inventory', 'upsert_', 'record_run')) %}
```bash
{{ step.command }}
```
{% endif %}

{% endfor %}

---

## Verification

```bash
# Verify SSH access via new alias
ssh {{ plan.spec_name | lower | replace(' ', '-') }}

# Check services
{% for step in verify_steps %}
# {{ step.description }}
{% if step.command and not step.command.startswith(('check:', 'http_check:', 'ssh_check:')) %}
{{ step.command }}
{% endif %}
{% endfor %}
```

---

## Recovery Notes

If the SSH gate check (step {{ gate_index }}) fails:

- **Do not** disable root login
- **Do not** disable password authentication
- Restore sshd_config from backup:

```bash
cp /etc/ssh/sshd_config.bak /etc/ssh/sshd_config
systemctl reload ssh
```

If local SSH config update fails:
- Remote bootstrap may still be successful
- Manually add the SSH conf.d entry

If SQLCipher inventory update fails:
- Remote bootstrap is still considered successful
- A warning will be shown in the output
"""

_COMMANDS_TEMPLATE = """\
# {{ plan.spec_name }} — Commands

> nodeforge docs --commands | Target: `{{ plan.target_host }}`

## Remote Steps

{% for step in remote_steps %}
{% if step.command and not step.command.startswith(('ssh_check:', 'check:', 'http_check:', 'echo ')) %}
# [{{ step.index }}] {{ step.description }}
{{ step.command }}

{% endif %}
{% endfor %}

## Local Steps

{% for step in local_final_steps %}
{% if step.command and not step.command.startswith(('backup_ssh_config', 'ensure_include', 'init_inventory', 'upsert_', 'record_run')) %}
# [{{ step.index }}] {{ step.description }}
{{ step.command }}

{% endif %}
{% endfor %}
"""


def render_markdown(plan: Plan, mode: str = "guide") -> str:
    """Render plan as Markdown. mode='guide' (verbose) or 'commands' (compact)."""
    env = Environment(autoescape=False, keep_trailing_newline=True)

    remote_steps = [s for s in plan.steps if s.scope == StepScope.REMOTE]
    local_steps = [s for s in plan.steps if s.scope == StepScope.LOCAL]
    verify_steps = [s for s in plan.steps if s.scope == StepScope.VERIFY and not s.gate]

    # Separate local steps into prep (before bootstrap) and finalization (after)
    local_prep_steps: list = []
    local_final_steps = local_steps

    # Find gate index for recovery notes
    gate_index = next((s.index for s in plan.steps if s.gate), "N/A")

    template_str = _GUIDE_TEMPLATE if mode == "guide" else _COMMANDS_TEMPLATE
    template = env.from_string(template_str)

    return template.render(
        plan=plan,
        remote_steps=remote_steps,
        local_prep_steps=local_prep_steps,
        local_final_steps=local_final_steps,
        verify_steps=verify_steps,
        gate_index=gate_index,
    )
