"""Architecture Lane Billing: admit invoice_record independently of Core Stripe.

No new kernel, checkout binding, subscription, or Stripe fields on invoice_record.
"""

from pathlib import Path

import pytest
import yaml
from src.domain.app_blocks.canonical import CANONICAL_BLOCK_IDS, feature_pack_only_aliases
from src.domain.app_blocks.catalogs import FEATURE_PACK_ALIASES, default_known_packs
from src.domain.app_blocks.execution import assert_install_plan_installable
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 src.domain.app_blocks.vertical_recipes import match_vertical_recipe
from src.domain.starter_library.catalog import known_contract_ids
from tests.test_phase11_pack_migration_materialization import _captured_composition

ROOT = Path(__file__).resolve().parents[1]
RECEIPT = ROOT / "src/domain/app_blocks/recipes/billing_freeze.receipt.txt"
AUDIT = ROOT / "src/domain/app_blocks/recipes/billing_architecture_audit.receipt.txt"
REGISTRY = ROOT / "src/domain/app_blocks/platform_coverage_registry.v1.yaml"
GATED = (
    "payments.checkout",
    "membership.portal",
)
STARTER_SLOTS = {
    "list": "record.table",
    "detail": "record.detail",
    "create": "record.create",
    "edit": "record.edit",
    "empty": "state.empty",
}


def billing_composition():
    return _captured_composition("alembic/versions/147_billing_management_pack.py", "billing-management")


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


def test_billing_runtime_is_canonical_without_new_kernel():
    registry = load_default_registry()
    block = registry.require("billing.invoice")
    canonical = registry.canonical.require("billing.invoice")
    assert block.status == "implemented"
    assert "billing.invoice" in CANONICAL_BLOCK_IDS
    assert "billing.invoice" not in FEATURE_PACK_ALIASES
    assert "billing.invoice" not in feature_pack_only_aliases()
    assert canonical.installation.feature_packs == ("billing-management",)
    primitives = {dep.id for dep in canonical.requires if dep.kind == "primitive"}
    assert primitives == {"data.entity"}
    assert "payment-checkout" not in canonical.installation.feature_packs
    assert "saas-membership" not in canonical.installation.feature_packs
    assert "order-management" not in canonical.installation.feature_packs
    assert "billing-management" in default_known_packs()
    composition = billing_composition()
    entity = composition["entities"][0]
    assert entity["entity_key"] == "invoice_record"
    keys = {field["key"] for field in entity["schema"]["fields"]}
    assert keys == {
        "reference",
        "customer_name",
        "customer_email",
        "notes",
        "amount",
        "currency",
        "due_date",
        "status",
    }
    status = next(field for field in entity["schema"]["fields"] if field["key"] == "status")
    assert status["options"] == ["draft", "issued", "paid", "void"]
    assert "stripe_invoice_id" not in keys
    assert "stripe_customer_id" not in keys
    assert "stripe_payment_intent_id" not in keys
    assert "source_job_id" not in keys


def test_billing_install_plan_does_not_bind_core_stripe(monkeypatch):
    from src.domain.app_blocks import compiler

    registry = load_default_registry()
    monkeypatch.setattr(
        compiler,
        "select_capabilities",
        lambda _intent: [("billing_invoice", "billing.invoice", "Selected.")],
    )
    plan = compiler.compile_install_plan(None, registry=registry)
    assert_install_plan_installable(plan)
    assert "billing-management" in plan.required_feature_packs
    assert "payment-checkout" not in plan.required_feature_packs
    assert "saas-membership" not in plan.required_feature_packs
    assert "data.entity" in plan.required_primitives


def test_billing_does_not_imply_checkout_orders_or_catalog():
    ids = _ids(
        ProductIntent(
            purpose="Staff need an invoice management screen. No checkout, orders, or catalog.",
            source_prompt="Staff need an invoice management screen. No checkout, orders, or catalog.",
            staff_review=True,
        )
    )
    assert "billing.invoice" in ids
    assert "orders.order" not in ids
    assert "catalog.product" not in ids
    assert "booking.appointment" not in ids
    assert "inventory.stock" not in ids
    assert not (ids & set(GATED))


def test_orders_and_billing_compose_without_merging_stores():
    prompt = "Staff manage customer orders and invoices for clients. No checkout."
    ids = _ids(ProductIntent(purpose=prompt, source_prompt=prompt, staff_review=True))
    assert {"orders.order", "billing.invoice"} <= ids
    assert "catalog.product" not in ids
    assert "inventory.stock" not in ids
    assert not (ids & set(GATED))


def test_catalog_does_not_imply_billing():
    prompt = "Staff manage a product catalog. No checkout, orders, or billing."
    ids = _ids(ProductIntent(purpose=prompt, source_prompt=prompt, staff_review=True))
    assert "catalog.product" in ids
    assert "billing.invoice" not in ids


def test_false_positives_do_not_select_billing():
    for purpose in (
        "customers pay us later",
        "track customer balances",
        "we sell services",
        "pricing page",
        "subscription business",
        "accept payments eventually",
        "Staff manage a product catalog",
        "Staff manage customer orders",
        "Paid invoices and refunds",
        "Stripe login",
    ):
        ids = _ids(ProductIntent(purpose=purpose, source_prompt=purpose, staff_review=True))
        assert "billing.invoice" not in ids, purpose


def test_negative_intent_skips_billing():
    for prompt in (
        "Client records for the firm. Do not add billing.",
        "Staff CRM. No invoicing system.",
        "Case tracking. I don't need invoices.",
        "Consulting workspace. No payment tracking.",
        "Internal tool. Do not build a billing module.",
        "We handle invoices somewhere else.",
        "Client portal. No billing functionality yet.",
    ):
        ids = _ids(ProductIntent(purpose=prompt, source_prompt=prompt, staff_review=True))
        assert "billing.invoice" not in ids, prompt


