"""CRM admission uses the existing composition, install and Starter contracts."""
from pathlib import Path
import json
from unittest.mock import patch

import pytest

from src.domain.app_blocks.catalogs import FEATURE_PACK_ALIASES, pack_preferred_for_new_projects
from src.domain.app_blocks.compiler import compile_install_plan
from src.domain.app_blocks.hosts import merge_host_map
from src.domain.app_blocks.planner import ProductIntent, select_capabilities
from src.domain.app_blocks.registry import load_default_registry
from src.domain.feature_packs.composer import derive_pack_composition
from src.domain.feature_packs.crm_authorization import staff_policy
from src.domain.feature_packs.ownership import validate_composition_ownership_contracts
from src.domain.feature_packs.requirements_spec import CompositionRequirement
from tests.test_crm_authorization_11e2 import lead_composition
from tests.test_phase11_pack_migration_materialization import _captured_composition

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


def customer_composition():
    return _captured_composition("alembic/versions/144_customer_records_pack.py", "customer-records")


def test_crm_canonical_composition_and_optional_authorities():
    registry = load_default_registry()
    for block, pack in (("crm.customer", "customer-records"), ("crm.pipeline", "lead-lifecycle")):
        with patch("src.domain.app_blocks.compiler.select_capabilities", return_value=[(block, block, "Selected")]):
            plan = compile_install_plan(None, registry=registry)
        assert not plan.unsatisfiable
        assert plan.required_feature_packs == [pack]
        assert "crm-pipeline" not in plan.required_feature_packs
        assert "task-management" not in plan.required_feature_packs
        assert registry.canonical.get(block).installation.feature_packs == (pack,)
    assert "crm.pipeline" not in FEATURE_PACK_ALIASES
    assert FEATURE_PACK_ALIASES["crm.lead"] == FEATURE_PACK_ALIASES["crm.lead_lifecycle"] == "lead-lifecycle"
    assert not pack_preferred_for_new_projects("crm-pipeline")


@pytest.mark.parametrize("text,expected", [
    ("customer records", "crm.customer"), ("customer list", "crm.customer"),
    ("sales pipeline board", "crm.pipeline"), ("lead pipeline board", "crm.pipeline"),
])
def test_positive_planner(text, expected):
    assert expected in {block for _, block, _ in select_capabilities(ProductIntent(purpose=text))}


@pytest.mark.parametrize("text", ["CRM", "sync to CRM", "HubSpot contact sync", "deployment pipeline", "data pipeline board"])
def test_negative_planner(text):
    blocks = {block for _, block, _ in select_capabilities(ProductIntent(purpose=text))}
    assert not blocks.intersection({"crm.customer", "crm.pipeline", "crm.lead"})


def test_persistence_and_authorization_are_stamped_without_new_ownership():
    customer, lead = customer_composition(), lead_composition()
    for composition, key in ((customer, "customer"), (lead, "lead")):
        validate_composition_ownership_contracts(composition)
        assert composition["function_slugs"] == composition["connector_actions"] == []
        assert len(composition["entities"]) == 1
        entity = composition["entities"][0]
        assert entity["entity_key"] == key
        assert entity["schema"]["access_policy"] == staff_policy()
        assert not entity.get("rls_enabled") and not entity.get("owner_managed")
    assert "workflow" not in customer["entities"][0]["schema"]
    assert lead["entities"][0]["schema"]["workflow"]["field"] == "status"
    assert set(f["key"] for f in customer["entities"][0]["schema"]["fields"]) == {
        "name", "email", "phone", "company", "source", "notes", "status",
    }


def test_generated_staff_composition_uses_existing_validator_and_primitives():
    requirement = CompositionRequirement.model_validate({
        "name": "customer", "actors": ["admin", "super_admin"],
        "requirements": [{"action": action, "resource": "customer", "actor": actor}
                         for actor in ("admin", "super_admin") for action in ("create", "read", "update", "delete")],
        "fields": [{"key": "name", "label": "Name", "type": "text", "required": True}],
    })
    derived = derive_pack_composition(requirement, "crm-proof")
    assert derived.entities and not derived.entities[0].rls_enabled


@pytest.mark.parametrize("framework", ["html", "nuxt_app", "react_app"])
def test_materialized_hosts_survive_catalog_refresh_and_resolve_to_real_components(framework):
    root = ROOT / "boilerplates" / framework
    hosts = merge_host_map(json.loads((root / "p2hs/app-block-hosts.json").read_text()))
    crm = [h for h in hosts["hosts"] if h["block"] in ("crm.customer", "crm.pipeline")]
    assert len(crm) == 9
    pipeline = {h["slot"]: h for h in crm if h["block"] == "crm.pipeline"}
    assert pipeline["status"]["component_contract"] == "record.edit"
    for host in crm:
        path, _, export = host["implementation"].partition("#")
        source = (root / path).read_text()
        if export:
            assert f"function {export}" in source
        assert "crm_deal" not in source
        assert "ALLOWED_TRANSITIONS" not in source
