"""Tests for Knowledge Production — the Playbook Draft Generator.

Acceptance: deterministically assemble a playbook DRAFT for a capability from a transition-pattern
delta requirement (why / closes / evidence with provenance), leaving practitioner know-how as an
explicit TODO; turn a whole pattern into one draft per delta capability. No LLM, fully deterministic.
The expert reviews drafts instead of writing from a blank page.
"""

from __future__ import annotations

from compliance.knowledge_production import (
    DraftStatus, PlaybookDraft, drafts_from_pattern, generate_playbook_draft,
)

REQ = {
    "capability": "sbom_creation",
    "why_asked": "CRA requires an SBOM; MaschinenVO does not.",
    "covers_targets": ["CRA"],
    "expected_evidence": ["sbom"],
}
CONV_REQ = {
    "capability": "product_cyber_risk_assessment",
    "why_asked": "Both require assessing cyber threats.",
    "covers_targets": ["CRA", "MaschinenVO"],
    "expected_evidence": ["product_risk_assessment"],
}


def test_assembles_owned_fields_with_provenance():
    d = generate_playbook_draft("sbom_creation", REQ, control_links=["component_inventory"])
    assert d.status == DraftStatus.DRAFT_GENERATED
    assert d.why.startswith("CRA requires an SBOM")
    assert d.closes_regulations == ["CRA"] and d.expected_evidence == ["sbom"]
    assert d.typical_controls == ["component_inventory"]
    assert d.provenance["why"] == "transition_pattern:why_asked"
    assert d.provenance["closes_regulations"] == "leverage:covers_targets"
    assert d.provenance["typical_controls"] == "execution:control_links"


def test_soft_fields_are_todo():
    d = generate_playbook_draft("sbom_creation", REQ)
    assert d.todo == ["tools", "process_steps", "how_others_do_it"]   # practitioner know-how owed


def test_missing_owned_fields_go_to_todo():
    d = generate_playbook_draft("x", {})
    assert "why" in d.todo and "expected_evidence" in d.todo
    assert d.closes_regulations == [] and d.typical_controls == []
    assert d.status == DraftStatus.DRAFT_GENERATED


def test_missing_because_fallback_for_why():
    d = generate_playbook_draft("x", {"missing_because": "no analogue in ISO 27001"})
    assert d.why == "no analogue in ISO 27001" and "why" not in d.todo


def test_closes_deduped_sorted_and_title_humanised():
    d = generate_playbook_draft("secure_signed_update_distribution", {"covers_targets": ["MaschinenVO", "CRA", "CRA"]})
    assert d.closes_regulations == ["CRA", "MaschinenVO"]
    assert d.title == "secure signed update distribution"


def test_controls_default_empty_no_execution_data():
    d = generate_playbook_draft("x", REQ)
    assert d.typical_controls == []                                   # nothing injected -> empty


def test_drafts_from_pattern_one_per_delta_in_order():
    pattern = {"delta_requirements": [REQ, CONV_REQ]}
    drafts = drafts_from_pattern(pattern)
    assert [d.capability_id for d in drafts] == ["sbom_creation", "product_cyber_risk_assessment"]
    assert drafts[1].closes_regulations == ["CRA", "MaschinenVO"]     # leverage 2 carried through


def test_drafts_from_pattern_injects_controls_and_skips_unnamed():
    pattern = {"delta_requirements": [REQ, {"why_asked": "no capability key"}]}
    drafts = drafts_from_pattern(pattern, control_links_by_cap={"sbom_creation": ["c1"]})
    assert len(drafts) == 1 and drafts[0].typical_controls == ["c1"]  # entry without capability skipped


def test_deterministic():
    pattern = {"delta_requirements": [REQ, CONV_REQ]}
    a = [(d.capability_id, d.why, tuple(d.todo)) for d in drafts_from_pattern(pattern)]
    b = [(d.capability_id, d.why, tuple(d.todo)) for d in drafts_from_pattern(pattern)]
    assert a == b


def test_returns_playbookdraft_type():
    assert isinstance(generate_playbook_draft("x", REQ), PlaybookDraft)
