"""Quote-request vertical composition contract (Phase 10F).

Does not add catalog events, producer kinds, or Cloud architecture.
Codegen may choose public/admin routes; execution stays on platform bindings.
"""

from __future__ import annotations

from src.domain.app_blocks.canonical import FORM_RUNTIME_BY_BLOCK
from src.domain.app_blocks.compiler import compile_install_plan
from src.domain.app_blocks.schemas import AppBlockPlan
from src.domain.feature_packs.contract import validate_workflow_transitions
from src.domain.projects.discovery_schemas import (
    Actor,
    AuthenticationSpec,
    DiscoverySpec,
    Entity,
    ProductDefinition,
    TenancySpec,
    Workflow,
)

QUOTE_VERTICAL_BLOCKS = (
    "forms.quote_request",
    "admin.shell",
    "admin.record_table",
    "admin.record_detail",
)

QUOTE_STATUS_OPTIONS = ["new", "reviewing", "quoted", "won", "lost"]
QUOTE_TRANSITIONS = {
    "new": ["reviewing"],
    "reviewing": ["quoted", "lost"],
    "quoted": ["won", "lost"],
    "won": [],
    "lost": [],
}

# Formal App Block invariants proven in 10F. 10G must not collapse them.
BLOCK_OWNS_DECLARED_EXECUTION_CONTRACTS_ONLY = True
CROSS_BLOCK_EXECUTION_BINDING_DUPLICATION = False

# Binding ids owned by one selected block. Shared primitives (data.entity) may
# appear on several blocks; these execution bindings must not be copied.
QUOTE_BINDING_OWNERS = {
    "forms.quote_request": frozenset(
        {"persistence", "abuse_control", "status_graph", "submitted_event"}
    ),
    "admin.record_table": frozenset({"query"}),
    "admin.record_detail": frozenset({"record_read"}),
    "admin.shell": frozenset({"admin_auth", "admin_bff"}),
}


def quote_discovery_spec() -> DiscoverySpec:
    return DiscoverySpec(
        product=ProductDefinition(
            purpose="Website for a roofing company where visitors request a quote.",
            first_build_goal="Capture quote requests and let staff mark them New to Lost.",
        ),
        archetypes=["marketing_site"],
        actors=[
            Actor(id="visitor", name="Website visitor"),
            Actor(id="staff", name="Staff"),
        ],
        workflows=[
            Workflow(id="request_quote", name="Request a quote", description="Visitor requests a quote"),
            Workflow(id="review_quote", name="Review quote requests", description="Staff review quotes"),
        ],
        entities=[Entity(id="quote", name="Quote request", description="A roofing quote")],
        tenancy=TenancySpec(model="single_workspace"),
        authentication=AuthenticationSpec(required=False),
    )


def compile_quote_vertical_plan(**kwargs) -> AppBlockPlan:
    return compile_install_plan(quote_discovery_spec(), **kwargs)


def quote_entity_key() -> str:
    return FORM_RUNTIME_BY_BLOCK["forms.quote_request"]


def generated_quote_routes(plan: AppBlockPlan) -> dict[str, str]:
    """Codegen route materialization. Does not change backend contracts."""
    quote = next(block for block in plan.blocks if block.id == "forms.quote_request")
    return {
        "public": str(quote.slots.get("route") or "/request-quote"),
        "admin_list": "/admin/quotes",
        "admin_detail": "/admin/quotes/:id",
    }


def quote_transition_allowed(from_state: str, to_state: str) -> bool:
    if from_state == to_state:
        return False
    return to_state in QUOTE_TRANSITIONS.get(from_state, [])


def canonical_quote_workflow() -> dict[str, list[str]]:
    return validate_workflow_transitions("select", QUOTE_STATUS_OPTIONS, QUOTE_TRANSITIONS)
