"""Architecture Lane Billing production-path planner smoke.

Does not invent a Billing recipe family.
"""

from __future__ import annotations

import json
from datetime import UTC, datetime
from pathlib import Path

from src.domain.app_blocks.planner import product_intent_from_spec, select_capabilities
from src.domain.app_blocks.vertical_recipes import load_recipe_catalogs, load_recipes, match_recipe_for_intent
from src.domain.projects.discovery_schemas import (
    Actor,
    AuthenticationSpec,
    DiscoverySpec,
    Entity,
    ProductDefinition,
    TenancySpec,
    Workflow,
)

ROOT = Path(__file__).resolve().parents[1]
RECEIPT = ROOT / "src/domain/app_blocks/recipes/billing_freeze.receipt.txt"

GATED = (
    "payments.checkout",
    "inventory.stock",
    "membership.portal",
)

SMOKE = (
    ("I need to create invoices for clients.", None, ("billing.invoice",)),
    ("Track invoices and whether they are paid.", None, ("billing.invoice",)),
    ("Staff need an invoice management screen.", None, ("billing.invoice",)),
    ("Build a billing dashboard for our consulting clients.", "consulting_clients", ("billing.invoice",)),
    (
        "Staff manage customer orders and invoices for clients. No checkout.",
        None,
        ("orders.order", "billing.invoice"),
    ),
)

CONTROLS = (
    ("Staff manage a product catalog. No checkout, orders, or billing.", None, ("catalog.product",)),
    ("Staff manage customer orders. No checkout, billing, or inventory.", None, ("orders.order",)),
    ("appointment booking", None, ("booking.appointment",)),
)

NEGATIVES = (
    ("Do not add billing.", None, "billing.invoice"),
    ("No invoicing system.", None, "billing.invoice"),
    ("I don't need invoices.", None, "billing.invoice"),
    ("accept payments eventually", None, "billing.invoice"),
    ("subscription business", None, "billing.invoice"),
    ("pricing page", None, "billing.invoice"),
    (
        "Our sales office maintains catalog items in a shared list. "
        "Employees should sign in, add items and correct descriptions. Do not create orders.",
        None,
        "billing.invoice",
    ),
)


def _spec(prompt: str) -> DiscoverySpec:
    return DiscoverySpec(
        product=ProductDefinition(
            purpose="track records for a consultancy",
            first_build_goal="Staff review submitted records",
        ),
        original_intent=prompt,
        actors=[Actor(id="staff", name="Staff counselor")],
        workflows=[Workflow(id="review", name="Staff review", description="Review records")],
        entities=[Entity(id="record", name="Record", description="A record")],
        tenancy=TenancySpec(model="single_workspace"),
        authentication=AuthenticationSpec(required=False),
    )


def _ids(prompt: str) -> set[str]:
    intent = product_intent_from_spec(_spec(prompt), source_prompt=prompt)
    return {block_id for _cap, block_id, _reason in select_capabilities(intent)}


def _recipe(prompt: str):
    return match_recipe_for_intent(product_intent_from_spec(_spec(prompt), source_prompt=prompt))


def run_smoke() -> dict:
    rows = []
    failures = []
    live = {recipe.id for recipe in load_recipes(composition_ready_only=True)}
    on_disk_billing = [
        recipe.id
        for catalog in load_recipe_catalogs()
        for recipe in catalog.recipes
        if catalog.batch != "marketing_coverage"
        and ("bill" in recipe.id or "invoice" in recipe.id or catalog.batch.startswith("billing"))
    ]
    for prompt, expected, required in SMOKE:
        recipe = _recipe(prompt)
        ids = _ids(prompt)
        matched = None if recipe is None else recipe.id
        ok = set(required) <= ids and matched == expected
        ok = ok and not (ids & set(GATED))
        if "catalog.product" not in required:
            ok = ok and "catalog.product" not in ids
        if not ok:
            failures.append(prompt)
        rows.append({"prompt": prompt, "kind": "smoke", "matched": matched, "ids": sorted(ids), "pass": ok})
    for prompt, expected, required in CONTROLS:
        recipe = _recipe(prompt)
        ids = _ids(prompt)
        matched = None if recipe is None else recipe.id
        ok = matched == expected and set(required) <= ids
        ok = ok and "billing.invoice" not in ids
        ok = ok and not (ids & set(GATED))
        if not ok:
            failures.append(prompt)
        rows.append({"prompt": prompt, "kind": "control", "matched": matched, "ids": sorted(ids), "pass": ok})
    for prompt, expected, stripped in NEGATIVES:
        recipe = _recipe(prompt)
        ids = _ids(prompt)
        matched = None if recipe is None else recipe.id
        ok = matched == expected and stripped not in ids
        if not ok:
            failures.append(prompt)
        rows.append({"prompt": prompt, "kind": "negative", "matched": matched, "ids": sorted(ids), "pass": ok})
    payload = {
        "generated_at": datetime.now(UTC).isoformat(),
        "live_recipes": len(live),
        "on_disk_billing_recipes": on_disk_billing,
        "failures": failures,
        "pass": not failures and on_disk_billing == [],
        "rows": rows,
    }
    if on_disk_billing:
        payload["failures"] = [*failures, "on_disk_billing_recipes"]
        payload["pass"] = False
    return payload


def main() -> int:
    payload = run_smoke()
    print(json.dumps({k: payload[k] for k in payload if k != "rows"}, indent=2))
    for row in payload["rows"]:
        mark = "PASS" if row["pass"] else "FAIL"
        print(f"{mark} {row['kind']:8} {row['prompt']!r} matched={row.get('matched')} ids={row['ids']}")
    if RECEIPT.is_file() and payload["pass"]:
        text = RECEIPT.read_text(encoding="utf-8")
        if "production_proven=pending_this_slice" in text:
            RECEIPT.write_text(
                text.replace("production_proven=pending_this_slice", "production_proven=local_smoke_pass"),
                encoding="utf-8",
            )
    return 0 if payload["pass"] else 1


if __name__ == "__main__":
    raise SystemExit(main())
