"""Job-level Feature Packs for harvested marketing clusters.

These packs sit *below* `mkt_*` recipes. Recipes remain the demand match.
Packs are declarative composition of frozen App Blocks and backend Feature
Pack slugs. They invent no persistence, Cloud engines, or unfrozen IDs.
"""

from __future__ import annotations

from pydantic import BaseModel, ConfigDict, Field

from src.domain.app_blocks.catalogs import DEFAULT_KNOWN_PACKS
from src.domain.app_blocks.canonical import CANONICAL_BLOCK_IDS

JOB_PACK_VERSION = "1"

UNIVERSAL_FORBIDDEN = (
    "payments.checkout",
    "membership.portal",
    "saas-membership",
    "private_vertical_backend",
    "portal.portal",
)


class JobFeaturePack(BaseModel):
    model_config = ConfigDict(extra="forbid")
    id: str
    version: str = JOB_PACK_VERSION
    job_family: str
    required_app_blocks: list[str] = Field(default_factory=list)
    optional_app_blocks: list[str] = Field(default_factory=list)
    required_backend_packs: list[str] = Field(default_factory=list)
    optional_backend_packs: list[str] = Field(default_factory=list)
    compatible_starters: list[str] = Field(default_factory=list)
    forbidden_implicit_capabilities: list[str] = Field(default_factory=list)
    source_recipe_ids: list[str] = Field(default_factory=list)
    architecture_gaps: list[str] = Field(default_factory=list)
    notes: str = ""


def _pack(
    pack_id: str,
    family: str,
    *,
    blocks: list[str],
    backend: list[str],
    starters: list[str],
    recipes: list[str],
    forbidden: tuple[str, ...] = UNIVERSAL_FORBIDDEN,
    optional_blocks: list[str] | None = None,
    gaps: list[str] | None = None,
    notes: str = "",
) -> JobFeaturePack:
    unknown_blocks = [item for item in blocks if item not in CANONICAL_BLOCK_IDS]
    unknown_packs = [item for item in backend if item not in DEFAULT_KNOWN_PACKS]
    if unknown_blocks or unknown_packs:
        raise ValueError(f"{pack_id} unfrozen ids blocks={unknown_blocks} packs={unknown_packs}")
    return JobFeaturePack(
        id=pack_id,
        job_family=family,
        required_app_blocks=blocks,
        optional_app_blocks=list(optional_blocks or []),
        required_backend_packs=backend,
        compatible_starters=starters,
        forbidden_implicit_capabilities=list(dict.fromkeys(forbidden)),
        source_recipe_ids=recipes,
        architecture_gaps=list(gaps or []),
        notes=notes,
    )


