"""FL-D2 reusable presentation variants. Not Reporting. Not Workflow. Not D3."""

from src.domain.app_blocks import compiler
from src.domain.app_blocks.canonical import CANONICAL_BLOCK_IDS
from src.domain.app_blocks.job_packs import JOB_FEATURE_PACKS, known_job_pack_ids
from src.domain.app_blocks.presentation_render import (
    booking_calendar,
    booking_staff_table,
    compact_dashboard,
    kanban_grouping,
    portal_read_only_detail,
    relationship_view,
    split_list_detail,
    status_grouping,
)
from src.domain.app_blocks.presentation_variants import (
    CODEGEN_SURFACE_RESOLUTION,
    FORBIDDEN_PRODUCT_TABLE_BLOCKS,
    HELD_PORTAL_BLOCKS,
    VARIANT_OWNERS,
    presentations_on,
    resolve_codegen_surface,
    variant_catalog,
)
from src.domain.app_blocks.registry import load_default_registry
from src.domain.codegen.prompt_wrapper import build_system_prompt
from src.domain.starter_library.archetypes import known_archetype_ids
from src.domain.starter_library.catalog import known_contract_ids

RECORDS = [
    {"id": "1", "status": "open", "subject_entity_key": "case_record", "title": "A"},
    {"id": "2", "status": "open", "subject_entity_key": "lead", "title": "B"},
    {"id": "3", "status": "closed", "subject_entity_key": "case_record", "title": "C"},
]


def test_variant_registry_is_authored_and_capability_free():
    catalog = variant_catalog()
    assert set(catalog) == set(VARIANT_OWNERS)
    for item in catalog.values():
        assert item.owner_block in CANONICAL_BLOCK_IDS
        assert item.component_contract in known_contract_ids()
        assert item.new_capability_required is False
        assert item.id not in CANONICAL_BLOCK_IDS
        assert item.id not in known_job_pack_ids()


def test_generic_variants_are_consumed_by_more_than_one_product_block():
    registry = load_default_registry()
    owners = {
        "status_grouping": [],
        "split_list_detail": [],
        "compact_dashboard": [],
        "kanban_grouping": [],
        "relationship_view": [],
        "portal_read_only": [],
    }
    for manifest in registry.all():
        kinds = presentations_on(manifest)
        for kind in owners:
            if kind in kinds:
                owners[kind].append(manifest.id)
    assert "admin.record_table" in owners["status_grouping"]
    assert {"crm.lead", "case.management", "support.ticket"} <= set(owners["status_grouping"])
    assert {"crm.lead", "case.management"} <= set(owners["split_list_detail"])
    assert {"crm.lead", "crm.customer"} <= set(owners["compact_dashboard"])
    assert {"crm.lead", "crm.pipeline"} <= set(owners["kanban_grouping"])
    assert {"files.attachments", "task.management"} <= set(owners["relationship_view"])
    assert "admin.record_detail" in owners["portal_read_only"]
    assert "case.management" in owners["portal_read_only"]
    assert not (HELD_PORTAL_BLOCKS & set(owners["portal_read_only"]))


def test_status_grouping_and_split_and_dashboard_and_kanban_render():
    grouped = status_grouping(RECORDS)
    assert set(grouped) == {"open", "closed"}
    assert len(grouped["open"]) == 2
    split = split_list_detail(RECORDS, selected_id="1", detail=RECORDS[0])
    assert split["list"] == RECORDS
    assert split["detail"]["title"] == "A"
    dash = compact_dashboard(RECORDS)
    assert dash["record_count"] == 3
    assert dash["group_counts"]["open"] == 2
    assert "timeseries" not in dash
    assert "analytics" not in dash
    board = kanban_grouping(RECORDS)
    assert set(board["columns"]) == {"open", "closed"}
    assert board["applied_move"] is None


