"""add tenant commercial contracts, entitlements, usage and cost ledgers

Revision ID: 20260716_0016
Revises: 20260716_0015
Create Date: 2026-07-16 23:10:00
"""

from __future__ import annotations

from collections.abc import Sequence

import sqlalchemy as sa

from alembic import op

revision: str = "20260716_0016"
down_revision: str | None = "20260716_0015"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


def _require_postgresql() -> None:
    dialect_name = op.get_bind().dialect.name
    if dialect_name != "postgresql":
        raise RuntimeError(
            "20260716_0016 only supports PostgreSQL; "
            f"refusing to mutate {dialect_name} without transactional constraint DDL"
        )


def _require_empty_commercial_domain_for_downgrade() -> None:
    bind = op.get_bind()
    counts = {
        table_name: int(bind.scalar(sa.text(f"SELECT COUNT(*) FROM {table_name}")) or 0)
        for table_name in (
            "tenant_commercial_plans",
            "tenant_subscriptions",
            "commercial_entitlements",
            "usage_meter_events",
            "commercial_cost_events",
        )
    }
    if any(counts.values()):
        summary = ", ".join(f"{name}={count}" for name, count in counts.items())
        raise RuntimeError(
            "cannot downgrade commercial metering: contracts or immutable facts exist "
            f"({summary})"
        )


def _json_object_default() -> sa.TextClause:
    return sa.text("'{}'::json")


