"""Fast Lane batch crm_intake_01 — Vertical Recipes over admitted App Blocks."""

from src.domain.app_blocks.compiler import compile_install_plan
from src.domain.app_blocks.execution import assert_install_plan_installable
from src.domain.app_blocks.planner import (
    ProductIntent,
    default_slots,
    product_intent_from_spec,
    select_capabilities,
)
from src.domain.app_blocks.public import public_composition
from src.domain.app_blocks.registry import load_default_registry
from src.domain.app_blocks.vertical_recipes import (
    load_recipes,
    match_recipe_for_intent,
    match_vertical_recipe,
    validate_recipes,
)
from src.domain.projects.discovery_schemas import (
    Actor,
    AuthenticationSpec,
    DiscoverySpec,
    Entity,
    ProductDefinition,
    TenancySpec,
    Workflow,
)


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


def _spec(*, purpose: str, goal: str, entity: str, entity_id: str = "record") -> DiscoverySpec:
    return DiscoverySpec(
        product=ProductDefinition(
            name="First build",
            name_status="working",
            purpose=purpose,
            first_build_goal=goal,
        ),
        archetypes=["operations"],
        actors=[
            Actor(id="visitor", name="Website visitor"),
            Actor(id="staff", name="Staff counselor"),
        ],
        workflows=[
            Workflow(id="intake", name="Intake", description=goal),
            Workflow(id="review", name="Staff review", description="Staff review submitted records"),
        ],
        entities=[Entity(id=entity_id, name=entity, description=entity)],
        tenancy=TenancySpec(model="single_workspace"),
        authentication=AuthenticationSpec(required=False),
    )


def test_recipes_only_compose_admitted_implemented_blocks():
    problems = validate_recipes()
    assert problems == []
    recipes = load_recipes()
    assert {recipe.id for recipe in recipes} >= {
        "sales_crm",
        "agency_leads",
        "consulting_clients",
        "student_admissions",
        "recruitment_pipeline",
        "property_enquiries",
        "legal_intake",
        "tax_return_cases",
        "immigration_cases",
        "insurance_ops_cases",
        "client_onboarding_ops",
        "grant_award_cases",
        "permit_review_cases",
        "complaint_investigation_cases",
        "loan_origination_cases",
        "quality_nonconformance_cases",
        "clinic_booking",
        "consultant_booking",
        "tutor_scheduling",
        "salon_booking",
        "home_service_booking",
        "professional_booking",
        "contractor_quote_crm",
        "course_catalog",
        "service_catalog",
        "internal_service_directory",
        "it_service_desk",
        "facilities_requests",
        "hr_people_ops",
        "vendor_issues",
        "customer_success_queue",
        "content_publishing_approval",
        "discount_exception_approval",
        "access_grant_approval",
        "vendor_onboarding_approval",
        "refund_exception_approval",
        "construction_punch_list",
        "restaurant_opening_checklist",
        "warehouse_pick_list",
        "marketing_campaign_checklist",
        "field_inspection_checklist",
        "shift_handover_notes",
        "meeting_minutes_notebook",
        "interview_notes_workspace",
        "field_report_notes",
        "after_action_notes",
    }
    registry = load_default_registry()
    for recipe in recipes:
        for block_id in recipe.required_app_blocks:
            assert registry.require(block_id).implemented


