"""Phase 11B2: task.management end-to-end proof.

Composition-kernel proof only (contract layer), matching the 11B1/10E precedent.
No new primitive, App Block ontology, slot/binding semantics, or event mechanism.
Assignment composes via the App Block optional_requires seam (workflow.assignment),
the same mechanism crm.lead already uses for forms.contact -- not a new dependency
kind.

This slice's specific hazard: seeds/function_library/task-create.yaml /
task-list.yaml already create an ungoverned "task" entity (no access_policy, no
schema.workflow, executed by a service-token caller that bypasses
checkAccessPolicy entirely) on any project where approval-workflow's
function_slugs ran. task-management deliberately uses entity_key "platform_task"
instead. The tests below prove the new capability never resolves to, requires,
or shares state with that legacy path.
"""
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
from src.domain.feature_packs.catalog_v2 import CATALOG_OVERLAYS
from src.domain.feature_packs.normalize import load_pack_contract


def test_planner_matches_tight_task_phrases_only():
    hit = select_capabilities(ProductIntent(purpose="We need an internal task board for staff"))
    assert any(block_id == "task.management" for _cap, block_id, _reason in hit)

    # Bare "task"/"tasks" is far too common to match as a bag of words.
    miss = select_capabilities(ProductIntent(purpose="Our task is to help students enroll"))
    assert not any(block_id == "task.management" for _cap, block_id, _reason in miss)


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


def test_public_label_exists_and_does_not_leak_the_raw_id():
    label = _BLOCK_COPY["task.management"]
    assert label == "Task tracking"
    assert label != "task.management"


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


def test_workflow_assignment_is_observed_optional_not_auto_installed(monkeypatch):
    """Proves the assignment/task composition seam works (task_feature_pack +
    subject_reference_reusable) without task.management hard-requiring
    workflow.assignment -- the same optional_requires contract crm.lead already
    uses for forms.contact (9C: observed, never auto-installed)."""
    registry = load_default_registry()
    monkeypatch.setattr(
        compiler,
        "select_capabilities",
        lambda _intent: [("task_management", "task.management", "Selected.")],
    )
    plan = compiler.compile_install_plan(None, registry=registry)
    task_block = next(b for b in plan.blocks if b.id == "task.management")
    assert any(
        opt.kind == "app_block" and opt.id == "workflow.assignment" and opt.status == "satisfied"
        for opt in task_block.optional_requirements
    )
    # Never auto-installed: selecting only task.management must not pull
    # workflow.assignment (or its pack) into the required install set.
    assert "workflow.assignment" not in {b.id for b in plan.blocks}
    assert "assignment-tracking" not in plan.required_feature_packs


def test_missing_task_management_pack_is_an_honest_gap_not_a_fallback(monkeypatch):
    from src.domain.app_blocks.catalogs import default_known_packs

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


def test_task_management_never_resolves_to_the_legacy_task_entity_or_functions():
    """The seam most likely to create split authority: task.management must not
    require, reference, or provision anything from the pre-Phase-9
    task-create/task-list function library."""
    contract = load_pack_contract(
        "task-management",
        {"requires": ["workflow.transition"], "entities": []},
    )
    assert contract.required_primitive_ids() == ["workflow.transition"]
    entity_keys = {e.entity_key for e in contract.entities} | {
        w.entity_key for w in contract.workflows
    }
    assert entity_keys == {"platform_task"}
    assert "task" not in entity_keys

    raw = CATALOG_OVERLAYS["task-management"]
    assert raw.get("function_slugs") in (None, [])
    workflow_entity_keys = {wf.entity_key for wf in raw["workflows"]}
    assert workflow_entity_keys == {"platform_task"}


def test_stamp_adds_task_status_graph():
    from src.domain.feature_packs.normalize import stamp_composition

    stamped = stamp_composition(
        "task-management",
        {
            "requires": [],
            "entities": [
                {
                    "entity_key": "platform_task",
                    "schema": {
                        "fields": [
                            {
                                "key": "status",
                                "type": "select",
                                "options": ["todo", "in_progress", "done"],
                            }
                        ]
                    },
                }
            ],
        },
    )
    workflow = stamped["entities"][0]["schema"]["workflow"]
    assert workflow["transitions"]["todo"] == ["in_progress", "done"]
    assert workflow["transitions"]["done"] == ["in_progress"]
    events = stamped["entities"][0]["schema"]["domain_events"]
    ids = [item["id"] for item in events]
    assert ids == ["task.created", "task.status_changed", "task.completed"]