JOB_FEATURE_PACKS: dict[str, JobFeaturePack] = {
    "approval_operations": _pack(
        "approval_operations",
        "approval_operations",
        blocks=["workflow.approval", "documents", "files.attachments", "task.management"],
        backend=["approval-workflow", "documents-lifecycle", "file-attachments", "task-management"],
        starters=["internal_operations", "document_heavy"],
        recipes=["mkt_approval_ops"],
    ),
    "case_operations": _pack(
        "case_operations",
        "case_operations",
        blocks=[
            "case.management",
            "task.management",
            "documents",
            "files.attachments",
            "activity.timeline",
        ],
        backend=[
            "case-lifecycle",
            "task-management",
            "documents-lifecycle",
            "file-attachments",
            "activity-timeline",
        ],
        starters=["case_operations", "compliance_audit"],
        recipes=["mkt_case_ops"],
    ),
    "task_operations": _pack(
        "task_operations",
        "task_operations",
        blocks=["task.management", "workflow.assignment", "activity.timeline"],
        backend=["task-management", "assignment-tracking", "activity-timeline"],
        starters=["internal_operations"],
        recipes=["mkt_task_ops"],
        notes="Customer onboarding uses tasks/records/documents — not onboarding.onboarding.",
    ),
    "document_control": _pack(
        "document_control",
        "documents_operations",
        blocks=["documents", "files.attachments", "workflow.approval"],
        backend=["documents-lifecycle", "file-attachments", "approval-workflow"],
        starters=["document_heavy"],
        recipes=["mkt_documents_ops"],
    ),
    "support_operations": _pack(
        "support_operations",
        "support_operations",
        blocks=["support.ticket", "workflow.assignment", "activity.timeline"],
        backend=["support-inbox", "assignment-tracking", "activity-timeline"],
        starters=["case_operations", "internal_operations"],
        recipes=["mkt_support_ops"],
    ),
    "ai_support_operations": _pack(
        "ai_support_operations",
        "support_operations",
        blocks=["support.ticket", "workflow.assignment", "activity.timeline"],
        backend=["support-inbox", "assignment-tracking", "activity-timeline", "ai-knowledge-assistant"],
        starters=["case_operations"],
        recipes=["mkt_ai_support"],
        notes="AI output is suggested and human-reviewed; it is not an autonomous outbound reply engine.",
    ),
    "crm_sales_operations": _pack(
        "crm_sales_operations",
        "crm_sales",
        blocks=["crm.customer", "crm.lead", "crm.pipeline", "task.management", "activity.timeline"],
        backend=[
            "customer-records",
            "lead-lifecycle",
            "crm-pipeline",
            "task-management",
            "activity-timeline",
        ],
        starters=["crm_sales"],
        recipes=["mkt_crm_sales"],
    ),
    "customer_success_operations": _pack(
        "customer_success_operations",
        "crm_sales",
        blocks=["crm.customer", "task.management", "activity.timeline", "case.management"],
        backend=["customer-records", "task-management", "activity-timeline", "case-lifecycle"],
        starters=["crm_sales", "case_operations"],
        recipes=["mkt_crm_sales"],
        optional_blocks=["case.management"],
        notes="Customer-success wording reuses CRM/case/task records. No success engine.",
    ),
    "crm_booking_operations": _pack(
        "crm_booking_operations",
        "crm_sales",
        blocks=["crm.customer", "crm.lead", "crm.pipeline", "booking.appointment", "task.management"],
        backend=[
            "customer-records",
            "lead-lifecycle",
            "crm-pipeline",
            "appointment-booking",
            "task-management",
        ],
        starters=["crm_sales", "booking"],
        recipes=["mkt_crm_plus_booking"],
    ),
    "association_management": _pack(
        "association_management",
        "membership_operations",
        blocks=["membership.member", "crm.customer", "documents", "task.management"],
        backend=[
            "membership-management",
            "customer-records",
            "documents-lifecycle",
            "task-management",
        ],
        starters=["membership_association"],
        recipes=["mkt_membership_roster"],
        notes="Application membership.member only. Never saas-membership or member portal.",
    ),
    "board_governance": _pack(
        "board_governance",
        "governance_operations",
        blocks=["documents", "files.attachments", "task.management", "workflow.approval"],
        backend=[
            "documents-lifecycle",
            "file-attachments",
            "task-management",
            "approval-workflow",
        ],
        starters=["document_heavy", "internal_operations"],
        recipes=["mkt_board_governance"],
        forbidden=UNIVERSAL_FORBIDDEN + ("membership.member",),
        notes="Board participants are not membership.member records.",
    ),
    "committee_management": _pack(
        "committee_management",
        "governance_operations",
        blocks=["documents", "files.attachments", "task.management", "workflow.approval"],
        backend=[
            "documents-lifecycle",
            "file-attachments",
            "task-management",
            "approval-workflow",
        ],
        starters=["document_heavy", "internal_operations"],
        recipes=["mkt_board_governance"],
        forbidden=UNIVERSAL_FORBIDDEN + ("membership.member",),
        notes="Committee member wording is not membership.member.",
    ),
    "billing_records": _pack(
        "billing_records",
        "billing_records",
        blocks=["billing.invoice", "crm.customer", "task.management"],
        backend=["billing-management", "customer-records", "task-management"],
        starters=["internal_operations", "catalog_orders"],
        recipes=["mkt_billing_records"],
        notes="billing.invoice with paid=status. Not Stripe Invoice or Core workspace invoice.",
    ),
    "reporting_dashboards": _pack(
        "reporting_dashboards",
        "reporting",
        blocks=["reporting.dashboard", "crm.customer", "task.management", "billing.invoice"],
        backend=[
            "reporting-dashboards",
            "customer-records",
            "task-management",
            "billing-management",
        ],
        starters=["internal_operations"],
        recipes=[],
        notes=(
            "reporting.dashboard renders via view-time records.aggregate/records.facet. "
            "No BI warehouse or cross-project analytics."
        ),
    ),
    "saas_analytics": _pack(
        "saas_analytics",
        "reporting",
        blocks=["reporting.dashboard", "visualization.chart", "calculation.calculator", "crm.customer", "task.management", "billing.invoice"],
        backend=["reporting-dashboards", "customer-records", "task-management", "billing-management"],
        starters=["internal_operations"],
        recipes=["mkt_saas_analytics"],
        notes="MRR, churn, cohort, and revenue analytics compose view-time records.aggregate/records.facet with calculation and chart outputs; no warehouse is required.",
    ),
    "construction_estimating": _pack(
        "construction_estimating",
        "construction_operations",
        blocks=["case.management", "task.management", "documents", "crm.customer", "forms.quote_request", "calculation.calculator"],
        backend=["case-lifecycle", "task-management", "documents-lifecycle", "customer-records", "contact-capture"],
        starters=["case_operations"],
        recipes=["mkt_construction_estimating"],
        notes="Markup and quote calculations are platform-backed; automated drawing takeoff remains a specialist boundary.",
    ),
    "lims_operations": _pack(
        "lims_operations",
        "laboratory_operations",
        blocks=["case.management", "documents", "inventory.stock", "files.attachments", "lab.sample", "lab.result_review"],
        backend=["case-lifecycle", "documents-lifecycle", "inventory-management", "file-attachments"],
        starters=["case_operations"],
        recipes=["mkt_lims_operations"],
        notes="Sample and result workflows are platform-backed; regulated laboratory claims remain a specialist boundary.",
    ),
    "catalog_order_operations": _pack(
        "catalog_order_operations",
        "commerce_records",
        blocks=[
            "catalog.product",
            "orders.order",
            "inventory.stock",
            "crm.customer",
            "billing.invoice",
            "payments.checkout",
        ],
        backend=[
            "product-catalog",
            "order-management",
            "inventory-management",
            "customer-records",
            "billing-management",
            "payment-checkout",
        ],
        starters=["catalog_orders"],
        recipes=["mkt_catalog_orders_inventory"],
        forbidden=tuple(item for item in UNIVERSAL_FORBIDDEN if item != "payments.checkout"),
    ),
    "b2b_order_portal": _pack(
        "b2b_order_portal",
        "commerce_records",
        blocks=["catalog.product", "orders.order", "crm.customer", "payments.checkout"],
        backend=["product-catalog", "order-management", "customer-records", "payment-checkout"],
        starters=["catalog_orders", "client_portal"],
        recipes=["mkt_catalog_orders_inventory"],
        forbidden=tuple(item for item in UNIVERSAL_FORBIDDEN if item != "payments.checkout"),
        notes="Orders remain application records; checkout is a separate provider payment boundary.",
    ),
    "booking_operations": _pack(
        "booking_operations",
        "booking_operations",
        blocks=["booking.appointment", "crm.customer", "task.management", "billing.invoice"],
        backend=[
            "appointment-booking",
            "customer-records",
            "task-management",
            "billing-management",
        ],
        starters=["booking"],
        recipes=["mkt_booking_ops", "mkt_clinic_admin_not_ehr"],
    ),
    "field_service_operations": _pack(
        "field_service_operations",
        "field_service_operations",
        blocks=[
            "crm.customer",
            "booking.appointment",
            "task.management",
            "case.management",
            "billing.invoice",
            "files.attachments",
        ],
        backend=[
            "customer-records",
            "appointment-booking",
            "task-management",
            "case-lifecycle",
            "billing-management",
            "file-attachments",
        ],
        starters=["service_field"],
        recipes=["mkt_field_service"],
        notes="No GPS, route optimizer, dispatch engine, or technician backend.",
    ),
    "client_portal_operations": _pack(
        "client_portal_operations",
        "client_portal",
        blocks=[
            "auth.sign_in",
            "auth.profile",
            "documents",
            "files.attachments",
            "workflow.approval",
            "task.management",
            "crm.customer",
        ],
        backend=[
            "documents-lifecycle",
            "file-attachments",
            "approval-workflow",
            "task-management",
            "customer-records",
            "client-portal",
        ],
        starters=["client_portal"],
        recipes=["mkt_client_portal"],
        forbidden=UNIVERSAL_FORBIDDEN + ("portal.portal",),
    ),
    "client_onboarding": _pack(
        "client_onboarding",
        "client_portal",
        blocks=["auth.sign_in", "crm.customer", "documents", "task.management", "workflow.approval"],
        backend=[
            "customer-records",
            "documents-lifecycle",
            "task-management",
            "approval-workflow",
            "client-portal",
        ],
        starters=["client_portal", "internal_operations"],
        recipes=["mkt_client_portal", "mkt_task_ops"],
        notes="Composes records/tasks/documents. Does not invent onboarding.onboarding.",
    ),
    "contract_lifecycle_documents": _pack(
        "contract_lifecycle_documents",
        "contract_lifecycle",
        blocks=["documents", "files.attachments", "workflow.approval", "crm.customer"],
        backend=[
            "documents-lifecycle",
            "file-attachments",
            "approval-workflow",
            "customer-records",
        ],
        starters=["document_heavy"],
        recipes=["mkt_contract_lifecycle"],
        gaps=["contract_record_capability"],
        notes="C4: documents+approvals only. contract.contract is not admitted.",
    ),
    "workforce_operations_records": _pack(
        "workforce_operations_records",
        "workforce_operations",
        blocks=["task.management", "documents", "workflow.approval", "crm.customer"],
        backend=["task-management", "documents-lifecycle", "approval-workflow", "customer-records"],
        starters=["internal_operations"],
        recipes=["mkt_workforce"],
        forbidden=UNIVERSAL_FORBIDDEN + ("membership.member",),
        gaps=["workforce_credential_capability", "shift_scheduling_capability"],
        notes="C4: staff are not membership.member. No workforce.* capability.",
    ),
    "property_operations_records": _pack(
        "property_operations_records",
        "property_operations",
        blocks=[
            "case.management",
            "task.management",
            "crm.customer",
            "property.unit",
            "asset.serialized",
            "booking.appointment",
            "billing.invoice",
        ],
        backend=[
            "case-lifecycle",
            "task-management",
            "customer-records",
            "appointment-booking",
            "billing-management",
        ],
        starters=["internal_operations", "service_field"],
        recipes=["mkt_property_assets"],
        notes="Property units and serialized assets use generic data.entity records; maintenance is case/task.",
    ),
    "procurement_operations": _pack(
        "procurement_operations",
        "procurement_operations",
        blocks=[
            "workflow.approval",
            "documents",
            "crm.customer",
            "orders.order",
            "case.management",
        ],
        backend=[
            "approval-workflow",
            "documents-lifecycle",
            "customer-records",
            "order-management",
            "case-lifecycle",
        ],
        starters=["internal_operations", "catalog_orders"],
        recipes=["mkt_procurement"],
    ),
    "education_certification": _pack(
        "education_certification",
        "education_operations",
        blocks=[
            "case.management",
            "documents",
            "workflow.approval",
            "membership.member",
            "crm.customer",
        ],
        backend=[
            "case-lifecycle",
            "documents-lifecycle",
            "approval-workflow",
            "membership-management",
            "customer-records",
        ],
        starters=["case_operations", "membership_association"],
        recipes=["mkt_education_cert"],
    ),
    "application_portal_operations": _pack(
        "application_portal_operations",
        "application_portal_operations",
        blocks=[
            "forms.contact",
            "case.management",
            "requirements.checklist",
            "collaboration.conversation",
            "collaboration.comment",
            "files.attachments",
            "notifications.in_app",
            "workflow.assignment",
            "task.management",
            "activity.timeline",
        ],
        backend=[
            "case-lifecycle",
            "requirements-fulfillment",
            "messaging",
            "comment-threads",
            "file-attachments",
            "notifications-in-app",
            "assignment-tracking",
            "task-management",
            "activity-timeline",
        ],
        starters=["application_portal"],
        recipes=["application_portal"],
        forbidden=UNIVERSAL_FORBIDDEN
        + ("student_admissions_backend", "student_consultancy_backend"),
        gaps=["declarative-event-reactions"],
        notes=(
            "Generic intake, requirements, participant conversation, comments, "
            "activity, staff assignment, and action-item composition; event "
            "reactions remain an explicit orchestration gap; no vertical "
            "application engine."
        ),
    ),
    "education_lms": _pack(
        "education_lms",
        "education_operations",
        blocks=[
            "education.course_delivery",
            "education.progress",
            "education.assessment",
            "education.certificate",
            "content.collection",
            "documents",
            "membership.member",
            "task.management",
        ],
        backend=[
            "education-learning",
            "documents-lifecycle",
            "membership-management",
            "task-management",
        ],
        starters=["internal_operations"],
        recipes=["mkt_education_lms"],
        notes="Course delivery is record-backed; external accreditation credentials remain configuration.",
    ),
    "school_operations": _pack(
        "school_operations",
        "education_operations",
        blocks=["education.student", "case.management", "documents", "workflow.approval", "membership.member", "payments.checkout", "education.attendance"],
        backend=["education-learning", "case-lifecycle", "documents-lifecycle", "approval-workflow", "membership-management", "payment-checkout"],
        starters=["case_operations"],
        recipes=["mkt_school_operations"],
        forbidden=tuple(item for item in UNIVERSAL_FORBIDDEN if item != "payments.checkout"),
    ),
    "social_operations": _pack(
        "social_operations",
        "content_operations",
        blocks=["content.publishing", "content.collection", "workflow.approval", "task.management", "files.attachments"],
        backend=["approval-workflow", "task-management", "file-attachments"],
        starters=["internal_operations"],
        recipes=["mkt_social_operations"],
        notes="Social publish credentials and provider adapters remain explicit configuration.",
    ),
    "warehouse_operations": _pack(
        "warehouse_operations",
        "warehouse_operations",
        blocks=["inventory.warehouse", "inventory.stock", "logistics.shipment", "task.management", "documents", "calculation.calculator"],
        backend=["inventory-management", "task-management", "documents-lifecycle"],
        starters=["internal_operations"],
        recipes=["mkt_warehouse_operations"],
        notes="Location-ordered pick lists use the bounded authoritative calculation evaluator; proprietary wave optimization is not claimed.",
    ),
    "transport_operations": _pack(
        "transport_operations",
        "transport_operations",
        blocks=["logistics.shipment", "task.management", "documents", "crm.customer"],
        backend=["task-management", "documents-lifecycle", "customer-records"],
        starters=["internal_operations"],
        recipes=["mkt_transport_operations"],
        notes="Shipment records and dispatch tasks; TMS optimization remains a specialist gap.",
    ),
    "manufacturing_operations": _pack(
        "manufacturing_operations",
        "manufacturing_operations",
        blocks=["manufacturing.work_order", "inventory.stock", "task.management", "workflow.approval", "reporting.dashboard"],
        backend=["inventory-management", "task-management", "approval-workflow", "reporting-dashboards"],
        starters=["internal_operations"],
        recipes=["mkt_manufacturing_operations"],
        notes="Work orders, material records, and plant reporting; MRP and finite-capacity planning are not claimed.",
    ),
    "loan_operations": _pack(
        "loan_operations",
        "loan_operations",
        blocks=["finance.loan_operations", "case.management", "documents", "billing.invoice"],
        backend=["case-lifecycle", "documents-lifecycle", "billing-management"],
        starters=["internal_operations"],
        recipes=["mkt_loan_operations"],
        notes="Loan servicing records and evidence; regulated decisioning and posting remain specialist gaps.",
    ),
    "aviation_operations": _pack(
        "aviation_operations",
        "aviation_operations",
        blocks=["aviation.maintenance", "asset.serialized", "case.management", "task.management", "workflow.approval", "documents"],
        backend=["case-lifecycle", "task-management", "approval-workflow", "documents-lifecycle"],
        starters=["internal_operations"],
        recipes=["mkt_aviation_operations"],
        notes="Maintenance evidence, technician approval, and release-to-service workflow gates are platform-backed; regulated airworthiness authority remains a provider boundary.",
    ),
    "subscription_operations": _pack(
        "subscription_operations",
        "subscription_operations",
        blocks=["billing.subscription_operations", "billing.tax", "crm.customer"],
        backend=["subscription-operations", "tax-calculation", "customer-records"],
        starters=["internal_operations"],
        recipes=["mkt_subscription_operations"],
        notes="Subscription operations use provider configuration; app-customer entitlement enforcement is outside this operations pack.",
    ),
    "course_delivery": _pack(
        "course_delivery",
        "course_delivery",
        blocks=["education.course_delivery", "education.progress", "education.assessment", "education.certificate", "payments.checkout", "content.collection", "documents", "membership.member"],
        backend=["education-learning", "payment-checkout", "documents-lifecycle", "membership-management"],
        starters=["internal_operations"],
        recipes=["mkt_course_delivery"],
        forbidden=tuple(item for item in UNIVERSAL_FORBIDDEN if item != "payments.checkout"),
    ),
    "lab_trace": _pack(
        "lab_trace",
        "laboratory_operations",
        blocks=["case.management", "documents", "inventory.stock", "files.attachments", "lab.sample", "lab.result_review"],
        backend=["case-lifecycle", "documents-lifecycle", "inventory-management", "file-attachments"],
        starters=["case_operations"],
        recipes=["mkt_lab_trace"],
        notes="Sample custody and result review; regulated LIMS remains an explicit specialist claim.",
    ),
    "imports_operations": _pack(
        "imports_operations",
        "data_operations",
        blocks=["data.import", "case.management", "documents"],
        backend=["case-lifecycle", "documents-lifecycle"],
        starters=["internal_operations"],
        recipes=["mkt_imports_operations"],
    ),
    "clinical_case_operations": _pack(
        "clinical_case_operations",
        "clinical_case_operations",
        blocks=["case.management", "task.management", "documents", "files.attachments", "activity.timeline"],
        backend=["case-lifecycle", "task-management", "documents-lifecycle", "file-attachments", "activity-timeline"],
        starters=["case_operations"],
        recipes=["mkt_clinical_case"],
        notes="Generic case runtime is composed; clinical records and private vertical policy remain explicit gaps.",
    ),
    "certification_operations": _pack(
        "certification_operations",
        "education_operations",
        blocks=["case.management", "documents", "workflow.approval", "membership.member", "education.progress", "education.assessment", "education.certificate"],
        backend=["case-lifecycle", "documents-lifecycle", "approval-workflow", "membership-management", "education-learning", "renewal-operations"],
        starters=["case_operations"],
        recipes=["mkt_certification_operations"],
        notes="Accreditor credentials are configuration; certificate records and renewal dates are platform-backed.",
    ),
    "procurement_match_operations": _pack(
        "procurement_match_operations",
        "procurement_operations",
        blocks=["workflow.approval", "documents", "crm.customer", "orders.order", "billing.invoice", "case.management", "calculation.calculator"],
        backend=["approval-workflow", "documents-lifecycle", "customer-records", "order-management", "billing-management", "case-lifecycle"],
        starters=["internal_operations"],
        recipes=["mkt_procurement_match"],
        notes="Purchase orders and invoices are platform-backed; automated three-way matching remains a specialist boundary.",
    ),
    "construction_precon": _pack(
        "construction_precon",
        "construction_operations",
        blocks=[
            "case.management",
            "task.management",
            "documents",
            "crm.customer",
            "forms.quote_request",
        ],
        backend=[
            "case-lifecycle",
            "task-management",
            "documents-lifecycle",
            "customer-records",
            "contact-capture",
        ],
        starters=["case_operations"],
        recipes=["mkt_construction_precon"],
    ),
}