def test_each_live_recipe_compiles_an_installable_plan():
    live = load_recipes(composition_ready_only=True)
    assert {recipe.id for recipe in live} >= {
        "sales_crm",
        "student_admissions",
        "legal_intake",
        "tax_return_cases",
        "immigration_cases",
        "insurance_ops_cases",
        "client_onboarding_ops",
        "grant_award_cases",
        "permit_review_cases",
        "complaint_investigation_cases",
        "loan_origination_cases",
        "quality_nonconformance_cases",
        "it_service_desk",
        "facilities_requests",
        "hr_people_ops",
        "vendor_issues",
        "customer_success_queue",
        "content_publishing_approval",
        "discount_exception_approval",
        "access_grant_approval",
        "vendor_onboarding_approval",
        "refund_exception_approval",
        "construction_punch_list",
        "restaurant_opening_checklist",
        "warehouse_pick_list",
        "marketing_campaign_checklist",
        "field_inspection_checklist",
        "shift_handover_notes",
        "meeting_minutes_notebook",
        "interview_notes_workspace",
        "field_report_notes",
        "after_action_notes",
        "clinic_booking",
        "consultant_booking",
        "tutor_scheduling",
        "salon_booking",
        "home_service_booking",
        "professional_booking",
        "contractor_quote_crm",
        "course_catalog",
        "service_catalog",
        "internal_service_directory",
    }
    for recipe in live:
        intent = ProductIntent(
            purpose=recipe.intent_phrases[0],
            first_build_goal=recipe.intent_phrases[0],
            actor_names=["Staff"],
            staff_review=True,
        )
        plan = compile_install_plan(
            DiscoverySpec(
                product=ProductDefinition(
                    purpose=recipe.intent_phrases[0],
                    first_build_goal=f"Ship {recipe.name}",
                ),
                archetypes=["operations"],
                actors=[Actor(id="staff", name="Staff")],
                workflows=[Workflow(id="run", name="Run", description=recipe.intent_phrases[0])],
                entities=[Entity(id="record", name="Record", description="Record")],
                tenancy=TenancySpec(model="single_workspace"),
                authentication=AuthenticationSpec(required=False),
            )
        )
        assert_install_plan_installable(plan)
        selected = {block.id for block in plan.blocks}
        assert set(recipe.required_app_blocks) <= selected
        assert match_recipe_for_intent(intent).id == recipe.id


def test_student_admissions_selects_canonical_crm_not_a_student_backend():
    intent = ProductIntent(
        purpose="Build a student admissions CRM for counselors",
        first_build_goal="Track university applications through an admissions pipeline",
        entity_names=["Student", "Application"],
        staff_review=True,
    )
    ids = _ids(intent)
    assert "crm.customer" in ids
    assert "crm.pipeline" in ids
    assert "case.management" in ids
    assert "student_pipeline" not in ids
    assert "legal_customer" not in ids
    registry = load_default_registry()
    slots = default_slots(registry.require("crm.customer"), intent)
    assert slots["heading"] == "Students"
    assert slots["empty_state"] == "No students yet"
    case_slots = default_slots(registry.require("case.management"), intent)
    assert case_slots["heading"] == "Applications"


def test_student_admissions_public_copy_uses_recipe_labels():
    spec = _spec(
        purpose="Student admissions CRM for a consultancy",
        goal="Track university applications in an admissions pipeline",
        entity="Student application",
        entity_id="application",
    )
    composition = public_composition(spec.model_dump())
    assert "Student records" in composition["building"]
    assert "Admissions pipeline" in composition["building"]
    assert "Application tracking" in composition["building"]


def test_recipe_phrases_do_not_steal_generic_contact_or_quote():
    assert match_vertical_recipe("allow visitors to send a contact form message") is None
    assert match_vertical_recipe("website for a roofing company where visitors request a quote") is None
    contact_ids = _ids(ProductIntent(purpose="Public contact form for website visitors"))
    assert "crm.customer" not in contact_ids
    assert "crm.pipeline" not in contact_ids


def test_recipe_phrases_do_not_collide_with_13a_corpus():
    from pathlib import Path

    from scripts.measure_generation_reliability_13a import corpus

    rows = corpus(Path(__file__).resolve().parents[1] / "scripts/phase_13a_corpus.txt")
    collisions = []
    all_recipes = load_recipes()
    for row in rows:
        haystack = row["prompt"].lower()
        for recipes in (None, all_recipes):
            recipe = match_vertical_recipe(haystack, recipes)
            if recipe is not None:
                collisions.append((row["id"], recipe.id, row["prompt"]))
    assert collisions == []


def test_recipe_first_phrases_are_unique():
    phrases = [recipe.intent_phrases[0] for recipe in load_recipes()]
    assert len(phrases) == len(set(phrases))


def test_legal_intake_composes_case_and_crm_without_a_legal_store():
    intent = ProductIntent(
        purpose="Legal intake CRM for a law firm",
        first_build_goal="Capture legal client intake and track matters",
        staff_review=True,
    )
    ids = _ids(intent)
    assert {"forms.contact", "crm.customer", "crm.pipeline", "case.management", "documents"} <= ids
    registry = load_default_registry()
    assert default_slots(registry.require("case.management"), intent)["heading"] == "Matters"


