"""add loyalty module tables

Revision ID: 0fb5d6d6ff97
Revises: zd3n4o5p6q7r8
Create Date: 2026-01-28 22:55:34.074321

"""
from collections.abc import Sequence

import sqlalchemy as sa
from sqlalchemy.dialects import postgresql, sqlite

from alembic import op

# revision identifiers, used by Alembic.
revision: str = "0fb5d6d6ff97"
down_revision: str | None = "zd3n4o5p6q7r8"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


def upgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table("loyalty_programs",
    sa.Column("id", sa.Integer(), nullable=False),
    sa.Column("store_id", sa.Integer(), nullable=False),
    sa.Column("loyalty_type", sa.String(length=20), nullable=False),
    sa.Column("stamps_target", sa.Integer(), nullable=False, comment="Number of stamps needed for reward"),
    sa.Column("stamps_reward_description", sa.String(length=255), nullable=False, comment="Description of stamp reward"),
    sa.Column("stamps_reward_value_cents", sa.Integer(), nullable=True, comment="Value of stamp reward in cents (for analytics)"),
    sa.Column("points_per_euro", sa.Integer(), nullable=False, comment="Points earned per euro spent"),
    sa.Column("points_rewards", sqlite.JSON(), nullable=False, comment="List of point rewards: [{id, name, points_required, description}]"),
    sa.Column("cooldown_minutes", sa.Integer(), nullable=False, comment="Minutes between stamps for same card"),
    sa.Column("max_daily_stamps", sa.Integer(), nullable=False, comment="Maximum stamps per card per day"),
    sa.Column("require_staff_pin", sa.Boolean(), nullable=False, comment="Require staff PIN for stamp/points operations"),
    sa.Column("card_name", sa.String(length=100), nullable=True, comment="Display name for loyalty card"),
    sa.Column("card_color", sa.String(length=7), nullable=False, comment="Primary color for card (hex)"),
    sa.Column("card_secondary_color", sa.String(length=7), nullable=True, comment="Secondary color for card (hex)"),
    sa.Column("logo_url", sa.String(length=500), nullable=True, comment="URL to store logo for card"),
    sa.Column("hero_image_url", sa.String(length=500), nullable=True, comment="URL to hero image for card"),
    sa.Column("google_issuer_id", sa.String(length=100), nullable=True, comment="Google Wallet Issuer ID"),
    sa.Column("google_class_id", sa.String(length=200), nullable=True, comment="Google Wallet Loyalty Class ID"),
    sa.Column("apple_pass_type_id", sa.String(length=100), nullable=True, comment="Apple Wallet Pass Type ID"),
    sa.Column("terms_text", sa.Text(), nullable=True, comment="Loyalty program terms and conditions"),
    sa.Column("privacy_url", sa.String(length=500), nullable=True, comment="URL to privacy policy"),
    sa.Column("is_active", sa.Boolean(), nullable=False),
    sa.Column("activated_at", sa.DateTime(timezone=True), nullable=True, comment="When program was first activated"),
    sa.Column("created_at", sa.DateTime(), nullable=False),
    sa.Column("updated_at", sa.DateTime(), nullable=False),
    sa.ForeignKeyConstraint(["store_id"], ["stores.id"], ondelete="CASCADE"),
    sa.PrimaryKeyConstraint("id")
    )
    op.create_index("idx_loyalty_program_store_active", "loyalty_programs", ["store_id", "is_active"], unique=False)
    op.create_index(op.f("ix_loyalty_programs_id"), "loyalty_programs", ["id"], unique=False)
    op.create_index(op.f("ix_loyalty_programs_is_active"), "loyalty_programs", ["is_active"], unique=False)
    op.create_index(op.f("ix_loyalty_programs_store_id"), "loyalty_programs", ["store_id"], unique=True)
    op.create_table("loyalty_cards",
    sa.Column("id", sa.Integer(), nullable=False),
    sa.Column("customer_id", sa.Integer(), nullable=False),
    sa.Column("program_id", sa.Integer(), nullable=False),
    sa.Column("store_id", sa.Integer(), nullable=False, comment="Denormalized for query performance"),
    sa.Column("card_number", sa.String(length=20), nullable=False, comment="Human-readable card number"),
    sa.Column("qr_code_data", sa.String(length=50), nullable=False, comment="Data encoded in QR code for scanning"),
    sa.Column("stamp_count", sa.Integer(), nullable=False, comment="Current stamps toward next reward"),
    sa.Column("total_stamps_earned", sa.Integer(), nullable=False, comment="Lifetime stamps earned"),
    sa.Column("stamps_redeemed", sa.Integer(), nullable=False, comment="Total rewards redeemed (stamps reset on redemption)"),
    sa.Column("points_balance", sa.Integer(), nullable=False, comment="Current available points"),
    sa.Column("total_points_earned", sa.Integer(), nullable=False, comment="Lifetime points earned"),
    sa.Column("points_redeemed", sa.Integer(), nullable=False, comment="Lifetime points redeemed"),
    sa.Column("google_object_id", sa.String(length=200), nullable=True, comment="Google Wallet Loyalty Object ID"),
    sa.Column("google_object_jwt", sa.String(length=2000), nullable=True, comment="JWT for Google Wallet 'Add to Wallet' button"),
    sa.Column("apple_serial_number", sa.String(length=100), nullable=True, comment="Apple Wallet pass serial number"),
    sa.Column("apple_auth_token", sa.String(length=100), nullable=True, comment="Apple Wallet authentication token for updates"),
    sa.Column("last_stamp_at", sa.DateTime(timezone=True), nullable=True, comment="Last stamp added (for cooldown)"),
    sa.Column("last_points_at", sa.DateTime(timezone=True), nullable=True, comment="Last points earned"),
    sa.Column("last_redemption_at", sa.DateTime(timezone=True), nullable=True, comment="Last reward redemption"),
    sa.Column("is_active", sa.Boolean(), nullable=False),
    sa.Column("created_at", sa.DateTime(), nullable=False),
    sa.Column("updated_at", sa.DateTime(), nullable=False),
    sa.ForeignKeyConstraint(["customer_id"], ["customers.id"], ondelete="CASCADE"),
    sa.ForeignKeyConstraint(["program_id"], ["loyalty_programs.id"], ondelete="CASCADE"),
    sa.ForeignKeyConstraint(["store_id"], ["stores.id"], ondelete="CASCADE"),
    sa.PrimaryKeyConstraint("id")
    )
    op.create_index("idx_loyalty_card_customer_program", "loyalty_cards", ["customer_id", "program_id"], unique=True)
    op.create_index("idx_loyalty_card_store_active", "loyalty_cards", ["store_id", "is_active"], unique=False)
    op.create_index(op.f("ix_loyalty_cards_apple_serial_number"), "loyalty_cards", ["apple_serial_number"], unique=True)
    op.create_index(op.f("ix_loyalty_cards_card_number"), "loyalty_cards", ["card_number"], unique=True)
    op.create_index(op.f("ix_loyalty_cards_customer_id"), "loyalty_cards", ["customer_id"], unique=False)
    op.create_index(op.f("ix_loyalty_cards_google_object_id"), "loyalty_cards", ["google_object_id"], unique=False)
    op.create_index(op.f("ix_loyalty_cards_id"), "loyalty_cards", ["id"], unique=False)
    op.create_index(op.f("ix_loyalty_cards_is_active"), "loyalty_cards", ["is_active"], unique=False)
    op.create_index(op.f("ix_loyalty_cards_program_id"), "loyalty_cards", ["program_id"], unique=False)
    op.create_index(op.f("ix_loyalty_cards_qr_code_data"), "loyalty_cards", ["qr_code_data"], unique=True)
    op.create_index(op.f("ix_loyalty_cards_store_id"), "loyalty_cards", ["store_id"], unique=False)
    op.create_table("staff_pins",
    sa.Column("id", sa.Integer(), nullable=False),
    sa.Column("program_id", sa.Integer(), nullable=False),
    sa.Column("store_id", sa.Integer(), nullable=False, comment="Denormalized for query performance"),
    sa.Column("name", sa.String(length=100), nullable=False, comment="Staff member name"),
    sa.Column("staff_id", sa.String(length=50), nullable=True, comment="Optional staff ID/employee number"),
    sa.Column("pin_hash", sa.String(length=255), nullable=False, comment="bcrypt hash of PIN"),
    sa.Column("failed_attempts", sa.Integer(), nullable=False, comment="Consecutive failed PIN attempts"),
    sa.Column("locked_until", sa.DateTime(timezone=True), nullable=True, comment="Lockout expires at this time"),
    sa.Column("last_used_at", sa.DateTime(timezone=True), nullable=True, comment="Last successful use of PIN"),
    sa.Column("is_active", sa.Boolean(), nullable=False),
    sa.Column("created_at", sa.DateTime(), nullable=False),
    sa.Column("updated_at", sa.DateTime(), nullable=False),
    sa.ForeignKeyConstraint(["program_id"], ["loyalty_programs.id"], ondelete="CASCADE"),
    sa.ForeignKeyConstraint(["store_id"], ["stores.id"], ondelete="CASCADE"),
    sa.PrimaryKeyConstraint("id")
    )
    op.create_index("idx_staff_pin_program_active", "staff_pins", ["program_id", "is_active"], unique=False)
    op.create_index("idx_staff_pin_store_active", "staff_pins", ["store_id", "is_active"], unique=False)
    op.create_index(op.f("ix_staff_pins_id"), "staff_pins", ["id"], unique=False)
    op.create_index(op.f("ix_staff_pins_is_active"), "staff_pins", ["is_active"], unique=False)
    op.create_index(op.f("ix_staff_pins_program_id"), "staff_pins", ["program_id"], unique=False)
    op.create_index(op.f("ix_staff_pins_staff_id"), "staff_pins", ["staff_id"], unique=False)
    op.create_index(op.f("ix_staff_pins_store_id"), "staff_pins", ["store_id"], unique=False)
    op.create_table("apple_device_registrations",
    sa.Column("id", sa.Integer(), nullable=False),
    sa.Column("card_id", sa.Integer(), nullable=False),
    sa.Column("device_library_identifier", sa.String(length=100), nullable=False, comment="Unique identifier for the device/library"),
    sa.Column("push_token", sa.String(length=100), nullable=False, comment="APNs push token for this device"),
    sa.Column("created_at", sa.DateTime(), nullable=False),
    sa.Column("updated_at", sa.DateTime(), nullable=False),
    sa.ForeignKeyConstraint(["card_id"], ["loyalty_cards.id"], ondelete="CASCADE"),
    sa.PrimaryKeyConstraint("id")
    )
    op.create_index("idx_apple_device_card", "apple_device_registrations", ["device_library_identifier", "card_id"], unique=True)
    op.create_index(op.f("ix_apple_device_registrations_card_id"), "apple_device_registrations", ["card_id"], unique=False)
    op.create_index(op.f("ix_apple_device_registrations_device_library_identifier"), "apple_device_registrations", ["device_library_identifier"], unique=False)
    op.create_index(op.f("ix_apple_device_registrations_id"), "apple_device_registrations", ["id"], unique=False)
    op.create_table("loyalty_transactions",
    sa.Column("id", sa.Integer(), nullable=False),
    sa.Column("card_id", sa.Integer(), nullable=False),
    sa.Column("store_id", sa.Integer(), nullable=False, comment="Denormalized for query performance"),
    sa.Column("staff_pin_id", sa.Integer(), nullable=True, comment="Staff PIN used for this operation"),
    sa.Column("transaction_type", sa.String(length=30), nullable=False),
    sa.Column("stamps_delta", sa.Integer(), nullable=False, comment="Change in stamps (+1 for earn, -N for redeem)"),
    sa.Column("points_delta", sa.Integer(), nullable=False, comment="Change in points (+N for earn, -N for redeem)"),
    sa.Column("stamps_balance_after", sa.Integer(), nullable=True, comment="Stamp count after this transaction"),
    sa.Column("points_balance_after", sa.Integer(), nullable=True, comment="Points balance after this transaction"),
    sa.Column("purchase_amount_cents", sa.Integer(), nullable=True, comment="Purchase amount in cents (for points calculation)"),
    sa.Column("order_reference", sa.String(length=100), nullable=True, comment="Reference to order that triggered points"),
    sa.Column("reward_id", sa.String(length=50), nullable=True, comment="ID of redeemed reward (from program.points_rewards)"),
    sa.Column("reward_description", sa.String(length=255), nullable=True, comment="Description of redeemed reward"),
    sa.Column("ip_address", sa.String(length=45), nullable=True, comment="IP address of requester (IPv4 or IPv6)"),
    sa.Column("user_agent", sa.String(length=500), nullable=True, comment="User agent string"),
    sa.Column("notes", sa.Text(), nullable=True, comment="Additional notes (e.g., reason for adjustment)"),
    sa.Column("transaction_at", sa.DateTime(timezone=True), nullable=False, comment="When the transaction occurred (may differ from created_at)"),
    sa.Column("created_at", sa.DateTime(), nullable=False),
    sa.Column("updated_at", sa.DateTime(), nullable=False),
    sa.ForeignKeyConstraint(["card_id"], ["loyalty_cards.id"], ondelete="CASCADE"),
    sa.ForeignKeyConstraint(["staff_pin_id"], ["staff_pins.id"], ondelete="SET NULL"),
    sa.ForeignKeyConstraint(["store_id"], ["stores.id"], ondelete="CASCADE"),
    sa.PrimaryKeyConstraint("id")
    )
    op.create_index("idx_loyalty_tx_card_type", "loyalty_transactions", ["card_id", "transaction_type"], unique=False)
    op.create_index("idx_loyalty_tx_type_date", "loyalty_transactions", ["transaction_type", "transaction_at"], unique=False)
    op.create_index("idx_loyalty_tx_store_date", "loyalty_transactions", ["store_id", "transaction_at"], unique=False)
    op.create_index(op.f("ix_loyalty_transactions_card_id"), "loyalty_transactions", ["card_id"], unique=False)
    op.create_index(op.f("ix_loyalty_transactions_id"), "loyalty_transactions", ["id"], unique=False)
    op.create_index(op.f("ix_loyalty_transactions_order_reference"), "loyalty_transactions", ["order_reference"], unique=False)
    op.create_index(op.f("ix_loyalty_transactions_staff_pin_id"), "loyalty_transactions", ["staff_pin_id"], unique=False)
    op.create_index(op.f("ix_loyalty_transactions_transaction_at"), "loyalty_transactions", ["transaction_at"], unique=False)
    op.create_index(op.f("ix_loyalty_transactions_transaction_type"), "loyalty_transactions", ["transaction_type"], unique=False)
    op.create_index(op.f("ix_loyalty_transactions_store_id"), "loyalty_transactions", ["store_id"], unique=False)
    op.alter_column("admin_menu_configs", "platform_id",
               existing_type=sa.INTEGER(),
               comment="Platform scope - applies to users/stores of this platform",
               existing_comment="Platform scope - applies to all platform admins of this platform",
               existing_nullable=True)
    op.alter_column("admin_menu_configs", "user_id",
               existing_type=sa.INTEGER(),
               comment="User scope - applies to this specific super admin (admin frontend only)",
               existing_comment="User scope - applies to this specific super admin",
               existing_nullable=True)
    op.alter_column("admin_menu_configs", "created_at",
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=sa.DateTime(),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.alter_column("admin_menu_configs", "updated_at",
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=sa.DateTime(),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.drop_index("idx_admin_menu_configs_frontend_type", table_name="admin_menu_configs")
    op.drop_index("idx_admin_menu_configs_menu_item_id", table_name="admin_menu_configs")
    op.drop_index("idx_admin_menu_configs_platform_id", table_name="admin_menu_configs")
    op.drop_index("idx_admin_menu_configs_user_id", table_name="admin_menu_configs")
    op.create_index(op.f("ix_admin_menu_configs_frontend_type"), "admin_menu_configs", ["frontend_type"], unique=False)
    op.create_index(op.f("ix_admin_menu_configs_id"), "admin_menu_configs", ["id"], unique=False)
    op.create_index(op.f("ix_admin_menu_configs_menu_item_id"), "admin_menu_configs", ["menu_item_id"], unique=False)
    op.create_index(op.f("ix_admin_menu_configs_platform_id"), "admin_menu_configs", ["platform_id"], unique=False)
    op.create_index(op.f("ix_admin_menu_configs_user_id"), "admin_menu_configs", ["user_id"], unique=False)
    op.alter_column("admin_platforms", "created_at",
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=sa.DateTime(),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.alter_column("admin_platforms", "updated_at",
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=sa.DateTime(),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.drop_index("idx_admin_platforms_platform_id", table_name="admin_platforms")
    op.drop_index("idx_admin_platforms_user_id", table_name="admin_platforms")
    op.create_index(op.f("ix_admin_platforms_id"), "admin_platforms", ["id"], unique=False)
    op.create_index(op.f("ix_admin_platforms_platform_id"), "admin_platforms", ["platform_id"], unique=False)
    op.create_index(op.f("ix_admin_platforms_user_id"), "admin_platforms", ["user_id"], unique=False)
    op.alter_column("content_pages", "platform_id",
               existing_type=sa.INTEGER(),
               comment="Platform this page belongs to",
               existing_nullable=False)
    op.alter_column("content_pages", "store_id",
               existing_type=sa.INTEGER(),
               comment="Store this page belongs to (NULL for platform/default pages)",
               existing_nullable=True)
    op.alter_column("content_pages", "is_platform_page",
               existing_type=sa.BOOLEAN(),
               comment="True = platform marketing page (homepage, pricing); False = store default or override",
               existing_nullable=False,
               existing_server_default=sa.text("false"))
    op.alter_column("platform_modules", "created_at",
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=sa.DateTime(),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.alter_column("platform_modules", "updated_at",
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=sa.DateTime(),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.create_index(op.f("ix_platform_modules_id"), "platform_modules", ["id"], unique=False)
    op.alter_column("platforms", "code",
               existing_type=sa.VARCHAR(length=50),
               comment="Unique platform identifier (e.g., 'oms', 'loyalty', 'sites')",
               existing_nullable=False)
    op.alter_column("platforms", "name",
               existing_type=sa.VARCHAR(length=100),
               comment="Display name (e.g., 'Wizamart OMS')",
               existing_nullable=False)
    op.alter_column("platforms", "description",
               existing_type=sa.TEXT(),
               comment="Platform description for admin/marketing purposes",
               existing_nullable=True)
    op.alter_column("platforms", "domain",
               existing_type=sa.VARCHAR(length=255),
               comment="Production domain (e.g., 'omsflow.lu', 'rewardflow.lu')",
               existing_nullable=True)
    op.alter_column("platforms", "path_prefix",
               existing_type=sa.VARCHAR(length=50),
               comment="Development path prefix (e.g., 'oms' for localhost:9999/oms/*)",
               existing_nullable=True)
    op.alter_column("platforms", "logo",
               existing_type=sa.VARCHAR(length=500),
               comment="Logo URL for light mode",
               existing_nullable=True)
    op.alter_column("platforms", "logo_dark",
               existing_type=sa.VARCHAR(length=500),
               comment="Logo URL for dark mode",
               existing_nullable=True)
    op.alter_column("platforms", "favicon",
               existing_type=sa.VARCHAR(length=500),
               comment="Favicon URL",
               existing_nullable=True)
    op.alter_column("platforms", "theme_config",
               existing_type=postgresql.JSON(astext_type=sa.Text()),
               comment="Theme configuration (colors, fonts, etc.)",
               existing_nullable=True)
    op.alter_column("platforms", "default_language",
               existing_type=sa.VARCHAR(length=5),
               comment="Default language code (e.g., 'fr', 'en', 'de')",
               existing_nullable=False,
               existing_server_default=sa.text("'fr'::character varying"))
    op.alter_column("platforms", "supported_languages",
               existing_type=postgresql.JSON(astext_type=sa.Text()),
               comment="List of supported language codes",
               existing_nullable=False)
    op.alter_column("platforms", "is_active",
               existing_type=sa.BOOLEAN(),
               comment="Whether the platform is active and accessible",
               existing_nullable=False,
               existing_server_default=sa.text("true"))
    op.alter_column("platforms", "is_public",
               existing_type=sa.BOOLEAN(),
               comment="Whether the platform is visible in public listings",
               existing_nullable=False,
               existing_server_default=sa.text("true"))
    op.alter_column("platforms", "settings",
               existing_type=postgresql.JSON(astext_type=sa.Text()),
               comment="Platform-specific settings and feature flags",
               existing_nullable=True)
    op.alter_column("platforms", "created_at",
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=sa.DateTime(),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.alter_column("platforms", "updated_at",
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=sa.DateTime(),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.create_index(op.f("ix_platforms_id"), "platforms", ["id"], unique=False)
    op.alter_column("subscription_tiers", "platform_id",
               existing_type=sa.INTEGER(),
               comment="Platform this tier belongs to (NULL = global tier)",
               existing_nullable=True)
    op.alter_column("subscription_tiers", "cms_pages_limit",
               existing_type=sa.INTEGER(),
               comment="Total CMS pages limit (NULL = unlimited)",
               existing_nullable=True)
    op.alter_column("subscription_tiers", "cms_custom_pages_limit",
               existing_type=sa.INTEGER(),
               comment="Custom pages limit, excluding overrides (NULL = unlimited)",
               existing_nullable=True)
    op.drop_index("ix_subscription_tiers_code", table_name="subscription_tiers")
    op.create_index(op.f("ix_subscription_tiers_code"), "subscription_tiers", ["code"], unique=False)
    op.alter_column("users", "is_super_admin",
               existing_type=sa.BOOLEAN(),
               comment=None,
               existing_comment="Whether this admin has access to all platforms (super admin)",
               existing_nullable=False,
               existing_server_default=sa.text("false"))
    op.alter_column("store_platforms", "store_id",
               existing_type=sa.INTEGER(),
               comment="Reference to the store",
               existing_nullable=False)
    op.alter_column("store_platforms", "platform_id",
               existing_type=sa.INTEGER(),
               comment="Reference to the platform",
               existing_nullable=False)
    op.alter_column("store_platforms", "tier_id",
               existing_type=sa.INTEGER(),
               comment="Platform-specific subscription tier",
               existing_nullable=True)
    op.alter_column("store_platforms", "is_active",
               existing_type=sa.BOOLEAN(),
               comment="Whether the store is active on this platform",
               existing_nullable=False,
               existing_server_default=sa.text("true"))
    op.alter_column("store_platforms", "is_primary",
               existing_type=sa.BOOLEAN(),
               comment="Whether this is the store's primary platform",
               existing_nullable=False,
               existing_server_default=sa.text("false"))
    op.alter_column("store_platforms", "custom_subdomain",
               existing_type=sa.VARCHAR(length=100),
               comment="Platform-specific subdomain (if different from main subdomain)",
               existing_nullable=True)
    op.alter_column("store_platforms", "settings",
               existing_type=postgresql.JSON(astext_type=sa.Text()),
               comment="Platform-specific store settings",
               existing_nullable=True)
    op.alter_column("store_platforms", "joined_at",
               existing_type=postgresql.TIMESTAMP(timezone=True),
               comment="When the store joined this platform",
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.alter_column("store_platforms", "created_at",
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=sa.DateTime(),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.alter_column("store_platforms", "updated_at",
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=sa.DateTime(),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.create_index(op.f("ix_store_platforms_id"), "store_platforms", ["id"], unique=False)
    # ### end Alembic commands ###


def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_index(op.f("ix_store_platforms_id"), table_name="store_platforms")
    op.alter_column("store_platforms", "updated_at",
               existing_type=sa.DateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.alter_column("store_platforms", "created_at",
               existing_type=sa.DateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.alter_column("store_platforms", "joined_at",
               existing_type=postgresql.TIMESTAMP(timezone=True),
               comment=None,
               existing_comment="When the store joined this platform",
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.alter_column("store_platforms", "settings",
               existing_type=postgresql.JSON(astext_type=sa.Text()),
               comment=None,
               existing_comment="Platform-specific store settings",
               existing_nullable=True)
    op.alter_column("store_platforms", "custom_subdomain",
               existing_type=sa.VARCHAR(length=100),
               comment=None,
               existing_comment="Platform-specific subdomain (if different from main subdomain)",
               existing_nullable=True)
    op.alter_column("store_platforms", "is_primary",
               existing_type=sa.BOOLEAN(),
               comment=None,
               existing_comment="Whether this is the store's primary platform",
               existing_nullable=False,
               existing_server_default=sa.text("false"))
    op.alter_column("store_platforms", "is_active",
               existing_type=sa.BOOLEAN(),
               comment=None,
               existing_comment="Whether the store is active on this platform",
               existing_nullable=False,
               existing_server_default=sa.text("true"))
    op.alter_column("store_platforms", "tier_id",
               existing_type=sa.INTEGER(),
               comment=None,
               existing_comment="Platform-specific subscription tier",
               existing_nullable=True)
    op.alter_column("store_platforms", "platform_id",
               existing_type=sa.INTEGER(),
               comment=None,
               existing_comment="Reference to the platform",
               existing_nullable=False)
    op.alter_column("store_platforms", "store_id",
               existing_type=sa.INTEGER(),
               comment=None,
               existing_comment="Reference to the store",
               existing_nullable=False)
    op.alter_column("users", "is_super_admin",
               existing_type=sa.BOOLEAN(),
               comment="Whether this admin has access to all platforms (super admin)",
               existing_nullable=False,
               existing_server_default=sa.text("false"))
    op.drop_index(op.f("ix_subscription_tiers_code"), table_name="subscription_tiers")
    op.create_index("ix_subscription_tiers_code", "subscription_tiers", ["code"], unique=True)
    op.alter_column("subscription_tiers", "cms_custom_pages_limit",
               existing_type=sa.INTEGER(),
               comment=None,
               existing_comment="Custom pages limit, excluding overrides (NULL = unlimited)",
               existing_nullable=True)
    op.alter_column("subscription_tiers", "cms_pages_limit",
               existing_type=sa.INTEGER(),
               comment=None,
               existing_comment="Total CMS pages limit (NULL = unlimited)",
               existing_nullable=True)
    op.alter_column("subscription_tiers", "platform_id",
               existing_type=sa.INTEGER(),
               comment=None,
               existing_comment="Platform this tier belongs to (NULL = global tier)",
               existing_nullable=True)
    op.drop_index(op.f("ix_platforms_id"), table_name="platforms")
    op.alter_column("platforms", "updated_at",
               existing_type=sa.DateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.alter_column("platforms", "created_at",
               existing_type=sa.DateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.alter_column("platforms", "settings",
               existing_type=postgresql.JSON(astext_type=sa.Text()),
               comment=None,
               existing_comment="Platform-specific settings and feature flags",
               existing_nullable=True)
    op.alter_column("platforms", "is_public",
               existing_type=sa.BOOLEAN(),
               comment=None,
               existing_comment="Whether the platform is visible in public listings",
               existing_nullable=False,
               existing_server_default=sa.text("true"))
    op.alter_column("platforms", "is_active",
               existing_type=sa.BOOLEAN(),
               comment=None,
               existing_comment="Whether the platform is active and accessible",
               existing_nullable=False,
               existing_server_default=sa.text("true"))
    op.alter_column("platforms", "supported_languages",
               existing_type=postgresql.JSON(astext_type=sa.Text()),
               comment=None,
               existing_comment="List of supported language codes",
               existing_nullable=False)
    op.alter_column("platforms", "default_language",
               existing_type=sa.VARCHAR(length=5),
               comment=None,
               existing_comment="Default language code (e.g., 'fr', 'en', 'de')",
               existing_nullable=False,
               existing_server_default=sa.text("'fr'::character varying"))
    op.alter_column("platforms", "theme_config",
               existing_type=postgresql.JSON(astext_type=sa.Text()),
               comment=None,
               existing_comment="Theme configuration (colors, fonts, etc.)",
               existing_nullable=True)
    op.alter_column("platforms", "favicon",
               existing_type=sa.VARCHAR(length=500),
               comment=None,
               existing_comment="Favicon URL",
               existing_nullable=True)
    op.alter_column("platforms", "logo_dark",
               existing_type=sa.VARCHAR(length=500),
               comment=None,
               existing_comment="Logo URL for dark mode",
               existing_nullable=True)
    op.alter_column("platforms", "logo",
               existing_type=sa.VARCHAR(length=500),
               comment=None,
               existing_comment="Logo URL for light mode",
               existing_nullable=True)
    op.alter_column("platforms", "path_prefix",
               existing_type=sa.VARCHAR(length=50),
               comment=None,
               existing_comment="Development path prefix (e.g., 'oms' for localhost:9999/oms/*)",
               existing_nullable=True)
    op.alter_column("platforms", "domain",
               existing_type=sa.VARCHAR(length=255),
               comment=None,
               existing_comment="Production domain (e.g., 'omsflow.lu', 'rewardflow.lu')",
               existing_nullable=True)
    op.alter_column("platforms", "description",
               existing_type=sa.TEXT(),
               comment=None,
               existing_comment="Platform description for admin/marketing purposes",
               existing_nullable=True)
    op.alter_column("platforms", "name",
               existing_type=sa.VARCHAR(length=100),
               comment=None,
               existing_comment="Display name (e.g., 'Wizamart OMS')",
               existing_nullable=False)
    op.alter_column("platforms", "code",
               existing_type=sa.VARCHAR(length=50),
               comment=None,
               existing_comment="Unique platform identifier (e.g., 'oms', 'loyalty', 'sites')",
               existing_nullable=False)
    op.drop_index(op.f("ix_platform_modules_id"), table_name="platform_modules")
    op.alter_column("platform_modules", "updated_at",
               existing_type=sa.DateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.alter_column("platform_modules", "created_at",
               existing_type=sa.DateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.alter_column("content_pages", "is_platform_page",
               existing_type=sa.BOOLEAN(),
               comment=None,
               existing_comment="True = platform marketing page (homepage, pricing); False = store default or override",
               existing_nullable=False,
               existing_server_default=sa.text("false"))
    op.alter_column("content_pages", "store_id",
               existing_type=sa.INTEGER(),
               comment=None,
               existing_comment="Store this page belongs to (NULL for platform/default pages)",
               existing_nullable=True)
    op.alter_column("content_pages", "platform_id",
               existing_type=sa.INTEGER(),
               comment=None,
               existing_comment="Platform this page belongs to",
               existing_nullable=False)
    op.drop_index(op.f("ix_admin_platforms_user_id"), table_name="admin_platforms")
    op.drop_index(op.f("ix_admin_platforms_platform_id"), table_name="admin_platforms")
    op.drop_index(op.f("ix_admin_platforms_id"), table_name="admin_platforms")
    op.create_index("idx_admin_platforms_user_id", "admin_platforms", ["user_id"], unique=False)
    op.create_index("idx_admin_platforms_platform_id", "admin_platforms", ["platform_id"], unique=False)
    op.alter_column("admin_platforms", "updated_at",
               existing_type=sa.DateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.alter_column("admin_platforms", "created_at",
               existing_type=sa.DateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.drop_index(op.f("ix_admin_menu_configs_user_id"), table_name="admin_menu_configs")
    op.drop_index(op.f("ix_admin_menu_configs_platform_id"), table_name="admin_menu_configs")
    op.drop_index(op.f("ix_admin_menu_configs_menu_item_id"), table_name="admin_menu_configs")
    op.drop_index(op.f("ix_admin_menu_configs_id"), table_name="admin_menu_configs")
    op.drop_index(op.f("ix_admin_menu_configs_frontend_type"), table_name="admin_menu_configs")
    op.create_index("idx_admin_menu_configs_user_id", "admin_menu_configs", ["user_id"], unique=False)
    op.create_index("idx_admin_menu_configs_platform_id", "admin_menu_configs", ["platform_id"], unique=False)
    op.create_index("idx_admin_menu_configs_menu_item_id", "admin_menu_configs", ["menu_item_id"], unique=False)
    op.create_index("idx_admin_menu_configs_frontend_type", "admin_menu_configs", ["frontend_type"], unique=False)
    op.alter_column("admin_menu_configs", "updated_at",
               existing_type=sa.DateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.alter_column("admin_menu_configs", "created_at",
               existing_type=sa.DateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=False,
               existing_server_default=sa.text("now()"))
    op.alter_column("admin_menu_configs", "user_id",
               existing_type=sa.INTEGER(),
               comment="User scope - applies to this specific super admin",
               existing_comment="User scope - applies to this specific super admin (admin frontend only)",
               existing_nullable=True)
    op.alter_column("admin_menu_configs", "platform_id",
               existing_type=sa.INTEGER(),
               comment="Platform scope - applies to all platform admins of this platform",
               existing_comment="Platform scope - applies to users/stores of this platform",
               existing_nullable=True)
    op.drop_index(op.f("ix_loyalty_transactions_store_id"), table_name="loyalty_transactions")
    op.drop_index(op.f("ix_loyalty_transactions_transaction_type"), table_name="loyalty_transactions")
    op.drop_index(op.f("ix_loyalty_transactions_transaction_at"), table_name="loyalty_transactions")
    op.drop_index(op.f("ix_loyalty_transactions_staff_pin_id"), table_name="loyalty_transactions")
    op.drop_index(op.f("ix_loyalty_transactions_order_reference"), table_name="loyalty_transactions")
    op.drop_index(op.f("ix_loyalty_transactions_id"), table_name="loyalty_transactions")
    op.drop_index(op.f("ix_loyalty_transactions_card_id"), table_name="loyalty_transactions")
    op.drop_index("idx_loyalty_tx_store_date", table_name="loyalty_transactions")
    op.drop_index("idx_loyalty_tx_type_date", table_name="loyalty_transactions")
    op.drop_index("idx_loyalty_tx_card_type", table_name="loyalty_transactions")
    op.drop_table("loyalty_transactions")
    op.drop_index(op.f("ix_apple_device_registrations_id"), table_name="apple_device_registrations")
    op.drop_index(op.f("ix_apple_device_registrations_device_library_identifier"), table_name="apple_device_registrations")
    op.drop_index(op.f("ix_apple_device_registrations_card_id"), table_name="apple_device_registrations")
    op.drop_index("idx_apple_device_card", table_name="apple_device_registrations")
    op.drop_table("apple_device_registrations")
    op.drop_index(op.f("ix_staff_pins_store_id"), table_name="staff_pins")
    op.drop_index(op.f("ix_staff_pins_staff_id"), table_name="staff_pins")
    op.drop_index(op.f("ix_staff_pins_program_id"), table_name="staff_pins")
    op.drop_index(op.f("ix_staff_pins_is_active"), table_name="staff_pins")
    op.drop_index(op.f("ix_staff_pins_id"), table_name="staff_pins")
    op.drop_index("idx_staff_pin_store_active", table_name="staff_pins")
    op.drop_index("idx_staff_pin_program_active", table_name="staff_pins")
    op.drop_table("staff_pins")
    op.drop_index(op.f("ix_loyalty_cards_store_id"), table_name="loyalty_cards")
    op.drop_index(op.f("ix_loyalty_cards_qr_code_data"), table_name="loyalty_cards")
    op.drop_index(op.f("ix_loyalty_cards_program_id"), table_name="loyalty_cards")
    op.drop_index(op.f("ix_loyalty_cards_is_active"), table_name="loyalty_cards")
    op.drop_index(op.f("ix_loyalty_cards_id"), table_name="loyalty_cards")
    op.drop_index(op.f("ix_loyalty_cards_google_object_id"), table_name="loyalty_cards")
    op.drop_index(op.f("ix_loyalty_cards_customer_id"), table_name="loyalty_cards")
    op.drop_index(op.f("ix_loyalty_cards_card_number"), table_name="loyalty_cards")
    op.drop_index(op.f("ix_loyalty_cards_apple_serial_number"), table_name="loyalty_cards")
    op.drop_index("idx_loyalty_card_store_active", table_name="loyalty_cards")
    op.drop_index("idx_loyalty_card_customer_program", table_name="loyalty_cards")
    op.drop_table("loyalty_cards")
    op.drop_index(op.f("ix_loyalty_programs_store_id"), table_name="loyalty_programs")
    op.drop_index(op.f("ix_loyalty_programs_is_active"), table_name="loyalty_programs")
    op.drop_index(op.f("ix_loyalty_programs_id"), table_name="loyalty_programs")
    op.drop_index("idx_loyalty_program_store_active", table_name="loyalty_programs")
    op.drop_table("loyalty_programs")
    # ### end Alembic commands ###