def test_kanban_move_requires_existing_status_contract():
    try:
        kanban_grouping(RECORDS, move={"id": "1", "status": "closed"})
        raise AssertionError("expected missing patch contract")
    except ValueError as exc:
        assert "existing status patch" in str(exc)
    applied = kanban_grouping(
        RECORDS,
        move={"id": "1", "status": "closed"},
        existing_status_patch=lambda payload: {"patched": payload},
    )
    assert applied["applied_move"]["patched"]["status"] == "closed"


def test_relationship_and_portal_and_booking_render_from_authored_contracts():
    related = relationship_view(RECORDS, field="subject_entity_key", value="case_record")
    assert [item["id"] for item in related] == ["1", "3"]
    portal = portal_read_only_detail(
        {"id": "1", "title": "A", "status": "open"},
        visible_fields=["id", "title"],
        mutation_controls=["edit", "patch"],
    )
    assert portal["read_only"] is True
    assert portal["mutation_controls"] == []
    assert "status" not in portal["record"]
    table = booking_staff_table(
        [
            {
                "name": "Chair 1",
                "resource_key": "chair-1",
                "duration_minutes": 30,
                "timezone": "UTC",
                "status": "active",
            }
        ]
    )
    assert table["columns"][0] == "name"
    assert table["rows"][0]["resource_key"] == "chair-1"
    calendar = booking_calendar(
        [{"starts_at": "2026-09-17T10:00:00Z", "ends_at": "2026-09-17T10:30:00Z"}]
    )
    assert "2026-09-17" in calendar["days"]


def test_booking_and_files_authored_slots_close_d2_product_gaps():
    registry = load_default_registry()
    booking = registry.require("booking.appointment")
    kinds = presentations_on(booking)
    assert "staff_table" in kinds
    assert "calendar" in kinds
    assert "data.entity" not in {dep.id for dep in booking.requires}
    files = registry.require("files.attachments")
    assert any(slot.component_contract == "file.upload" for slot in files.slots)
    lead = registry.require("crm.lead")
    assert {slot.component_contract for slot in lead.slots} >= {"record.create", "record.edit"}
    case = registry.require("case.management")
    assert {slot.component_contract for slot in case.slots} >= {"record.create", "record.edit"}
    pipeline = registry.require("crm.pipeline")
    assert "record.create" not in {slot.component_contract for slot in pipeline.slots}


def test_forbidden_product_table_blocks_are_not_canonical():
    assert FORBIDDEN_PRODUCT_TABLE_BLOCKS.isdisjoint(CANONICAL_BLOCK_IDS)
    assert "appointment-table" not in CANONICAL_BLOCK_IDS
    assert "reporting" not in known_contract_ids()
    assert "search" not in known_contract_ids()


def test_codegen_resolves_authored_variants_instead_of_private_surfaces():
    for need, expected in CODEGEN_SURFACE_RESOLUTION.items():
        assert resolve_codegen_surface(need) == expected
    prompt = build_system_prompt("nuxt_app", "")
    assert "support.ticket status_grouping" in prompt
    assert "CustomTicketQueue" in prompt
    assert "compact_dashboard summarizes" in prompt
    assert "kanban_grouping is not a workflow engine" in prompt
    assert "Do not admit D3 candidate Feature Packs" in prompt


def test_d3_candidates_still_fail_normal_pack_resolution(monkeypatch):
    admitted = known_job_pack_ids()
    assert len(JOB_FEATURE_PACKS) == len(known_job_pack_ids())
    assert "client_case_management" not in admitted
    assert "repair_operations" not in admitted
    assert len(known_archetype_ids()) == 10
    registry = load_default_registry()
    monkeypatch.setattr(
        compiler,
        "select_capabilities",
        lambda _intent: [("files_attachments", "files.attachments", "Selected.")],
    )
    plan = compiler.compile_install_plan(None, registry=registry)
    assert "file.upload" in plan.starter_contracts
    assert "client_case_management" not in plan.required_feature_packs