CLUSTER_JOB_PACK_IDS: dict[str, list[str]] = {
    "approval_ops": ["approval_operations"],
    "case_ops": ["case_operations"],
    "task_ops": ["task_operations", "client_onboarding"],
    "documents_ops": ["document_control"],
    "support_ops": ["support_operations"],
    "ai_support": ["ai_support_operations"],
    "crm_sales": ["crm_sales_operations", "customer_success_operations"],
    "crm_plus_booking": ["crm_booking_operations"],
    "membership_roster": ["association_management"],
    "membership_entitlements": ["association_management"],
    "membership_access": ["association_management"],
    "board_governance": ["board_governance", "committee_management"],
    "billing_records": ["billing_records"],
    "catalog_orders_inventory": ["catalog_order_operations", "b2b_order_portal"],
    "booking_ops": ["booking_operations"],
    "clinic_admin_not_ehr": ["booking_operations"],
    "field_service": ["field_service_operations"],
    "client_portal": ["client_portal_operations", "client_onboarding"],
    "contract_lifecycle": ["contract_lifecycle_documents"],
    "workforce": ["workforce_operations_records"],
    "property_assets": ["property_operations_records"],
    "procurement": ["procurement_operations"],
    "education_cert": ["education_certification"],
    "education_lms": ["education_lms"],
    "school_operations": ["school_operations"],
    "social_operations": ["social_operations"],
    "warehouse_operations": ["warehouse_operations"],
    "transport_operations": ["transport_operations"],
    "manufacturing_operations": ["manufacturing_operations"],
    "loan_operations": ["loan_operations"],
    "aviation_operations": ["aviation_operations"],
    "subscription_operations": ["subscription_operations"],
    "course_delivery": ["course_delivery"],
    "lab_trace": ["lab_trace"],
    "imports_ops": ["imports_operations"],
    "clinical_case": ["clinical_case_operations"],
    "certification_operations": ["certification_operations"],
    "procurement_match": ["procurement_match_operations"],
    "saas_analytics": ["saas_analytics"],
    "construction_estimating": ["construction_estimating"],
    "lims_operations": ["lims_operations"],
    "construction_precon": ["construction_precon"],
    "reporting": ["reporting_dashboards"],
}

RECIPE_JOB_PACK_IDS: dict[str, list[str]] = {
    f"mkt_{cluster_id}": list(pack_ids) for cluster_id, pack_ids in CLUSTER_JOB_PACK_IDS.items()
}


def list_job_packs() -> list[JobFeaturePack]:
    return [JOB_FEATURE_PACKS[key] for key in sorted(JOB_FEATURE_PACKS)]


def get_job_pack(pack_id: str) -> JobFeaturePack | None:
    return JOB_FEATURE_PACKS.get(pack_id)


def job_pack_ids_for_cluster(cluster_id: str) -> list[str]:
    return list(CLUSTER_JOB_PACK_IDS.get(cluster_id) or [])


def job_pack_ids_for_recipe(recipe_id: str | None) -> list[str]:
    if not recipe_id:
        return []
    return list(RECIPE_JOB_PACK_IDS.get(recipe_id) or [])


def known_job_pack_ids() -> frozenset[str]:
    return frozenset(JOB_FEATURE_PACKS)