def upgrade() -> None:
    _require_postgresql()
    op.create_table(
        "tenant_commercial_plans",
        sa.Column("id", sa.String(length=36), nullable=False),
        sa.Column("tenant_id", sa.String(length=64), nullable=False),
        sa.Column("plan_code", sa.String(length=80), nullable=False),
        sa.Column("name", sa.String(length=160), nullable=False),
        sa.Column("pricing_model", sa.String(length=24), nullable=False),
        sa.Column("billing_interval", sa.String(length=20), nullable=False),
        sa.Column("currency", sa.String(length=3), nullable=False),
        sa.Column("base_fee", sa.Numeric(20, 4), nullable=False),
        sa.Column("included_seats", sa.Integer(), nullable=False, server_default="0"),
        sa.Column(
            "overage_enabled", sa.Boolean(), nullable=False, server_default=sa.text("false")
        ),
        sa.Column("status", sa.String(length=16), nullable=False, server_default="draft"),
        sa.Column("effective_from", sa.DateTime(timezone=True), nullable=False),
        sa.Column("effective_to", sa.DateTime(timezone=True), nullable=True),
        sa.Column("version", sa.Integer(), nullable=False, server_default="1"),
        sa.Column(
            "contract_terms_json",
            sa.JSON(),
            nullable=False,
            server_default=_json_object_default(),
        ),
        sa.Column("created_by", sa.String(length=120), nullable=False),
        sa.Column(
            "created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()
        ),
        sa.Column(
            "updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()
        ),
        sa.CheckConstraint(
            "pricing_model IN ('subscription', 'usage', 'hybrid', 'pilot', 'custom')",
            name="ck_tenant_commercial_plans_pricing_model",
        ),
        sa.CheckConstraint(
            "billing_interval IN ('monthly', 'quarterly', 'annual', 'contract')",
            name="ck_tenant_commercial_plans_billing_interval",
        ),
        sa.CheckConstraint(
            "status IN ('draft', 'active', 'retired')",
            name="ck_tenant_commercial_plans_status",
        ),
        sa.CheckConstraint(
            "base_fee >= 0 AND included_seats >= 0 AND version >= 1",
            name="ck_tenant_commercial_plans_values",
        ),
        sa.CheckConstraint(
            "length(trim(plan_code)) > 0 AND length(trim(name)) > 0",
            name="ck_tenant_commercial_plans_keys",
        ),
        sa.CheckConstraint(
            "length(trim(currency)) = 3", name="ck_tenant_commercial_plans_currency"
        ),
        sa.CheckConstraint(
            "effective_to IS NULL OR effective_to > effective_from",
            name="ck_tenant_commercial_plans_effective_window",
        ),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint(
            "tenant_id", "id", name="uq_tenant_commercial_plans_tenant_id"
        ),
        sa.UniqueConstraint(
            "tenant_id",
            "plan_code",
            "version",
            name="uq_tenant_commercial_plans_tenant_code_version",
        ),
    )
    op.create_index(
        "uq_tenant_commercial_plans_active_code",
        "tenant_commercial_plans",
        ["tenant_id", "plan_code"],
        unique=True,
        postgresql_where=sa.text("status = 'active'"),
    )
    op.create_index(
        "ix_tenant_commercial_plans_tenant_status",
        "tenant_commercial_plans",
        ["tenant_id", "status", "effective_from"],
    )
    op.create_table(
        "tenant_subscriptions",
        sa.Column("id", sa.String(length=36), nullable=False),
        sa.Column("tenant_id", sa.String(length=64), nullable=False),
        sa.Column("subscription_key", sa.String(length=120), nullable=False),
        sa.Column("plan_id", sa.String(length=36), nullable=False),
        sa.Column("status", sa.String(length=20), nullable=False),
        sa.Column("starts_at", sa.DateTime(timezone=True), nullable=False),
        sa.Column("ends_at", sa.DateTime(timezone=True), nullable=True),
        sa.Column("current_period_start", sa.DateTime(timezone=True), nullable=False),
        sa.Column("current_period_end", sa.DateTime(timezone=True), nullable=False),
        sa.Column("seats", sa.Integer(), nullable=False),
        sa.Column("base_fee_snapshot", sa.Numeric(20, 4), nullable=False),
        sa.Column("currency", sa.String(length=3), nullable=False),
        sa.Column("billing_interval", sa.String(length=20), nullable=False),
        sa.Column("auto_renew", sa.Boolean(), nullable=False, server_default=sa.text("false")),
        sa.Column("external_provider", sa.String(length=60), nullable=True),
        sa.Column("external_subscription_id", sa.String(length=160), nullable=True),
        sa.Column("canceled_at", sa.DateTime(timezone=True), nullable=True),
        sa.Column("version", sa.Integer(), nullable=False, server_default="1"),
        sa.Column(
            "metadata_json", sa.JSON(), nullable=False, server_default=_json_object_default()
        ),
        sa.Column("created_by", sa.String(length=120), nullable=False),
        sa.Column(
            "created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()
        ),
        sa.Column(
            "updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()
        ),
        sa.CheckConstraint(
            "status IN ('trialing', 'active', 'past_due', 'suspended', 'canceled', 'expired')",
            name="ck_tenant_subscriptions_status",
        ),
        sa.CheckConstraint(
            "billing_interval IN ('monthly', 'quarterly', 'annual', 'contract')",
            name="ck_tenant_subscriptions_billing_interval",
        ),
        sa.CheckConstraint(
            "seats > 0 AND base_fee_snapshot >= 0 AND version >= 1",
            name="ck_tenant_subscriptions_values",
        ),
        sa.CheckConstraint(
            "length(trim(subscription_key)) > 0 AND length(trim(currency)) = 3",
            name="ck_tenant_subscriptions_keys",
        ),
        sa.CheckConstraint(
            "current_period_end > current_period_start",
            name="ck_tenant_subscriptions_period",
        ),
        sa.CheckConstraint(
            "ends_at IS NULL OR ends_at > starts_at",
            name="ck_tenant_subscriptions_contract_window",
        ),
        sa.CheckConstraint(
            "(external_provider IS NULL AND external_subscription_id IS NULL) OR "
            "(external_provider IS NOT NULL AND external_subscription_id IS NOT NULL)",
            name="ck_tenant_subscriptions_external_pair",
        ),
        sa.CheckConstraint(
            "status != 'canceled' OR canceled_at IS NOT NULL",
            name="ck_tenant_subscriptions_cancellation",
        ),
        sa.ForeignKeyConstraint(
            ["tenant_id", "plan_id"],
            ["tenant_commercial_plans.tenant_id", "tenant_commercial_plans.id"],
            name="fk_tenant_subscriptions_tenant_plan",
            ondelete="RESTRICT",
        ),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint("tenant_id", "id", name="uq_tenant_subscriptions_tenant_id"),
        sa.UniqueConstraint(
            "tenant_id", "subscription_key", name="uq_tenant_subscriptions_tenant_key"
        ),
        sa.UniqueConstraint(
            "tenant_id",
            "external_provider",
            "external_subscription_id",
            name="uq_tenant_subscriptions_external_ref",
        ),
    )
    op.create_index(
        "uq_tenant_subscriptions_current",
        "tenant_subscriptions",
        ["tenant_id"],
        unique=True,
        postgresql_where=sa.text(
            "status IN ('trialing', 'active', 'past_due', 'suspended')"
        ),
    )
    op.create_index(
        "ix_tenant_subscriptions_tenant_status_period",
        "tenant_subscriptions",
        ["tenant_id", "status", "current_period_end"],
    )
    op.create_table(
        "commercial_entitlements",
        sa.Column("id", sa.String(length=36), nullable=False),
        sa.Column("tenant_id", sa.String(length=64), nullable=False),
        sa.Column("subscription_id", sa.String(length=36), nullable=False),
        sa.Column("entitlement_key", sa.String(length=120), nullable=False),
        sa.Column("metric_key", sa.String(length=120), nullable=False),
        sa.Column("entitlement_type", sa.String(length=20), nullable=False),
        sa.Column("unit", sa.String(length=40), nullable=False),
        sa.Column("included_quantity", sa.Numeric(20, 6), nullable=True),
        sa.Column("hard_limit_quantity", sa.Numeric(20, 6), nullable=True),
        sa.Column("reset_interval", sa.String(length=20), nullable=False),
        sa.Column("overage_policy", sa.String(length=16), nullable=False),
        sa.Column("status", sa.String(length=16), nullable=False),
        sa.Column("effective_from", sa.DateTime(timezone=True), nullable=False),
        sa.Column("effective_to", sa.DateTime(timezone=True), nullable=True),
        sa.Column("version", sa.Integer(), nullable=False, server_default="1"),
        sa.Column("config_json", sa.JSON(), nullable=False, server_default=_json_object_default()),
        sa.Column(
            "created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()
        ),
        sa.Column(
            "updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()
        ),
        sa.CheckConstraint(
            "entitlement_type IN ('feature', 'metered', 'unlimited')",
            name="ck_commercial_entitlements_type",
        ),
        sa.CheckConstraint(
            "reset_interval IN ('none', 'monthly', 'quarterly', 'annual', 'contract')",
            name="ck_commercial_entitlements_reset_interval",
        ),
        sa.CheckConstraint(
            "overage_policy IN ('block', 'allow', 'alert')",
            name="ck_commercial_entitlements_overage_policy",
        ),
        sa.CheckConstraint(
            "status IN ('active', 'suspended', 'expired')",
            name="ck_commercial_entitlements_status",
        ),
        sa.CheckConstraint(
            "version >= 1 AND (included_quantity IS NULL OR included_quantity >= 0) "
            "AND (hard_limit_quantity IS NULL OR hard_limit_quantity >= 0)",
            name="ck_commercial_entitlements_values",
        ),
        sa.CheckConstraint(
            "(entitlement_type = 'unlimited' AND included_quantity IS NULL "
            "AND hard_limit_quantity IS NULL) OR "
            "(entitlement_type = 'feature' AND included_quantity IN (0, 1) "
            "AND (hard_limit_quantity IS NULL OR hard_limit_quantity IN (0, 1))) OR "
            "(entitlement_type = 'metered' AND included_quantity IS NOT NULL "
            "AND (hard_limit_quantity IS NULL OR hard_limit_quantity >= included_quantity))",
            name="ck_commercial_entitlements_quota_shape",
        ),
        sa.CheckConstraint(
            "length(trim(entitlement_key)) > 0 AND length(trim(metric_key)) > 0 "
            "AND length(trim(unit)) > 0",
            name="ck_commercial_entitlements_keys",
        ),
        sa.CheckConstraint(
            "effective_to IS NULL OR effective_to > effective_from",
            name="ck_commercial_entitlements_effective_window",
        ),
        sa.ForeignKeyConstraint(
            ["tenant_id", "subscription_id"],
            ["tenant_subscriptions.tenant_id", "tenant_subscriptions.id"],
            name="fk_commercial_entitlements_tenant_subscription",
            ondelete="RESTRICT",
        ),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint(
            "tenant_id", "id", name="uq_commercial_entitlements_tenant_id"
        ),
        sa.UniqueConstraint(
            "tenant_id",
            "subscription_id",
            "id",
            name="uq_commercial_entitlements_tenant_subscription_id",
        ),
        sa.UniqueConstraint(
            "tenant_id",
            "subscription_id",
            "entitlement_key",
            name="uq_commercial_entitlements_subscription_key",
        ),
    )
    op.create_index(
        "ix_commercial_entitlements_subscription_status",
        "commercial_entitlements",
        ["tenant_id", "subscription_id", "status"],
    )
    op.create_table(
        "usage_meter_events",
        sa.Column("id", sa.String(length=36), nullable=False),
        sa.Column("tenant_id", sa.String(length=64), nullable=False),
        sa.Column("subscription_id", sa.String(length=36), nullable=False),
        sa.Column("entitlement_id", sa.String(length=36), nullable=False),
        sa.Column("event_type", sa.String(length=16), nullable=False),
        sa.Column("metric_key", sa.String(length=120), nullable=False),
        sa.Column("quantity", sa.Numeric(20, 6), nullable=False),
        sa.Column("unit", sa.String(length=40), nullable=False),
        sa.Column("period_key", sa.String(length=32), nullable=False),
        sa.Column("occurred_at", sa.DateTime(timezone=True), nullable=False),
        sa.Column("source_system", sa.String(length=80), nullable=False),
        sa.Column("idempotency_key", sa.String(length=160), nullable=False),
        sa.Column("request_fingerprint", sa.String(length=80), nullable=False),
        sa.Column("reversal_of_event_id", sa.String(length=36), nullable=True),
        sa.Column("subject_type", sa.String(length=60), nullable=True),
        sa.Column("subject_id", sa.String(length=160), nullable=True),
        sa.Column("actor_type", sa.String(length=20), nullable=False),
        sa.Column("actor_id", sa.String(length=120), nullable=False),
        sa.Column("correlation_id", sa.String(length=120), nullable=True),
        sa.Column("trace_id", sa.String(length=120), nullable=True),
        sa.Column(
            "metadata_json", sa.JSON(), nullable=False, server_default=_json_object_default()
        ),
        sa.Column(
            "recorded_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()
        ),
        sa.CheckConstraint(
            "event_type IN ('usage', 'credit', 'adjustment', 'reversal')",
            name="ck_usage_meter_events_type",
        ),
        sa.CheckConstraint(
            "(event_type = 'usage' AND quantity > 0) OR "
            "(event_type = 'credit' AND quantity < 0) OR "
            "(event_type IN ('adjustment', 'reversal') AND quantity <> 0)",
            name="ck_usage_meter_events_quantity",
        ),
        sa.CheckConstraint(
            "(event_type = 'reversal' AND reversal_of_event_id IS NOT NULL) OR "
            "(event_type != 'reversal' AND reversal_of_event_id IS NULL)",
            name="ck_usage_meter_events_reversal",
        ),
        sa.CheckConstraint(
            "(subject_type IS NULL AND subject_id IS NULL) OR "
            "(subject_type IS NOT NULL AND subject_id IS NOT NULL)",
            name="ck_usage_meter_events_subject_pair",
        ),
        sa.CheckConstraint(
            "actor_type IN ('system', 'user', 'integration', 'admin')",
            name="ck_usage_meter_events_actor_type",
        ),
        sa.CheckConstraint(
            "length(trim(metric_key)) > 0 AND length(trim(unit)) > 0 "
            "AND length(trim(period_key)) > 0 AND length(trim(source_system)) > 0 "
            "AND length(trim(idempotency_key)) > 0 "
            "AND length(trim(request_fingerprint)) > 0",
            name="ck_usage_meter_events_keys",
        ),
        sa.ForeignKeyConstraint(
            ["tenant_id", "subscription_id"],
            ["tenant_subscriptions.tenant_id", "tenant_subscriptions.id"],
            name="fk_usage_meter_events_tenant_subscription",
            ondelete="RESTRICT",
        ),
        sa.ForeignKeyConstraint(
            ["tenant_id", "subscription_id", "entitlement_id"],
            [
                "commercial_entitlements.tenant_id",
                "commercial_entitlements.subscription_id",
                "commercial_entitlements.id",
            ],
            name="fk_usage_meter_events_tenant_entitlement",
            ondelete="RESTRICT",
        ),
        sa.ForeignKeyConstraint(
            ["tenant_id", "subscription_id", "entitlement_id", "reversal_of_event_id"],
            [
                "usage_meter_events.tenant_id",
                "usage_meter_events.subscription_id",
                "usage_meter_events.entitlement_id",
                "usage_meter_events.id",
            ],
            name="fk_usage_meter_events_tenant_reversal",
            ondelete="RESTRICT",
        ),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint("tenant_id", "id", name="uq_usage_meter_events_tenant_id"),
        sa.UniqueConstraint(
            "tenant_id",
            "subscription_id",
            "id",
            name="uq_usage_meter_events_tenant_subscription_id",
        ),
        sa.UniqueConstraint(
            "tenant_id",
            "subscription_id",
            "entitlement_id",
            "id",
            name="uq_usage_meter_events_entitlement_id",
        ),
        sa.UniqueConstraint(
            "tenant_id",
            "source_system",
            "idempotency_key",
            name="uq_usage_meter_events_source_request",
        ),
    )
    op.create_index(
        "ix_usage_meter_events_quota_window",
        "usage_meter_events",
        ["tenant_id", "subscription_id", "metric_key", "period_key", "occurred_at"],
    )
    op.create_index(
        "ix_usage_meter_events_correlation",
        "usage_meter_events",
        ["tenant_id", "correlation_id", "occurred_at"],
    )
    op.create_table(
        "commercial_cost_events",
        sa.Column("id", sa.String(length=36), nullable=False),
        sa.Column("tenant_id", sa.String(length=64), nullable=False),
        sa.Column("subscription_id", sa.String(length=36), nullable=True),
        sa.Column("usage_event_id", sa.String(length=36), nullable=True),
        sa.Column("event_type", sa.String(length=16), nullable=False),
        sa.Column("cost_category", sa.String(length=32), nullable=False),
        sa.Column("quantity", sa.Numeric(20, 6), nullable=False),
        sa.Column("unit", sa.String(length=40), nullable=False),
        sa.Column("unit_cost", sa.Numeric(20, 8), nullable=False),
        sa.Column("cost_amount", sa.Numeric(20, 4), nullable=False),
        sa.Column("original_currency", sa.String(length=3), nullable=False),
        sa.Column("reporting_amount", sa.Numeric(20, 4), nullable=False),
        sa.Column("reporting_currency", sa.String(length=3), nullable=False),
        sa.Column("fx_rate", sa.Numeric(20, 8), nullable=False),
        sa.Column("provider", sa.String(length=120), nullable=True),
        sa.Column("sku", sa.String(length=120), nullable=True),
        sa.Column("model_name", sa.String(length=120), nullable=True),
        sa.Column("allocation_key", sa.String(length=160), nullable=False),
        sa.Column("occurred_at", sa.DateTime(timezone=True), nullable=False),
        sa.Column("source_system", sa.String(length=80), nullable=False),
        sa.Column("idempotency_key", sa.String(length=160), nullable=False),
        sa.Column("request_fingerprint", sa.String(length=80), nullable=False),
        sa.Column("reversal_of_cost_event_id", sa.String(length=36), nullable=True),
        sa.Column("correlation_id", sa.String(length=120), nullable=True),
        sa.Column("trace_id", sa.String(length=120), nullable=True),
        sa.Column(
            "metadata_json", sa.JSON(), nullable=False, server_default=_json_object_default()
        ),
        sa.Column(
            "recorded_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()
        ),
        sa.CheckConstraint(
            "event_type IN ('incurred', 'credit', 'adjustment', 'reversal')",
            name="ck_commercial_cost_events_type",
        ),
        sa.CheckConstraint(
            "cost_category IN ('ai_inference', 'ocr', 'storage', 'connector', "
            "'support', 'implementation', 'infrastructure', 'payment', 'other')",
            name="ck_commercial_cost_events_category",
        ),
        sa.CheckConstraint(
            "quantity > 0 AND unit_cost >= 0 AND fx_rate > 0",
            name="ck_commercial_cost_events_values",
        ),
        sa.CheckConstraint(
            "(event_type = 'incurred' AND cost_amount >= 0 AND reporting_amount >= 0) OR "
            "(event_type = 'credit' AND cost_amount <= 0 AND reporting_amount <= 0) OR "
            "(event_type IN ('adjustment', 'reversal') AND cost_amount <> 0 "
            "AND reporting_amount <> 0)",
            name="ck_commercial_cost_events_amount_direction",
        ),
        sa.CheckConstraint(
            "(event_type = 'reversal' AND reversal_of_cost_event_id IS NOT NULL) OR "
            "(event_type != 'reversal' AND reversal_of_cost_event_id IS NULL)",
            name="ck_commercial_cost_events_reversal",
        ),
        sa.CheckConstraint(
            "usage_event_id IS NULL OR subscription_id IS NOT NULL",
            name="ck_commercial_cost_events_usage_pair",
        ),
        sa.CheckConstraint(
            "length(trim(unit)) > 0 AND length(trim(source_system)) > 0 "
            "AND length(trim(idempotency_key)) > 0 "
            "AND length(trim(request_fingerprint)) > 0 "
            "AND length(trim(original_currency)) = 3 "
            "AND length(trim(reporting_currency)) = 3",
            name="ck_commercial_cost_events_keys",
        ),
        sa.ForeignKeyConstraint(
            ["tenant_id", "subscription_id"],
            ["tenant_subscriptions.tenant_id", "tenant_subscriptions.id"],
            name="fk_commercial_cost_events_tenant_subscription",
            ondelete="RESTRICT",
        ),
        sa.ForeignKeyConstraint(
            ["tenant_id", "subscription_id", "usage_event_id"],
            [
                "usage_meter_events.tenant_id",
                "usage_meter_events.subscription_id",
                "usage_meter_events.id",
            ],
            name="fk_commercial_cost_events_tenant_usage",
            ondelete="RESTRICT",
        ),
        sa.ForeignKeyConstraint(
            ["tenant_id", "reversal_of_cost_event_id"],
            ["commercial_cost_events.tenant_id", "commercial_cost_events.id"],
            name="fk_commercial_cost_events_tenant_reversal",
            ondelete="RESTRICT",
        ),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint(
            "tenant_id", "id", name="uq_commercial_cost_events_tenant_id"
        ),
        sa.UniqueConstraint(
            "tenant_id",
            "source_system",
            "idempotency_key",
            name="uq_commercial_cost_events_source_request",
        ),
    )
    op.create_index(
        "ix_commercial_cost_events_tenant_period",
        "commercial_cost_events",
        ["tenant_id", "occurred_at", "cost_category"],
    )
    op.create_index(
        "ix_commercial_cost_events_subscription_period",
        "commercial_cost_events",
        ["tenant_id", "subscription_id", "occurred_at"],
    )
    op.create_index(
        "ix_commercial_cost_events_allocation",
        "commercial_cost_events",
        ["tenant_id", "allocation_key", "occurred_at"],
    )
    op.execute(
        """
        CREATE OR REPLACE FUNCTION prevent_commercial_events_mutation()
        RETURNS trigger AS $$
        BEGIN
            RAISE EXCEPTION '% is append-only', TG_TABLE_NAME;
        END;
        $$ LANGUAGE plpgsql
        """
    )
    for table_name in ("usage_meter_events", "commercial_cost_events"):
        op.execute(
            f"""
            CREATE TRIGGER trg_{table_name}_append_only
            BEFORE UPDATE OR DELETE ON {table_name}
            FOR EACH ROW EXECUTE FUNCTION prevent_commercial_events_mutation()
            """
        )


def downgrade() -> None:
    _require_postgresql()
    _require_empty_commercial_domain_for_downgrade()
    for table_name in ("commercial_cost_events", "usage_meter_events"):
        op.execute(f"DROP TRIGGER IF EXISTS trg_{table_name}_append_only ON {table_name}")
    op.execute("DROP FUNCTION IF EXISTS prevent_commercial_events_mutation()")
    op.drop_table("commercial_cost_events")
    op.drop_table("usage_meter_events")
    op.drop_table("commercial_entitlements")
    op.drop_table("tenant_subscriptions")
    op.drop_table("tenant_commercial_plans")