def test_tax_return_tracker_uses_case_not_a_tax_engine():
    intent = ProductIntent(
        purpose="Tax return tracker for an accounting practice",
        first_build_goal="Run a tax return workflow with filing tasks",
        staff_review=True,
    )
    ids = _ids(intent)
    assert {"case.management", "crm.customer", "documents", "task.management"} <= ids
    assert "tax_return" not in ids
    registry = load_default_registry()
    assert default_slots(registry.require("case.management"), intent)["heading"] == "Tax returns"


def test_grant_award_tracker_uses_case_not_a_grants_engine():
    intent = ProductIntent(
        purpose="Grant award tracker for foundation grant files",
        first_build_goal="Run a grant funding caseload for staff reviewers",
        staff_review=True,
    )
    ids = _ids(intent)
    assert {"forms.contact", "case.management", "crm.customer", "documents"} <= ids
    assert "grant_portal" not in ids
    assert "booking.appointment" not in ids
    assert "catalog.product" not in ids
    assert "support.ticket" not in ids
    assert "workflow.approval" not in ids
    registry = load_default_registry()
    assert default_slots(registry.require("case.management"), intent)["heading"] == "Grant awards"
    composition = public_composition(
        _spec(
            purpose="Grant award tracker for foundation grant files",
            goal="Run a grant funding caseload for staff reviewers",
            entity="Grant award",
        ).model_dump()
    )
    assert "Grant award tracking" in composition["building"]
    assert "Grantee records" in composition["building"]


def test_generic_case_gold_does_not_match_case_ops_09_recipes():
    assert match_vertical_recipe(
        "our service team needs case management. staff should inspect cases and move their status"
    ) is None
    assert match_vertical_recipe(
        "an insurance claim review app for staff. they inspect each claim and change its workflow status"
    ) is None
    assert match_vertical_recipe(
        "i need an application tracking tool for our enrollment office"
    ) is None
    ids = _ids(
        ProductIntent(
            purpose="Our service team needs case management. Staff inspect cases.",
            staff_review=True,
        )
    )
    assert "case.management" in ids
    assert "crm.customer" not in ids


def test_case_ops_09_negation_skips_recipe_extras():
    ids = _ids(
        ProductIntent(
            purpose="Grant award tracker",
            source_prompt="Grant award tracker. Do not add cases.",
            staff_review=True,
        )
    )
    assert "case.management" not in ids
    assert "crm.customer" in ids


def test_live_booking_and_catalog_recipes_are_selectable():
    live_ids = {recipe.id for recipe in load_recipes(composition_ready_only=True)}
    assert "clinic_booking" in live_ids
    assert "course_catalog" in live_ids
    matched = match_recipe_for_intent(
        ProductIntent(purpose="Medical clinic booking with clinic appointments")
    )
    assert matched is not None
    assert matched.id == "clinic_booking"
    catalog = match_recipe_for_intent(ProductIntent(purpose="Course catalog for a training company"))
    assert catalog is not None
    assert catalog.id == "course_catalog"
    clinic_ids = _ids(ProductIntent(purpose="Medical clinic booking with clinic appointments"))
    assert {"booking.appointment", "forms.contact", "crm.customer"} <= clinic_ids


def test_generic_staff_booking_does_not_steal_clinic_recipe():
    assert match_vertical_recipe(
        "a staff-only appointment booking website for a clinic. receptionists reserve a room"
    ) is None
    assert match_vertical_recipe(
        "our salon receptionist needs to book an appointment for a customer"
    ) is None
    assert match_vertical_recipe(
        "an enquiry page for a language tutor. anybody may submit it"
    ) is None


def test_live_planner_uses_initial_prompt_when_spec_is_paraphrased():
    spec = DiscoverySpec(
        product=ProductDefinition(
            purpose="Track applications for a consultancy",
            first_build_goal="Staff review submitted records",
        ),
        actors=[Actor(id="staff", name="Staff counselor")],
        workflows=[Workflow(id="review", name="Staff review", description="Review records")],
        entities=[Entity(id="application", name="Application", description="An application")],
        tenancy=TenancySpec(model="single_workspace"),
        authentication=AuthenticationSpec(required=False),
    )
    paraphrased = product_intent_from_spec(spec)
    assert "crm.customer" not in _ids(paraphrased)
    live = product_intent_from_spec(
        spec, source_prompt="Build a student admissions CRM for counselors"
    )
    ids = _ids(live)
    assert {"crm.customer", "crm.pipeline", "case.management"} <= ids
    plan = compile_install_plan(
        spec, source_prompt="Build a student admissions CRM for counselors"
    )
    assert_install_plan_installable(plan)
    composition = public_composition(
        spec.model_dump(), source_prompt="Build a student admissions CRM for counselors"
    )
    assert "Student records" in composition["building"]


