"""PROMPT_ALIGNMENT_01: Discovery understands intent; Codegen implements composition.

Does not change Fast Lane recipe files, 13A/B2 gold, or planner negation rules.
"""

from pathlib import Path

import pytest
from pydantic import ValidationError
from src.domain.app_blocks.planner import (
    product_intent_from_spec,
    select_capabilities,
)
from src.domain.app_blocks.vertical_recipes import (
    RECIPE_DIR,
    intent_corpus,
    match_recipe_for_intent,
)
from src.domain.codegen.prompt_wrapper import build_plan_system_prompt, build_system_prompt
from src.domain.projects.discovery_readiness import compile_discovery_brief
from src.domain.projects.discovery_schemas import (
    DiscoverySpec,
    Entity,
    ProductDefinition,
    Workflow,
)
from src.integrations.project_intake_client import COMPILATION_PROMPT, DISCOVERY_PROMPT

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


def _ids(spec: DiscoverySpec, *, source_prompt: str = "") -> set[str]:
    return {
        block_id
        for _cap, block_id, _reason in select_capabilities(
            product_intent_from_spec(spec, source_prompt=source_prompt)
        )
    }


def test_discovery_prompt_preserves_intent_and_stays_architecture_neutral():
    assert "INTENT FIDELITY" in DISCOVERY_PROMPT
    assert "Preserve the customer's original intent" in DISCOVERY_PROMPT
    assert "student admissions CRM" in DISCOVERY_PROMPT
    assert "legal intake" in DISCOVERY_PROMPT
    assert "do not add CRM/customer records" in DISCOVERY_PROMPT
    assert "requires_configuration" in DISCOVERY_PROMPT
    assert "initial_prompt" in DISCOVERY_PROMPT
    assert "unavailable without a new backend" in DISCOVERY_PROMPT
    assert "do not invent a substitute" in DISCOVERY_PROMPT
    assert "Never decide that a vertical needs a special backend" in DISCOVERY_PROMPT
    assert "Never invent App Blocks, Feature Packs" in DISCOVERY_PROMPT
    assert "Discovery is not the" in DISCOVERY_PROMPT
    assert "planner; Core matches recipes" in DISCOVERY_PROMPT


def test_codegen_prompt_treats_composition_as_architecture_authority():
    prompt = build_system_prompt(project_type="nuxt_app", instruction_content="")
    plan = build_plan_system_prompt(project_type="nuxt_app", instruction_content="")
    for text in (prompt, plan):
        assert "Do not reinterpret the customer's request into architecture" in text
        assert "ARCHITECTURE AUTHORITY" in text
        assert "student_customer table" in text
        assert "crm.customer / case.management / workflow.assignment" in text
        assert "installed → implement" in text
        assert "configuration_required → generate the UI affordance" in text
        assert "unavailable / gap → do not implement a substitute" in text
    assert "do not invent undeclared backends" in prompt
    assert "Honor installed, configuration_required, and unavailable/gap states" in prompt


def test_compilation_prompt_is_not_an_alternate_planner():
    assert "Compilation is not a planner" in COMPILATION_PROMPT
    assert "must not invent App Blocks" in COMPILATION_PROMPT


def test_schema_captures_structured_signals_without_platform_ids():
    spec = DiscoverySpec.model_fields
    assert "customer's own product phrasing" in (spec["original_intent"].description or "")
    assert "Never a backend" in (spec["domain"].description or "")
    assert "Not a canonical platform identity" in (
        spec["requested_product_shape"].description or ""
    )
    assert "negation words" in (spec["explicit_exclusions"].description or "")
    with pytest.raises(ValidationError):
        DiscoverySpec.model_validate({"app_blocks": ["crm.customer"]})


