"""create whatsapp reseller tables

Revision ID: e7f2b4a6c8d9
Revises: ff1a2b3c4d5e
Create Date: 2026-05-03 11:00:00.000000

Creates the 16 tables that back the WhatsApp Business Reseller Manager
module. All creates are idempotent — repeated bootstraps succeed even if
a subset of tables already exists.
"""
from typing import Sequence, Union

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision: str = "e7f2b4a6c8d9"
down_revision: Union[str, Sequence[str], None] = "ff1a2b3c4d5e"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def _has_table(table: str) -> bool:
    bind = op.get_bind()
    inspector = sa.inspect(bind)
    try:
        return inspector.has_table(table)
    except Exception:  # noqa: BLE001
        return False


def _safe_create_index(name: str, table: str, cols: list, unique: bool = False) -> None:
    try:
        op.create_index(name, table, cols, unique=unique)
    except Exception:  # noqa: BLE001
        pass


def _ts_cols() -> list:
    return [
        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(),
        ),
    ]


def upgrade() -> None:
    # ---- whatsapp_packages ----
    if not _has_table("whatsapp_packages"):
        op.create_table(
            "whatsapp_packages",
            sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
            sa.Column("slug", sa.String(length=64), nullable=False),
            sa.Column("name", sa.String(length=128), nullable=False),
            sa.Column("description", sa.Text(), nullable=True),
            sa.Column("tagline", sa.String(length=255), nullable=True),
            sa.Column("max_devices", sa.Integer(), nullable=False, server_default="1"),
            sa.Column("devices_unlimited", sa.Boolean(), nullable=False, server_default=sa.false()),
            sa.Column("messages_per_month", sa.Integer(), nullable=False, server_default="1000"),
            sa.Column("messages_unlimited", sa.Boolean(), nullable=False, server_default=sa.false()),
            sa.Column("max_contacts", sa.Integer(), nullable=False, server_default="1000"),
            sa.Column("contacts_unlimited", sa.Boolean(), nullable=False, server_default=sa.false()),
            sa.Column("max_campaigns", sa.Integer(), nullable=False, server_default="5"),
            sa.Column("campaigns_unlimited", sa.Boolean(), nullable=False, server_default=sa.false()),
            sa.Column("max_workflows", sa.Integer(), nullable=False, server_default="5"),
            sa.Column("workflows_unlimited", sa.Boolean(), nullable=False, server_default=sa.false()),
            sa.Column("max_team_operators", sa.Integer(), nullable=False, server_default="1"),
            sa.Column("team_operators_unlimited", sa.Boolean(), nullable=False, server_default=sa.false()),
            sa.Column("chat_retention_days", sa.Integer(), nullable=False, server_default="90"),
            sa.Column("auto_reply_allowed", sa.Boolean(), nullable=False, server_default=sa.false()),
            sa.Column("ai_assistant_allowed", sa.Boolean(), nullable=False, server_default=sa.false()),
            sa.Column("workflow_automation_allowed", sa.Boolean(), nullable=False, server_default=sa.false()),
            sa.Column("webhook_access_allowed", sa.Boolean(), nullable=False, server_default=sa.false()),
            sa.Column("api_access_allowed", sa.Boolean(), nullable=False, server_default=sa.false()),
            sa.Column("media_sending_allowed", sa.Boolean(), nullable=False, server_default=sa.true()),
            sa.Column("template_messages_allowed", sa.Boolean(), nullable=False, server_default=sa.false()),
            sa.Column("whmcs_product_id", sa.Integer(), nullable=True),
            sa.Column("terminate_action", sa.String(length=16), nullable=False, server_default="archive"),
            sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.true()),
            sa.Column("is_featured", sa.Boolean(), nullable=False, server_default=sa.false()),
            sa.Column("price_per_month", sa.String(length=32), nullable=True),
            sa.Column("sort_order", sa.Integer(), nullable=False, server_default="0"),
            *_ts_cols(),
            sa.PrimaryKeyConstraint("id"),
            sa.UniqueConstraint("slug", name="uq_whatsapp_packages_slug"),
        )
        _safe_create_index("ix_whatsapp_packages_id", "whatsapp_packages", ["id"])
        _safe_create_index("ix_whatsapp_packages_slug", "whatsapp_packages", ["slug"])
        _safe_create_index("ix_whatsapp_packages_whmcs_product_id", "whatsapp_packages", ["whmcs_product_id"])

    # ---- whatsapp_client_subscriptions ----
    if not _has_table("whatsapp_client_subscriptions"):
        op.create_table(
            "whatsapp_client_subscriptions",
            sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
            sa.Column("user_id", sa.String(length=255), nullable=False),
            sa.Column("user_email", sa.String(length=255), nullable=True),
            sa.Column("package_id", sa.Integer(), nullable=False),
            sa.Column("whmcs_service_id", sa.Integer(), nullable=True),
            sa.Column("whmcs_product_id", sa.Integer(), nullable=True),
            sa.Column("status", sa.String(length=16), nullable=False, server_default="active"),
            sa.Column("devices_used", sa.Integer(), nullable=False, server_default="0"),
            sa.Column("messages_used_month", sa.Integer(), nullable=False, server_default="0"),
            sa.Column("contacts_used", sa.Integer(), nullable=False, server_default="0"),
            sa.Column("campaigns_used", sa.Integer(), nullable=False, server_default="0"),
            sa.Column("workflows_used", sa.Integer(), nullable=False, server_default="0"),
            sa.Column("usage_period_start", sa.DateTime(timezone=True), nullable=True),
            sa.Column("notes", sa.Text(), nullable=True),
            sa.Column("activated_at", sa.DateTime(timezone=True), nullable=True),
            sa.Column("suspended_at", sa.DateTime(timezone=True), nullable=True),
            sa.Column("terminated_at", sa.DateTime(timezone=True), nullable=True),
            *_ts_cols(),
            sa.PrimaryKeyConstraint("id"),
        )
        _safe_create_index("ix_wa_subs_id", "whatsapp_client_subscriptions", ["id"])
        _safe_create_index("ix_wa_subs_user_id", "whatsapp_client_subscriptions", ["user_id"])
        _safe_create_index("ix_wa_subs_status", "whatsapp_client_subscriptions", ["status"])
        _safe_create_index("ix_wa_subs_user_email", "whatsapp_client_subscriptions", ["user_email"])
        _safe_create_index("ix_wa_subs_package_id", "whatsapp_client_subscriptions", ["package_id"])
        _safe_create_index("ix_wa_subs_whmcs_service_id", "whatsapp_client_subscriptions", ["whmcs_service_id"])

    # ---- whatsapp_devices ----
    if not _has_table("whatsapp_devices"):
        op.create_table(
            "whatsapp_devices",
            sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
            sa.Column("subscription_id", sa.Integer(), nullable=False),
            sa.Column("user_id", sa.String(length=255), nullable=False),
            sa.Column("device_slug", sa.String(length=64), nullable=False),
            sa.Column("display_name", sa.String(length=128), nullable=False, server_default="WhatsApp Device"),
            sa.Column("phone_number", sa.String(length=40), nullable=True),
            sa.Column("status", sa.String(length=24), nullable=False, server_default="pending"),
            sa.Column("upstream_session_id", sa.String(length=128), nullable=True),
            sa.Column("qr_payload", sa.Text(), nullable=True),
            sa.Column("qr_expires_at", sa.DateTime(timezone=True), nullable=True),
            sa.Column("last_seen_at", sa.DateTime(timezone=True), nullable=True),
            sa.Column("last_error", sa.Text(), nullable=True),
            sa.Column("archived", sa.Boolean(), nullable=False, server_default=sa.false()),
            *_ts_cols(),
            sa.PrimaryKeyConstraint("id"),
        )
        _safe_create_index("ix_wa_devices_id", "whatsapp_devices", ["id"])
        _safe_create_index("ix_wa_devices_subscription", "whatsapp_devices", ["subscription_id"])
        _safe_create_index("ix_wa_devices_user", "whatsapp_devices", ["user_id"])
        _safe_create_index("ix_wa_devices_upstream", "whatsapp_devices", ["upstream_session_id"])
        _safe_create_index("ix_wa_devices_slug", "whatsapp_devices", ["device_slug"])

    # ---- whatsapp_device_sessions ----
    if not _has_table("whatsapp_device_sessions"):
        op.create_table(
            "whatsapp_device_sessions",
            sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
            sa.Column("device_id", sa.Integer(), nullable=False),
            sa.Column("event", sa.String(length=32), nullable=False),
            sa.Column("detail", sa.Text(), nullable=True),
            sa.Column("occurred_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
            sa.PrimaryKeyConstraint("id"),
        )
        _safe_create_index("ix_wa_device_sessions_id", "whatsapp_device_sessions", ["id"])
        _safe_create_index("ix_wa_device_sessions_device", "whatsapp_device_sessions", ["device_id"])

    # ---- whatsapp_contacts ----
    if not _has_table("whatsapp_contacts"):
        op.create_table(
            "whatsapp_contacts",
            sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
            sa.Column("subscription_id", sa.Integer(), nullable=False),
            sa.Column("user_id", sa.String(length=255), nullable=False),
            sa.Column("phone_number", sa.String(length=40), nullable=False),
            sa.Column("display_name", sa.String(length=128), nullable=True),
            sa.Column("tags", sa.String(length=255), nullable=True),
            sa.Column("notes", sa.Text(), nullable=True),
            sa.Column("is_opted_out", sa.Boolean(), nullable=False, server_default=sa.false()),
            sa.Column("last_message_at", sa.DateTime(timezone=True), nullable=True),
            *_ts_cols(),
            sa.PrimaryKeyConstraint("id"),
        )
        _safe_create_index("ix_wa_contacts_id", "whatsapp_contacts", ["id"])
        _safe_create_index("ix_wa_contacts_subscription", "whatsapp_contacts", ["subscription_id"])
        _safe_create_index("ix_wa_contacts_phone", "whatsapp_contacts", ["phone_number"])

    # ---- whatsapp_messages ----
    if not _has_table("whatsapp_messages"):
        op.create_table(
            "whatsapp_messages",
            sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
            sa.Column("subscription_id", sa.Integer(), nullable=False),
            sa.Column("device_id", sa.Integer(), nullable=True),
            sa.Column("contact_id", sa.Integer(), nullable=True),
            sa.Column("user_id", sa.String(length=255), nullable=False),
            sa.Column("direction", sa.String(length=16), nullable=False),
            sa.Column("message_type", sa.String(length=24), nullable=False, server_default="text"),
            sa.Column("body", sa.Text(), nullable=True),
            sa.Column("media_url", sa.Text(), nullable=True),
            sa.Column("media_mime", sa.String(length=64), nullable=True),
            sa.Column("delivery_status", sa.String(length=24), nullable=False, server_default="queued"),
            sa.Column("upstream_message_id", sa.String(length=128), nullable=True),
            sa.Column("error_detail", sa.Text(), nullable=True),
            sa.Column("is_read", sa.Boolean(), nullable=False, server_default=sa.false()),
            *_ts_cols(),
            sa.PrimaryKeyConstraint("id"),
        )
        _safe_create_index("ix_wa_messages_id", "whatsapp_messages", ["id"])
        _safe_create_index("ix_wa_messages_subscription", "whatsapp_messages", ["subscription_id"])
        _safe_create_index("ix_wa_messages_device", "whatsapp_messages", ["device_id"])
        _safe_create_index("ix_wa_messages_contact", "whatsapp_messages", ["contact_id"])
        _safe_create_index("ix_wa_messages_created", "whatsapp_messages", ["created_at"])
        _safe_create_index("ix_wa_messages_upstream", "whatsapp_messages", ["upstream_message_id"])

    # ---- whatsapp_campaigns ----
    if not _has_table("whatsapp_campaigns"):
        op.create_table(
            "whatsapp_campaigns",
            sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
            sa.Column("subscription_id", sa.Integer(), nullable=False),
            sa.Column("user_id", sa.String(length=255), nullable=False),
            sa.Column("name", sa.String(length=128), nullable=False),
            sa.Column("message_body", sa.Text(), nullable=False),
            sa.Column("media_url", sa.Text(), nullable=True),
            sa.Column("status", sa.String(length=24), nullable=False, server_default="draft"),
            sa.Column("scheduled_at", sa.DateTime(timezone=True), nullable=True),
            sa.Column("started_at", sa.DateTime(timezone=True), nullable=True),
            sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True),
            sa.Column("total_recipients", sa.Integer(), nullable=False, server_default="0"),
            sa.Column("sent_count", sa.Integer(), nullable=False, server_default="0"),
            sa.Column("delivered_count", sa.Integer(), nullable=False, server_default="0"),
            sa.Column("replied_count", sa.Integer(), nullable=False, server_default="0"),
            sa.Column("failed_count", sa.Integer(), nullable=False, server_default="0"),
            sa.Column("throttle_per_minute", sa.Integer(), nullable=False, server_default="30"),
            sa.Column("last_error", sa.Text(), nullable=True),
            *_ts_cols(),
            sa.PrimaryKeyConstraint("id"),
        )
        _safe_create_index("ix_wa_campaigns_id", "whatsapp_campaigns", ["id"])
        _safe_create_index("ix_wa_campaigns_subscription", "whatsapp_campaigns", ["subscription_id"])

    # ---- whatsapp_campaign_recipients ----
    if not _has_table("whatsapp_campaign_recipients"):
        op.create_table(
            "whatsapp_campaign_recipients",
            sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
            sa.Column("campaign_id", sa.Integer(), nullable=False),
            sa.Column("contact_id", sa.Integer(), nullable=True),
            sa.Column("phone_number", sa.String(length=40), nullable=False),
            sa.Column("status", sa.String(length=16), nullable=False, server_default="pending"),
            sa.Column("error_detail", sa.Text(), nullable=True),
            sa.Column("sent_at", sa.DateTime(timezone=True), nullable=True),
            sa.PrimaryKeyConstraint("id"),
        )
        _safe_create_index("ix_wa_campaign_recipients_id", "whatsapp_campaign_recipients", ["id"])
        _safe_create_index("ix_wa_campaign_recipients_campaign", "whatsapp_campaign_recipients", ["campaign_id"])

    # ---- whatsapp_automation_workflows ----
    if not _has_table("whatsapp_automation_workflows"):
        op.create_table(
            "whatsapp_automation_workflows",
            sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
            sa.Column("subscription_id", sa.Integer(), nullable=False),
            sa.Column("user_id", sa.String(length=255), nullable=False),
            sa.Column("name", sa.String(length=128), nullable=False),
            sa.Column("description", sa.Text(), nullable=True),
            sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.true()),
            sa.Column("trigger_json", sa.Text(), nullable=False, server_default="{}"),
            sa.Column("actions_json", sa.Text(), nullable=False, server_default="[]"),
            sa.Column("last_run_at", sa.DateTime(timezone=True), nullable=True),
            sa.Column("run_count", sa.Integer(), nullable=False, server_default="0"),
            *_ts_cols(),
            sa.PrimaryKeyConstraint("id"),
        )
        _safe_create_index("ix_wa_workflows_id", "whatsapp_automation_workflows", ["id"])
        _safe_create_index("ix_wa_workflows_subscription", "whatsapp_automation_workflows", ["subscription_id"])

    # ---- whatsapp_workflow_runs ----
    if not _has_table("whatsapp_workflow_runs"):
        op.create_table(
            "whatsapp_workflow_runs",
            sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
            sa.Column("workflow_id", sa.Integer(), nullable=False),
            sa.Column("subscription_id", sa.Integer(), nullable=False),
            sa.Column("trigger_detail", sa.Text(), nullable=True),
            sa.Column("result", sa.String(length=16), nullable=False, server_default="ok"),
            sa.Column("error_detail", sa.Text(), nullable=True),
            sa.Column("executed_actions", sa.Integer(), nullable=False, server_default="0"),
            sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
            sa.PrimaryKeyConstraint("id"),
        )
        _safe_create_index("ix_wa_workflow_runs_id", "whatsapp_workflow_runs", ["id"])
        _safe_create_index("ix_wa_workflow_runs_workflow", "whatsapp_workflow_runs", ["workflow_id"])
        _safe_create_index("ix_wa_workflow_runs_created", "whatsapp_workflow_runs", ["created_at"])

    # ---- whatsapp_ai_settings ----
    if not _has_table("whatsapp_ai_settings"):
        op.create_table(
            "whatsapp_ai_settings",
            sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
            sa.Column("subscription_id", sa.Integer(), nullable=False),
            sa.Column("auto_reply_enabled", sa.Boolean(), nullable=False, server_default=sa.false()),
            sa.Column("knowledge_base", sa.Text(), nullable=True),
            sa.Column("faq_training", sa.Text(), nullable=True),
            sa.Column("tone", sa.String(length=32), nullable=False, server_default="friendly"),
            sa.Column("language", sa.String(length=8), nullable=False, server_default="en"),
            sa.Column("confidence_threshold", sa.Integer(), nullable=False, server_default="60"),
            sa.Column("business_hours_only", sa.Boolean(), nullable=False, server_default=sa.false()),
            sa.Column("business_hours_start", sa.String(length=8), nullable=False, server_default="09:00"),
            sa.Column("business_hours_end", sa.String(length=8), nullable=False, server_default="18:00"),
            sa.Column("human_handoff_enabled", sa.Boolean(), nullable=False, server_default=sa.true()),
            sa.Column("preview_before_sending", sa.Boolean(), nullable=False, server_default=sa.true()),
            sa.Column("sentiment_detection_enabled", sa.Boolean(), nullable=False, server_default=sa.true()),
            sa.Column("suggested_replies_enabled", sa.Boolean(), nullable=False, server_default=sa.true()),
            sa.Column("spam_detection_enabled", sa.Boolean(), nullable=False, server_default=sa.true()),
            sa.Column("lead_qualification_enabled", sa.Boolean(), nullable=False, server_default=sa.false()),
            *_ts_cols(),
            sa.PrimaryKeyConstraint("id"),
            sa.UniqueConstraint("subscription_id", name="uq_wa_ai_settings_subscription"),
        )
        _safe_create_index("ix_wa_ai_settings_id", "whatsapp_ai_settings", ["id"])

    # ---- whatsapp_api_tokens ----
    if not _has_table("whatsapp_api_tokens"):
        op.create_table(
            "whatsapp_api_tokens",
            sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
            sa.Column("subscription_id", sa.Integer(), nullable=False),
            sa.Column("user_id", sa.String(length=255), nullable=False),
            sa.Column("label", sa.String(length=64), nullable=False, server_default="Default"),
            sa.Column("token_prefix", sa.String(length=16), nullable=False),
            sa.Column("token_hash", sa.String(length=128), nullable=False),
            sa.Column("last_used_at", sa.DateTime(timezone=True), nullable=True),
            sa.Column("revoked_at", sa.DateTime(timezone=True), nullable=True),
            sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
            sa.PrimaryKeyConstraint("id"),
            sa.UniqueConstraint("token_hash", name="uq_wa_api_tokens_hash"),
        )
        _safe_create_index("ix_wa_api_tokens_id", "whatsapp_api_tokens", ["id"])
        _safe_create_index("ix_wa_api_tokens_subscription", "whatsapp_api_tokens", ["subscription_id"])

    # ---- whatsapp_webhooks ----
    if not _has_table("whatsapp_webhooks"):
        op.create_table(
            "whatsapp_webhooks",
            sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
            sa.Column("subscription_id", sa.Integer(), nullable=False),
            sa.Column("user_id", sa.String(length=255), nullable=False),
            sa.Column("target_url", sa.Text(), nullable=False),
            sa.Column("signing_secret", sa.String(length=128), nullable=True),
            sa.Column(
                "events",
                sa.String(length=255),
                nullable=False,
                server_default="message.received,message.sent",
            ),
            sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.true()),
            sa.Column("last_delivery_at", sa.DateTime(timezone=True), nullable=True),
            sa.Column("last_delivery_status", sa.String(length=16), nullable=True),
            sa.Column("last_error", sa.Text(), nullable=True),
            *_ts_cols(),
            sa.PrimaryKeyConstraint("id"),
        )
        _safe_create_index("ix_wa_webhooks_id", "whatsapp_webhooks", ["id"])
        _safe_create_index("ix_wa_webhooks_subscription", "whatsapp_webhooks", ["subscription_id"])

    # ---- whatsapp_usage_snapshots ----
    if not _has_table("whatsapp_usage_snapshots"):
        op.create_table(
            "whatsapp_usage_snapshots",
            sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
            sa.Column("subscription_id", sa.Integer(), nullable=False),
            sa.Column("snapshot_date", sa.String(length=10), nullable=False),
            sa.Column("devices_used", sa.Integer(), nullable=False, server_default="0"),
            sa.Column("messages_sent", sa.Integer(), nullable=False, server_default="0"),
            sa.Column("messages_received", sa.Integer(), nullable=False, server_default="0"),
            sa.Column("campaigns_run", sa.Integer(), nullable=False, server_default="0"),
            sa.Column("workflow_runs", sa.Integer(), nullable=False, server_default="0"),
            sa.Column("api_calls", sa.Integer(), nullable=False, server_default="0"),
            sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
            sa.PrimaryKeyConstraint("id"),
        )
        _safe_create_index("ix_wa_usage_id", "whatsapp_usage_snapshots", ["id"])
        _safe_create_index(
            "ix_wa_usage_subscription_date",
            "whatsapp_usage_snapshots",
            ["subscription_id", "snapshot_date"],
        )

    # ---- whatsapp_api_logs ----
    if not _has_table("whatsapp_api_logs"):
        op.create_table(
            "whatsapp_api_logs",
            sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
            sa.Column("subscription_id", sa.Integer(), nullable=True),
            sa.Column("user_id", sa.String(length=255), nullable=True),
            sa.Column("channel", sa.String(length=16), nullable=False, server_default="upstream"),
            sa.Column("method", sa.String(length=8), nullable=False, server_default="GET"),
            sa.Column("endpoint", sa.String(length=255), nullable=False),
            sa.Column("status_code", sa.Integer(), nullable=True),
            sa.Column("duration_ms", sa.Integer(), nullable=True),
            sa.Column("error_detail", sa.Text(), nullable=True),
            sa.Column("request_preview", sa.Text(), nullable=True),
            sa.Column("response_preview", sa.Text(), nullable=True),
            sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
            sa.PrimaryKeyConstraint("id"),
        )
        _safe_create_index("ix_wa_api_logs_id", "whatsapp_api_logs", ["id"])
        _safe_create_index("ix_wa_api_logs_created", "whatsapp_api_logs", ["created_at"])
        _safe_create_index("ix_wa_api_logs_subscription", "whatsapp_api_logs", ["subscription_id"])

    # ---- whatsapp_admin_audit_logs ----
    if not _has_table("whatsapp_admin_audit_logs"):
        op.create_table(
            "whatsapp_admin_audit_logs",
            sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
            sa.Column("actor_user_id", sa.String(length=255), nullable=True),
            sa.Column("actor_email", sa.String(length=255), nullable=True),
            sa.Column("action", sa.String(length=64), nullable=False),
            sa.Column("target_type", sa.String(length=64), nullable=True),
            sa.Column("target_id", sa.String(length=64), nullable=True),
            sa.Column("detail", sa.Text(), nullable=True),
            sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
            sa.PrimaryKeyConstraint("id"),
        )
        _safe_create_index("ix_wa_audit_id", "whatsapp_admin_audit_logs", ["id"])
        _safe_create_index("ix_wa_audit_created", "whatsapp_admin_audit_logs", ["created_at"])


def downgrade() -> None:
    for table in [
        "whatsapp_admin_audit_logs",
        "whatsapp_api_logs",
        "whatsapp_usage_snapshots",
        "whatsapp_webhooks",
        "whatsapp_api_tokens",
        "whatsapp_ai_settings",
        "whatsapp_workflow_runs",
        "whatsapp_automation_workflows",
        "whatsapp_campaign_recipients",
        "whatsapp_campaigns",
        "whatsapp_messages",
        "whatsapp_contacts",
        "whatsapp_device_sessions",
        "whatsapp_devices",
        "whatsapp_client_subscriptions",
        "whatsapp_packages",
    ]:
        try:
            op.drop_table(table)
        except Exception:  # noqa: BLE001
            pass