def test_public_discovery_passes_initial_prompt_into_composition():
    from src.domain.projects.discovery_schemas import public_discovery

    spec = DiscoverySpec(
        product=ProductDefinition(
            purpose="Track applications for a consultancy",
            first_build_goal="Staff review submitted records",
        ),
        actors=[Actor(id="staff", name="Staff counselor")],
        workflows=[Workflow(id="review", name="Staff review", description="Review records")],
        entities=[Entity(id="application", name="Application", description="An application")],
        tenancy=TenancySpec(model="single_workspace"),
        authentication=AuthenticationSpec(required=False),
    )
    view = public_discovery(
        {
            "status": "ready",
            "version": 1,
            "initial_prompt": "Student admissions CRM with an admissions pipeline",
            "result": {"spec": spec.model_dump(), "summary": "Track applications"},
        }
    )
    assert view.first_build is not None
    assert "Student records" in view.first_build.composition.building


def test_negation_guards_retain_precedence_over_recipe_extras():
    ids = _ids(
        ProductIntent(
            purpose="Student admissions CRM",
            source_prompt="Student admissions CRM. Do not add customer records.",
            staff_review=True,
        )
    )
    assert "crm.customer" not in ids
    assert "case.management" in ids


def test_it_service_desk_composes_support_ticket_not_a_helpdesk_store():
    intent = ProductIntent(
        purpose="IT service desk tracker for workplace IT requests",
        first_build_goal="Run desktop support operations for employees",
        staff_review=True,
    )
    ids = _ids(intent)
    assert {"support.ticket", "crm.customer", "task.management"} <= ids
    assert "helpdesk" not in ids
    assert "it_ticket" not in ids
    registry = load_default_registry()
    assert default_slots(registry.require("support.ticket"), intent)["heading"] == "IT requests"
    assert default_slots(registry.require("crm.customer"), intent)["heading"] == "Employees"
    composition = public_composition(
        _spec(
            purpose="IT service desk tracker for workplace IT requests",
            goal="Run desktop support operations for employees",
            entity="IT request",
        ).model_dump()
    )
    assert "IT request queue" in composition["building"]
    assert "Employee requesters" in composition["building"]


def test_generic_support_ticket_prompt_does_not_match_a_vertical_recipe():
    assert match_vertical_recipe(
        "we need a support ticket system so our staff can track customer issues"
    ) is None
    assert match_vertical_recipe(
        "create an internal ticket queue for it support. employees sign in"
    ) is None
    ids = _ids(
        ProductIntent(
            purpose="We need a support ticket system so our staff can track customer issues",
            staff_review=True,
        )
    )
    assert "support.ticket" in ids
    assert "crm.customer" not in ids


def test_support_ticket_negation_skips_recipe_extras():
    ids = _ids(
        ProductIntent(
            purpose="IT service desk tracker",
            source_prompt="IT service desk tracker. Do not add support tickets.",
            staff_review=True,
        )
    )
    assert "support.ticket" not in ids
    assert "crm.customer" in ids


def test_content_publishing_approval_composes_workflow_approval_not_a_cms():
    intent = ProductIntent(
        purpose="Content publishing approval for editorial publish approval",
        first_build_goal="Run content release approval for authors",
        staff_review=True,
    )
    ids = _ids(intent)
    assert {"workflow.approval", "crm.customer", "documents"} <= ids
    assert "content_approval" not in ids
    registry = load_default_registry()
    assert default_slots(registry.require("workflow.approval"), intent)["heading"] == "Publish approvals"
    composition = public_composition(
        _spec(
            purpose="Content publishing approval for editorial publish approval",
            goal="Run content release approval for authors",
            entity="Publish request",
        ).model_dump()
    )
    assert "Publish approval queue" in composition["building"]
    assert "Author records" in composition["building"]