def test_over_normalized_purpose_recovers_via_original_intent():
    paraphrased = DiscoverySpec(
        product=ProductDefinition(
            purpose="track applications for a consultancy",
            first_build_goal="track applications for a consultancy",
        ),
        entities=[Entity(id="record", name="applications", description="applications")],
        workflows=[
            Workflow(
                id="track",
                name="application tracking",
                description="track applications for a consultancy",
            )
        ],
    )
    recovered = paraphrased.model_copy(
        update={
            "original_intent": "student admissions CRM",
            "domain": "student admissions",
            "requested_product_shape": "CRM",
            "forms": ["admissions form"],
        }
    )
    assert match_recipe_for_intent(product_intent_from_spec(paraphrased)) is None
    matched = match_recipe_for_intent(product_intent_from_spec(recovered))
    assert matched is not None
    assert matched.id == "student_admissions"
    ids = _ids(recovered)
    assert "crm.customer" in ids
    assert "case.management" in ids
    assert "student_pipeline" not in ids
    assert "legal_customer" not in ids


def test_empty_structured_evidence_does_not_change_corpus():
    intent = product_intent_from_spec(
        DiscoverySpec(product=ProductDefinition(purpose="Staff product catalog"))
    )
    assert intent.original_intent == ""
    assert intent.domain == ""
    assert intent.form_names == []
    expected = " ".join(
        [
            intent.source_prompt,
            intent.purpose,
            intent.first_build_goal,
            *intent.workflow_names,
            *intent.workflow_descriptions,
            *intent.entity_names,
            *intent.entity_descriptions,
            *intent.entity_lifecycles,
            *intent.archetypes,
            *intent.target_audience,
            *intent.access_scopes,
            *intent.requirement_lines,
        ]
    ).lower()
    assert intent_corpus(intent) == expected


def test_explicit_exclusions_use_existing_negation_without_changing_it():
    wants_customers = DiscoverySpec(
        product=ProductDefinition(purpose="customer records for the firm")
    )
    excluded = wants_customers.model_copy(
        update={"explicit_exclusions": ["do not add customer records"]}
    )
    assert "crm.customer" in _ids(wants_customers)
    assert "crm.customer" not in _ids(excluded)


def test_13a_corpus_still_does_not_collide_with_recipes():
    from scripts.measure_generation_reliability_13a import corpus
    from src.domain.app_blocks.vertical_recipes import load_recipes, match_vertical_recipe

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


def test_fast_lane_recipe_catalog_files_are_untouched():
    names = sorted(path.name for path in RECIPE_DIR.glob("*.yaml"))
    assert names == [
        "approval_ops_06.yaml",
        "booking_03.yaml",
        "case_ops_02.yaml",
        "catalog.v1.yaml",
        "catalog_04.yaml",
        "documents_ops_08.yaml",
        "marketing_coverage.yaml",
        "support_ops_05.yaml",
        "task_ops_07.yaml",
    ]


def test_brief_preserves_original_intent_and_exclusions():
    spec = DiscoverySpec(
        product=ProductDefinition(
            name="Admissions",
            name_status="working",
            purpose="track applications for a consultancy",
            first_build_goal="Ship the first admissions tracker",
        ),
        original_intent="student admissions CRM",
        domain="student admissions",
        requested_product_shape="CRM",
        forms=["admissions form"],
        explicit_exclusions=["do not add CRM/customer records"],
        requires_configuration=["connected email for counselor notices"],
        deferred_scope=["public parent portal"],
        entities=[Entity(id="student", name="students", description="applicants")],
    )
    brief = compile_discovery_brief(
        spec,
        {"messages": [{"role": "user", "content": "student admissions CRM"}]},
        product_name="Admissions",
        summary="First admissions tracker",
    )
    assert "## Original intent\nstudent admissions CRM" in brief
    assert "Domain: student admissions" in brief
    assert "Requested product shape: CRM" in brief
    assert "- admissions form" in brief
    assert "- do not add CRM/customer records" in brief
    assert "- connected email for counselor notices" in brief
    assert "architecture authority" in brief
    assert "must not invent backends" in brief


def test_agents_md_states_composition_authority():
    import inspect

    from src.workers.discovery_apply import write_brief_files

    source = inspect.getsource(write_brief_files)
    assert "validated composition is architecture authority" in source
    assert "Do not reinterpret the" in source
    assert "customer's request into architecture" in source
    assert "Honor installed, configuration_required, and unavailable/gap states" in source
    assert "Do not invent a private backend" in source