def test_positive_intent_selects_billing():
    for prompt in (
        "I need to create invoices for clients.",
        "Track invoices and whether they are paid.",
        "Staff need an invoice management screen.",
        "Record invoice amount, due date, customer and payment status.",
        "Build a billing dashboard for our consulting clients.",
    ):
        ids = _ids(ProductIntent(purpose=prompt, source_prompt=prompt, staff_review=True))
        assert "billing.invoice" in ids, prompt
        assert not (ids & set(GATED)), prompt


def test_cross_archetype_billing_is_the_same_block():
    prompts = (
        "Law firm staff create invoices for clients.",
        "Case operations with invoices for clients.",
        "Repair shop invoice management for service jobs.",
        "Consulting practice invoice records for students.",
    )
    for prompt in prompts:
        ids = _ids(ProductIntent(purpose=prompt, source_prompt=prompt, staff_review=True))
        assert "billing.invoice" in ids, prompt
        assert "payments.checkout" not in ids


def test_13a_do_not_create_orders_still_misses_orders_and_billing():
    prompt = (
        "Our sales office maintains catalog items in a shared list. "
        "Employees should sign in, add items and correct descriptions. Do not create orders."
    )
    assert match_vertical_recipe(prompt) is None
    ids = _ids(ProductIntent(purpose=prompt, source_prompt=prompt, staff_review=True))
    assert "catalog.product" in ids
    assert "orders.order" not in ids
    assert "billing.invoice" not in ids


def test_billing_reuses_record_starter_contracts():
    registry = load_default_registry()
    block = registry.require("billing.invoice")
    contracts = {
        slot.id: slot.component_contract
        for slot in block.slots
        if slot.component_contract
    }
    assert STARTER_SLOTS.items() <= contracts.items()
    assert "invoice.list" not in known_contract_ids()


def test_billing_runtime_crud_is_staff_managed_records():
    kernel = PlatformKernel(namespace="billing-freeze")
    kernel.as_anonymous()
    with pytest.raises(KernelDeniedError):
        kernel.create_record("invoice_record", {"reference": "INV-1"})
    kernel.as_staff()
    created = kernel.create_record(
        "invoice_record",
        {
            "reference": "INV-1048",
            "customer_name": "Ada",
            "customer_email": "ada@example.com",
            "amount": 2500,
            "currency": "USD",
            "due_date": "2026-10-15",
            "status": "issued",
        },
    )
    listed = kernel.list_records("invoice_record")
    assert listed[0]["id"] == created["id"]
    kernel.patch_record("invoice_record", created["id"], {"status": "paid"})
    read = kernel.get_record("invoice_record", created["id"])
    assert read["status"] == "paid"
    assert read["reference"] == "INV-1048"
    assert "stripe_invoice_id" not in read
    other = PlatformKernel(namespace="billing-other")
    other.as_staff()
    assert other.list_records("invoice_record") == []


def test_orders_and_billing_records_coexist_in_kernel():
    kernel = PlatformKernel(namespace="orders-billing")
    kernel.as_staff()
    order = kernel.create_record(
        "order_record",
        {"reference": "WO-1", "customer_name": "Ada", "status": "open"},
    )
    invoice = kernel.create_record(
        "invoice_record",
        {"reference": "INV-1", "customer_name": "Ada", "status": "draft"},
    )
    assert order["id"] != invoice["id"]
    assert kernel.get_record("order_record", order["id"])["reference"] == "WO-1"
    assert kernel.get_record("invoice_record", invoice["id"])["reference"] == "INV-1"


def test_materialized_invoice_hosts_use_records_not_stripe():
    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()))
        invoices = {h["slot"]: h for h in hosts["hosts"] if h["block"] == "billing.invoice"}
        assert {"list", "detail", "create", "edit", "empty"} <= set(invoices)
        for slot in ("list", "detail", "create", "edit"):
            path = invoices[slot]["implementation"].partition("#")[0]
            source = (root / path).read_text(encoding="utf-8")
            assert "invoice_record" in source
            assert "stripe_invoice_id" not in source
            assert "createCheckout" not in source
            assert "/api/billing" not in source
            assert "/api/checkout" not in source


def test_billing_freeze_receipts_and_coverage_next_family():
    receipt = RECEIPT.read_text(encoding="utf-8")
    audit = AUDIT.read_text(encoding="utf-8")
    for required in (
        "billing_admitted=yes",
        "canonical_app_block=billing.invoice",
        "canonical_feature_pack=billing-management",
        "billing_without_stripe=yes",
        "generated_app_billing_depends_on_core_stripe=no",
        "family_frozen=yes",
        "billing_catalog_composition=held",
        "next_architecture_family=inventory",
        "checkout_write_path_changed=no",
    ):
        assert required in receipt or required in audit, required
    registry = yaml.safe_load(REGISTRY.read_text(encoding="utf-8"))
    by_id = {row["id"]: row for row in registry["capabilities"]}
    assert by_id["billing.invoice"]["status"] == "frozen"
    assert by_id["billing"]["status"] == "frozen"
    assert registry["next_architecture_family"] == "not_yet_selected"
    assert registry["family_just_frozen"] == "visualization_calculation"


def test_billing_production_smoke_script():
    from scripts.architecture_lane_billing_smoke import run_smoke

    payload = run_smoke()
    assert payload["pass"] is True, payload["failures"]
    assert payload["on_disk_billing_recipes"] == []