def test_expense_and_time_off_gold_do_not_match_approval_recipes():
    assert match_vertical_recipe(
        "we need an approval workflow so managers can approve or reject requests submitted by staff"
    ) is None
    assert match_vertical_recipe(
        "create a system where expense requests require approval from an admin before they're marked resolved"
    ) is None
    assert match_vertical_recipe(
        "we want an approval workflow for time-off requests: staff submit, managers approve or reject"
    ) is None
    ids = _ids(
        ProductIntent(
            purpose="We need an approval workflow so managers can approve or reject requests",
            staff_review=True,
        )
    )
    assert "workflow.approval" in ids
    assert "crm.customer" not in ids
    assert "documents" not in ids


def test_approval_negation_skips_recipe_extras():
    ids = _ids(
        ProductIntent(
            purpose="Content publishing approval",
            source_prompt="Content publishing approval. Do not add approval requests.",
            staff_review=True,
        )
    )
    assert "workflow.approval" not in ids
    assert "crm.customer" in ids


def test_construction_punch_list_composes_tasks_not_a_construction_store():
    intent = ProductIntent(
        purpose="Construction punch list tracker for site punch list tracker work",
        first_build_goal="Run a construction punchlist tracker for contractors",
        staff_review=True,
    )
    ids = _ids(intent)
    assert {"task.management", "workflow.assignment", "crm.customer"} <= ids
    assert "punch_list" not in ids
    registry = load_default_registry()
    assert default_slots(registry.require("task.management"), intent)["heading"] == "Punch items"
    composition = public_composition(
        _spec(
            purpose="Construction punch list tracker for site punch list tracker work",
            goal="Run a construction punchlist tracker for contractors",
            entity="Punch item",
        ).model_dump()
    )
    assert "Punch list" in composition["building"]
    assert "Contractor records" in composition["building"]


def test_generic_task_tracking_gold_does_not_match_task_recipes():
    assert match_vertical_recipe("task tracking for staff, with status updates. one workspace.") is None
    assert match_vertical_recipe(
        "we want a private case management app and an ops checklist"
    ) is None
    ids = _ids(
        ProductIntent(
            purpose="Task tracking for staff, with status updates. One workspace.",
            staff_review=True,
        )
    )
    assert "task.management" in ids
    assert "crm.customer" not in ids


def test_task_negation_skips_recipe_extras():
    ids = _ids(
        ProductIntent(
            purpose="Construction punch list tracker",
            source_prompt="Construction punch list tracker. Do not add tasks.",
            staff_review=True,
        )
    )
    assert "task.management" not in ids
    assert "crm.customer" in ids


def test_shift_handover_notes_compose_documents_not_a_wiki():
    intent = ProductIntent(
        purpose="Shift handover notes for an end-of-shift notes notebook",
        first_build_goal="Keep a shift handover notebook for shift leads",
        staff_review=True,
    )
    ids = _ids(intent)
    assert {"documents", "crm.customer", "task.management"} <= ids
    assert "handover" not in ids
    registry = load_default_registry()
    assert default_slots(registry.require("documents"), intent)["heading"] == "Handover notes"
    composition = public_composition(
        _spec(
            purpose="Shift handover notes for an end-of-shift notes notebook",
            goal="Keep a shift handover notebook for shift leads",
            entity="Handover note",
        ).model_dump()
    )
    assert "Handover notes" in composition["building"]
    assert "Shift-lead records" in composition["building"]


def test_generic_document_gold_does_not_match_document_recipes():
    assert match_vertical_recipe(
        "create internal documents for our staff: title, body content and draft or published status"
    ) is None
    assert match_vertical_recipe(
        "a document library for employees, where each item has a title and editable text"
    ) is None
    ids = _ids(
        ProductIntent(
            purpose="Create internal documents for our staff: title, body content and draft or published status",
            staff_review=True,
        )
    )
    assert "documents" in ids
    assert "crm.customer" not in ids


def test_documents_negation_skips_recipe_extras():
    ids = _ids(
        ProductIntent(
            purpose="Shift handover notes",
            source_prompt="Shift handover notes. Do not add documents.",
            staff_review=True,
        )
    )
    assert "documents" not in ids
    assert "crm.customer" in ids
