"""Tests for post-mortem writer.

Covers: post-mortem file creation, content correctness, path reporting,
DER-structured step rendering, and legacy step rendering.
"""

from __future__ import annotations

from pathlib import Path

from mcp_vm_blackbox.models import CriterionResult, VerificationReport
from mcp_vm_blackbox.post_mortem import write_post_mortem


def test_post_mortem_file_created_at_correct_path(tmp_path: Path) -> None:
    """Post-mortem writer creates a .md file at the path derived from vm_name/scenario_name/timestamp."""
    write_post_mortem(
        vm_name="vm1",
        scenario_name="install",
        steps=[{"name": "step1", "outcome": "ok"}],
        verification_report=None,
        root=tmp_path,
    )

    expected_dir = tmp_path / "post-mortems" / "vm1" / "install"
    assert expected_dir.exists(), f"Expected directory {expected_dir} to exist"

    md_files = list(expected_dir.glob("*.md"))
    assert len(md_files) == 1, f"Expected exactly one .md file, found {md_files}"
    assert md_files[0].suffix == ".md"
    # Timestamp format: YYYYMMDDTHHMMSSZ
    stem = md_files[0].stem
    assert len(stem) == 16, f"Timestamp stem '{stem}' should be 16 chars (YYYYMMDDTHHMMSSZ)"
    assert stem.endswith("Z")


def test_post_mortem_contains_steps(tmp_path: Path) -> None:
    """Post-mortem .md file contains the steps list with outcomes."""
    steps: list[dict[str, str | None]] = [
        {"name": "vagrant_up", "outcome": "ok"},
        {"name": "run_installer", "outcome": "failed"},
    ]
    result = write_post_mortem(
        vm_name="vm2", scenario_name="upgrade", steps=steps, verification_report=None, root=tmp_path
    )

    content = Path(result.post_mortem_path).read_text(encoding="utf-8")
    assert "vagrant_up: ok" in content
    assert "run_installer: failed" in content
    assert "## Steps" in content
    assert "No verification run recorded." in content


def test_post_mortem_path_returned(tmp_path: Path) -> None:
    """Post-mortem writer returns the absolute path to the written .md file."""
    criterion_result = CriterionResult(
        criterion_id="crit-1", criterion_type="port_open", host="web", expected="open", actual="open", passed=True
    )
    report = VerificationReport(vm_name="vm3", scenario_name="smoke", passed=True, results=[criterion_result])

    result = write_post_mortem(
        vm_name="vm3",
        scenario_name="smoke",
        steps=[{"name": "check", "outcome": "pass"}],
        verification_report=report,
        root=tmp_path,
    )

    assert result.post_mortem_path, "post_mortem_path must be non-empty"
    written_path = Path(result.post_mortem_path)
    assert written_path.is_absolute(), "post_mortem_path must be absolute"
    assert written_path.exists(), "Written file must exist at the returned path"
    assert written_path.suffix == ".md"

    content = written_path.read_text(encoding="utf-8")
    assert "**Overall:** PASS" in content
    assert "port_open" in content
    assert "expected=open" in content


# ---------------------------------------------------------------------------
# DER rendering
# ---------------------------------------------------------------------------


def test_post_mortem_der_step_renders_structured_block(tmp_path: Path) -> None:
    """Post-mortem renders a step with DER fields as a structured markdown block.

    Tests: _render_step_der() path in _render_markdown() when DER fields are present.
    How: Write a post-mortem with one step that has intent, reason, outcome_status,
        outcome_detail, issue_classification, resolution, and next. Read the .md file
        and assert each DER field appears in the structured block format.
    Why: The post-mortem is the primary audit artefact; incorrect DER rendering means
        the inspector and QA engineers cannot trace what the agent decided and why.
    """
    # Arrange
    steps: list[dict[str, str | None]] = [
        {
            "action": "install service",
            "intent": "Install the Windows service",
            "reason": "Required by goal state",
            "outcome_status": "success",
            "outcome_detail": "Service started on first attempt",
            "issue_classification": None,
            "resolution": None,
            "next": "Verify via vm_powershell",
        }
    ]

    # Act
    result = write_post_mortem(
        vm_name="vm1", scenario_name="smoke", steps=steps, verification_report=None, root=tmp_path
    )

    # Assert
    content = Path(result.post_mortem_path).read_text(encoding="utf-8")
    assert "### Step 1: install service" in content
    assert "**Intent:** Install the Windows service" in content
    assert "**Reason:** Required by goal state" in content
    assert "**Outcome:** success" in content
    assert "Service started on first attempt" in content
    assert "**Next:** Verify via vm_powershell" in content


def test_post_mortem_der_step_with_issue_classification(tmp_path: Path) -> None:
    """Post-mortem renders issue classification and resolution in the Issue line.

    Tests: Issue line formatting in _render_step_der() when classification is present.
    How: Write a post-mortem with a step that has issue_classification='harness' and
        resolution='Reported upstream'. Assert the rendered content contains both
        the classification and the resolution joined with ' -- '.
    Why: The issue line is used by the inspector to identify harness vs user-facing
        problems; wrong formatting would break automated parsing of the markdown.
    """
    # Arrange
    steps: list[dict[str, str | None]] = [
        {
            "action": "provision vm",
            "outcome_status": "failed",
            "issue_classification": "harness",
            "resolution": "Reported upstream",
            "next": "Escalate to plugin team",
        }
    ]

    # Act
    result = write_post_mortem(
        vm_name="vm1", scenario_name="install", steps=steps, verification_report=None, root=tmp_path
    )

    # Assert
    content = Path(result.post_mortem_path).read_text(encoding="utf-8")
    assert "harness -- Reported upstream" in content
    assert "**Next:** Escalate to plugin team" in content


def test_post_mortem_legacy_step_renders_flat_line(tmp_path: Path) -> None:
    """Post-mortem renders a step without DER fields as a flat bullet line.

    Tests: Legacy rendering path in _render_markdown() when no DER fields are present.
    How: Write a post-mortem with one legacy step (action + outcome, no DER fields).
        Read the .md file and assert the content uses the flat '- action: outcome'
        format, not the structured '### Step' block.
    Why: Legacy steps logged before the DER feature must continue to render correctly;
        forcing them through the structured block renderer would produce 'N/A' noise.
    """
    # Arrange
    steps: list[dict[str, str | None]] = [{"action": "vagrant_up", "outcome": "ok"}]

    # Act
    result = write_post_mortem(
        vm_name="vm1", scenario_name="install", steps=steps, verification_report=None, root=tmp_path
    )

    # Assert
    content = Path(result.post_mortem_path).read_text(encoding="utf-8")
    assert "- vagrant_up: ok" in content
    assert "### Step 1:" not in content
