"""Phase 11B1: workflow.assignment end-to-end proof.

Composition-kernel proof only (contract layer), per the Phase 10E precedent of
proving forms.contact "at the contract layer" without requiring a hosted-browser
E2E for every new capability. No new primitive, App Block ontology, slot/binding
semantics, or event mechanism -- this capability reuses workflow.transition,
the existing PackEvent/producer/alias contract (10D-10D4), and the existing
admin.shell/admin.record_table/admin.record_detail App Blocks unchanged.
"""
from __future__ import annotations

from src.domain.app_blocks import compiler
from src.domain.app_blocks.hosts import host_map_payload
from src.domain.app_blocks.planner import ProductIntent, select_capabilities
from src.domain.app_blocks.public import _BLOCK_COPY
from src.domain.app_blocks.registry import load_default_registry


def test_planner_matches_tight_assignment_phrases_only():
    hit = select_capabilities(ProductIntent(purpose="Assign leads to staff for follow-up"))
    assert any(block_id == "workflow.assignment" for _cap, block_id, _reason in hit)

    # Bare "assign"/"assigned" is too common (e.g. "assign a role") to match as a
    # bag of words -- the matcher requires a tight phrase, not just the token.
    miss = select_capabilities(ProductIntent(purpose="Users can assign a role to a teammate"))
    assert not any(block_id == "workflow.assignment" for _cap, block_id, _reason in miss)


def test_host_catalog_lists_assignment_slots():
    payload = host_map_payload()
    keys = {(item["block"], item["slot"]) for item in payload["hosts"]}
    assert ("workflow.assignment", "pipeline") in keys
    assert ("workflow.assignment", "detail") in keys
    assert ("workflow.assignment", "status") in keys
    assert ("workflow.assignment", "empty") in keys


def test_public_label_exists_and_does_not_leak_the_raw_id():
    label = _BLOCK_COPY["workflow.assignment"]
    assert label == "Assignment tracking"
    assert label != "workflow.assignment"


def test_install_plan_resolves_assignment_tracking_pack_and_stays_satisfiable(monkeypatch):
    registry = load_default_registry()
    monkeypatch.setattr(
        compiler,
        "select_capabilities",
        lambda _intent: [("workflow_assignment", "workflow.assignment", "Selected.")],
    )
    plan = compiler.compile_install_plan(None, registry=registry)
    assert not plan.unsatisfiable
    assert plan.required_feature_packs == ["assignment-tracking"]
    assert "workflow.transition" in plan.required_primitives
    block_ids = {b.id for b in plan.blocks}
    expected = {"workflow.assignment", "admin.shell", "admin.record_table", "admin.record_detail"}
    assert expected <= block_ids


def test_missing_assignment_tracking_pack_is_an_honest_gap_not_a_fallback(monkeypatch):
    """Mirrors the kernel's existing 'no hallucinated backend fallback' guarantee
    (9E/10E) for this specific new capability, rather than assuming it holds."""
    from src.domain.app_blocks.catalogs import default_known_packs

    registry = load_default_registry()
    monkeypatch.setattr(
        compiler,
        "select_capabilities",
        lambda _intent: [("workflow_assignment", "workflow.assignment", "Selected.")],
    )
    plan = compiler.compile_install_plan(
        None, registry=registry, known_packs=default_known_packs() - {"assignment-tracking"}
    )
    assert plan.unsatisfiable
    assert any(
        g.type == "feature_pack_missing"
        and g.id == "assignment-tracking"
        and g.severity == "build_blocking"
        for g in plan.gaps
    )
