"""Capability Model Phase 3 (Tier 2): crm.quote, collaboration.comment,
governance.group, customer_success.account. All four are generic-entity
App Blocks -- no new Cloud primitive, same posture every prior family used
before earning a real primitive later if demand shows up.
"""

from pathlib import Path

import pytest

from src.domain.app_blocks.canonical import CANONICAL_BLOCK_IDS
from src.domain.app_blocks.catalogs import default_known_packs
from src.domain.app_blocks.generated_product_conformance_12h import (
    KernelDeniedError,
    PlatformKernel,
)
from src.domain.app_blocks.planner import ProductIntent, select_capabilities
from src.domain.app_blocks.registry import load_default_registry
from tests.test_phase11_pack_migration_materialization import _captured_composition

ROOT = Path(__file__).resolve().parents[1]

BLOCKS = {
    "crm.quote": (
        "quote-lifecycle", "alembic/versions/152_quote_lifecycle_pack.py", "quote",
    ),
    "collaboration.comment": (
        "comment-threads", "alembic/versions/153_comment_threads_pack.py", "comment",
    ),
    "governance.group": (
        "governance-groups", "alembic/versions/154_governance_groups_pack.py", "governance_group",
    ),
    "customer_success.account": (
        "customer-success-accounts",
        "alembic/versions/155_customer_success_accounts_pack.py",
        "customer_success_account",
    ),
}

POSITIVE_PROMPTS = {
    "crm.quote": "Staff need to track quotes for clients.",
    "collaboration.comment": "Staff need internal comments on cases.",
    "governance.group": "Staff need board management for our nonprofit.",
    "customer_success.account": "Staff track customer onboarding and health.",
}

NEGATIVE_PROMPTS = {
    "crm.quote": "Client records for the firm. Do not add quotes.",
    "collaboration.comment": "Case tracking. No internal notes needed.",
    "governance.group": "Internal tool. No committee management needed.",
    "customer_success.account": "CRM records. No customer success tracking needed.",
}


def _ids(intent: ProductIntent) -> set[str]:
    return {block_id for _cap, block_id, _reason in select_capabilities(intent)}


@pytest.mark.parametrize("block_id", sorted(BLOCKS))
def test_block_is_canonical_generic_entity_without_new_kernel(block_id):
    pack_slug, migration_path, entity_key = BLOCKS[block_id]
    registry = load_default_registry()
    block = registry.require(block_id)
    canonical = registry.canonical.require(block_id)
    assert block.status == "implemented"
    assert block.lifecycle == "stable"
    assert block_id in CANONICAL_BLOCK_IDS
    assert canonical.installation.feature_packs == (pack_slug,)
    primitives = {dep.id for dep in canonical.requires if dep.kind == "primitive"}
    assert primitives == {"data.entity"}
    assert pack_slug in default_known_packs()
    composition = _captured_composition(migration_path, pack_slug)
    entity = composition["entities"][0]
    assert entity["entity_key"] == entity_key


@pytest.mark.parametrize("block_id", sorted(BLOCKS))
def test_positive_intent_selects_block(block_id):
    prompt = POSITIVE_PROMPTS[block_id]
    ids = _ids(ProductIntent(purpose=prompt, source_prompt=prompt, staff_review=True))
    assert block_id in ids, prompt


@pytest.mark.parametrize("block_id", sorted(BLOCKS))
def test_negative_intent_skips_block(block_id):
    prompt = NEGATIVE_PROMPTS[block_id]
    ids = _ids(ProductIntent(purpose=prompt, source_prompt=prompt, staff_review=True))
    assert block_id not in ids, prompt


@pytest.mark.parametrize("block_id", sorted(BLOCKS))
def test_runtime_crud_is_staff_managed_records(block_id):
    _pack_slug, _migration_path, entity_key = BLOCKS[block_id]
    kernel = PlatformKernel(namespace=f"phase3-{block_id}")
    kernel.as_anonymous()
    with pytest.raises(KernelDeniedError):
        kernel.create_record(entity_key, {})
    kernel.as_staff()
    created = kernel.create_record(entity_key, _minimal_payload(entity_key))
    listed = kernel.list_records(entity_key)
    assert listed[0]["id"] == created["id"]


def _minimal_payload(entity_key: str) -> dict:
    return {
        "quote": {"reference": "Q-1", "status": "draft"},
        "comment": {
            "body": "Looks good",
            "related_entity_key": "case_record",
            "related_record_id": "case_1",
            "visibility": "internal",
        },
        "governance_group": {"name": "Board", "status": "active"},
        "customer_success_account": {"account_reference": "CS-1"},
    }[entity_key]


def test_materialized_hosts_use_records_for_all_four_blocks():
    import json

    from src.domain.app_blocks.hosts import merge_host_map

    for framework in ("html", "nuxt_app", "react_app"):
        root = ROOT / "boilerplates" / framework
        hosts = merge_host_map(json.loads((root / "p2hs/app-block-hosts.json").read_text()))
        for block_id, (_pack, _migration_path, entity_key) in BLOCKS.items():
            rows = {h["slot"]: h for h in hosts["hosts"] if h["block"] == block_id}
            assert {"list", "detail", "create", "edit", "empty"} <= set(rows), block_id
            for slot in ("list", "detail", "create", "edit"):
                path = rows[slot]["implementation"].partition("#")[0]
                source = (root / path).read_text(encoding="utf-8")
                assert entity_key in source, (block_id, framework, slot)
