<?php
/**
 * Plugin Name: Subscription Manager for Stripe
 * Plugin URI: https://profinto.com/plugins/
 * Description: Standalone Stripe Billing integration for WordPress. Handles recurring subscriptions with iDEAL, Bancontact, SEPA Direct Debit, usage-based billing, and a built-in affiliate system. Requires WooCommerce for order management.
 * Version: 2.2.3
 * Author: Profinto
 * Author URI: https://profinto.com
 * License: GPL v2 or later
 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain: subscription-manager-for-stripe
 * Domain Path: /languages
 * Requires at least: 6.0
 * Requires PHP: 8.0
 * WC requires at least: 8.0
 * WC tested up to: 9.0
 */

// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

// — Freemius SDK global helper ————————————————————————————————
// Provides strp_sub_fs() for easy access and enables free/premium version switching.
if ( function_exists( 'strp_sub_fs' ) ) {
    strp_sub_fs()->set_basename( false, __FILE__ );
} else {
    if ( ! function_exists( 'strp_sub_fs' ) ) {
        /**
         * Global Freemius helper function.
         *
         * @return \Freemius|null
         */
        function strp_sub_fs() {
            if ( class_exists( 'STRP_SUB_Freemius' ) ) {
                return STRP_SUB_Freemius::get_fs();
            }
            return null;
        }
    }

    // Declare HPOS and Cart/Checkout Blocks compatibility
add_action( 'before_woocommerce_init', function () {
    if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {
        \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility(
            'custom_order_tables',
            __FILE__,
            true
        );
        \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility(
            'cart_checkout_blocks',
            __FILE__,
            false
        );
    }
} );

// — Plugin constants (new canonical names) ————————————————
define( 'STRP_SUB_VERSION',         '2.2.3' );
define( 'STRP_SUB_PLUGIN_FILE',     __FILE__ );
define( 'STRP_SUB_PLUGIN_DIR',      plugin_dir_path( __FILE__ ) );
define( 'STRP_SUB_PLUGIN_URL',      plugin_dir_url( __FILE__ ) );
define( 'STRP_SUB_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );

/**
 * Main plugin class.
 */
class STRP_SUB_Manager {

    private static $instance = null;

    public static function get_instance() {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __clone() {}

    public function __wakeup() {
        throw new \RuntimeException( 'Cannot unserialize singleton.' );
    }

    private function __construct() {
        $this->init_hooks();
        if ( ! $this->load_stripe_library() ) {
            return; // Halt loading — includes() would fatal without the Stripe SDK.
        }
        $this->includes();
    }

    /**
     * WooCommerce is required for order management.
     * Uses class_exists() to support network-activated WooCommerce on multisite.
     */
    private function is_woocommerce_active() {
        return class_exists( 'WooCommerce' );
    }

    public function woocommerce_missing_notice() {
        echo '<div class="notice notice-error"><p>' . esc_html__( 'Stripe Subscription Manager requires WooCommerce to be installed and active for order management.', 'subscription-manager-for-stripe' ) . '</p></div>';
    }

    /**
     * Load the Stripe PHP SDK via Composer autoloader.
     *
     * @return bool TRUE when the SDK is (or was already) available, FALSE when the
     *              vendor folder is missing. On FALSE the constructor will bail out
     *              before loading any SDK-dependent include files.
     */
    private function load_stripe_library() {
        if ( class_exists( 'STRP_SUB_Vendor\Stripe\Stripe' ) ) {
            return true; // Already loaded (e.g. by another plugin sharing the SDK).
        }
        $autoloader = STRP_SUB_PLUGIN_DIR . 'vendor-prefixed/autoload.php';
        if ( file_exists( $autoloader ) ) {
            require_once $autoloader;
            return true;
        }
        // Vendor folder missing — show a clear admin notice instead of a fatal error.
        add_action( 'admin_notices', function () {
            echo '<div class="notice notice-error"><p>' . esc_html__( 'Stripe Subscription Manager is missing its dependencies. Please reinstall the plugin.', 'subscription-manager-for-stripe' ) . '</p></div>';
        } );
        return false;
    }

    private function includes() {
        // — Core infrastructure ——————————————————————————
        require_once STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-logger.php';
        require_once STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-settings.php';

        // — OAuth 2.0 (load before Stripe API so the token is available) —
        require_once STRP_SUB_PLUGIN_DIR . 'includes/class-stripe-sub-oauth.php';

        // — Stripe API wrapper ———————————————————————————
        require_once STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-stripe-api.php';

        // — Business logic ———————————————————————————————
        require_once STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-product-handler.php';
        require_once STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-checkout-handler.php';

        // — Webhook Retry (Starter+) ——————————————————————
        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-webhook-retry__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }

        require_once STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-webhook-handler.php';
        require_once STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-subscription-manager.php';
        require_once STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-customer-portal.php';

        // — Metered Billing (Premium only) ——————————————————————
        $_strp_metered = STRP_SUB_PLUGIN_DIR . 'includes/class-stripe-sub-metered-billing__premium_only.php';
        if ( file_exists( $_strp_metered ) ) {
            require_once $_strp_metered;
        }
        require_once STRP_SUB_PLUGIN_DIR . 'includes/class-stripe-sub-freemius.php';

        // — WooCommerce email integration ————————————————
        require_once STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-wc-emails.php';
        // — Affiliate Plugin Integration Helpers ————————
        require_once STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-affiliate-coupon-helper.php';

        // — Analytics Dashboard (Starter+) ——————————————————————
        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-analytics-dashboard__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }

        // — Webhook Health Monitor (Starter+) ————————————————
        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-webhook-monitor__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }

        // — Dashboard Widget (Starter+) ——————————————————————
        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-dashboard-widget__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }

        // — Setup Wizard (Starter+) ——————————————————————————
        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-setup-wizard__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }

        // — Email Template Customizer (Starter+) ——————————————
        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-email-customizer__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }

        // — Contextual Help Tabs (Starter+) ———————————————————
        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-help-tabs__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }

        // — Cancellation Retention Flow (Premium only) ————————————
        $_strp_cancel = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-cancellation-flow__premium_only.php';
        if ( file_exists( $_strp_cancel ) ) {
            require_once $_strp_cancel;
        }

        // — Affiliate System (Professional+) ————————————————————
        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-affiliate-encryption__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }

        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-affiliate-stats__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }

        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-affiliate-utilities__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }

        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-affiliate-tracker__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }

        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-affiliate-commission__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }

        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-affiliate-registration__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }

        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-affiliate-admin__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }

        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-affiliate-payouts__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }

        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-affiliate-ajax__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }

        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-affiliate-stripe__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }

        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-affiliate-reports__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }
    }

    private function init_hooks() {
        register_activation_hook( STRP_SUB_PLUGIN_FILE, array( $this, 'activate' ) );
        register_deactivation_hook( STRP_SUB_PLUGIN_FILE, array( $this, 'deactivate' ) );

        add_action( 'plugins_loaded', array( $this, 'init' ) );
        add_action( 'admin_init', array( $this, 'check_database_version' ) );
        add_action( 'admin_init', array( $this, 'maybe_migrate_retention_events' ) );
        add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) );
        add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
    }

    /**
     * Database migrations handler.
     * If the plugin logic upgrades to a new iteration, ensure the schema follows passively.
     */
    public function check_database_version() {
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }
        if ( ! class_exists( 'STRP_SUB_Webhook_Retry' ) ) {
            return;
        }
        if ( get_option( 'strp_sub_db_version' ) !== STRP_SUB_VERSION ) {
            $this->activate(); // Safe due to dbDelta conditional checks.
            update_option( 'strp_sub_db_version', STRP_SUB_VERSION );
        }
    }

    /**
     * One-time migration of retention events from user meta to the dedicated table.
     *
     * Runs on admin_init. Checks the option flag 'strp_sub_retention_migrated'
     * to ensure it only executes once. Migrates all existing user meta events
     * into {prefix}strp_sub_retention_events, then sets the flag.
     */
    public function maybe_migrate_retention_events() {
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }

        if ( get_option( 'strp_sub_retention_migrated' ) ) {
            return;
        }

        global $wpdb;

        $table_name = $wpdb->prefix . 'strp_sub_retention_events';

        // Only migrate if the table exists but is empty and user meta events exist.
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        $table_exists = $wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'" );
        if ( ! $table_exists ) {
            return; // Table not created yet; activation hook hasn't run.
        }

        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        $row_count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table_name}" );
        if ( $row_count > 0 ) {
            // Table already has data — mark as migrated and skip.
            update_option( 'strp_sub_retention_migrated', 1, true );
            return;
        }

        // Find users who have retention events in user meta.
        $users = get_users( array(
            'meta_key'     => '_strp_sub_retention_events',
            'meta_compare' => 'EXISTS',
            'fields'       => 'ID',
            'number'       => -1,
        ) );

        if ( empty( $users ) ) {
            // No legacy data to migrate.
            update_option( 'strp_sub_retention_migrated', 1, true );
            return;
        }

        foreach ( $users as $user_id ) {
            $user_events = get_user_meta( $user_id, '_strp_sub_retention_events', true );
            if ( ! is_array( $user_events ) ) {
                continue;
            }

            foreach ( $user_events as $event ) {
                $wpdb->insert(
                    $table_name,
                    array(
                        'user_id'      => $user_id,
                        'timestamp'    => ! empty( $event['timestamp'] ) ? $event['timestamp'] : current_time( 'mysql', true ),
                        'outcome'      => ! empty( $event['outcome'] ) ? $event['outcome'] : '',
                        'reason_label' => ! empty( $event['reason_label'] ) ? $event['reason_label'] : '',
                        'offer_type'   => ! empty( $event['offer_type'] ) ? $event['offer_type'] : '',
                    ),
                    array( '%d', '%s', '%s', '%s', '%s' )
                );
            }
        }

        update_option( 'strp_sub_retention_migrated', 1, true );
    }

    public function init() {
        if ( ! $this->is_woocommerce_active() ) {
            add_action( 'admin_notices', array( $this, 'woocommerce_missing_notice' ) );
            return;
        }

        load_plugin_textdomain( 'subscription-manager-for-stripe', false, dirname( STRP_SUB_PLUGIN_BASENAME ) . '/languages' );

        if ( ! class_exists( 'STRP_SUB_Settings' ) ) {
            return; // Dependency validation failed upstream, abort instantiation.
        }

        STRP_SUB_Settings::get_instance();
        if ( class_exists( 'STRP_SUB_Analytics_Dashboard' ) ) {
            STRP_SUB_Analytics_Dashboard::get_instance();
        }
        STRP_SUB_OAuth::get_instance();
        STRP_SUB_Stripe_API::get_instance();
        STRP_SUB_Product_Handler::get_instance();
        STRP_SUB_Checkout_Handler::get_instance();
        if ( class_exists( 'STRP_SUB_Webhook_Retry' ) ) {
            STRP_SUB_Webhook_Retry::get_instance();
        }
        STRP_SUB_Webhook_Handler::get_instance();
        STRP_SUB_Subscription_Manager::get_instance();
        STRP_SUB_Customer_Portal::get_instance();
        if ( class_exists( 'STRP_SUB_Metered_Billing' ) ) {
            STRP_SUB_Metered_Billing::get_instance();
        }
        STRP_SUB_Freemius::get_instance();

        do_action( 'strp_sub_fs_loaded' );

        // Multisite notice: Agency plan required for network-wide activation.
        if ( function_exists( 'strp_sub_fs' ) && strp_sub_fs() && strp_sub_fs()->is__premium_only() ) {
            if ( is_multisite() && ! STRP_SUB_Freemius::has_feature( 'agency' ) ) {
                add_action( 'admin_notices', function () {
                    echo '<div class="notice notice-warning"><p>';
                    esc_html_e( 'Stripe Subscription Manager: Multisite network activation requires the Agency plan. The plugin is running in single-site mode.', 'subscription-manager-for-stripe' );
                    echo '</p></div>';
                } );
            }
        }

        // White-label: register the plugin name filter (Agency plan).
        if ( function_exists( 'strp_sub_fs' ) && strp_sub_fs() && strp_sub_fs()->is__premium_only() ) {
            if ( STRP_SUB_Freemius::has_feature( 'agency' ) && get_option( 'strp_sub_white_label', '' ) === 'yes' ) {
                $custom_name = get_option( 'strp_sub_white_label_name', '' );
                add_filter( 'strp_sub_plugin_name', function () use ( $custom_name ) {
                    return ! empty( $custom_name ) ? $custom_name : __( 'Subscription Manager', 'subscription-manager-for-stripe' );
                } );
            }
        }

        STRP_SUB_WC_Emails::get_instance();
        if ( class_exists( 'STRP_SUB_Webhook_Monitor' ) ) {
            STRP_SUB_Webhook_Monitor::get_instance();
        }
        if ( class_exists( 'STRP_SUB_Dashboard_Widget' ) ) {
            STRP_SUB_Dashboard_Widget::get_instance();
        }
        if ( class_exists( 'STRP_SUB_Setup_Wizard' ) ) {
            STRP_SUB_Setup_Wizard::get_instance();
        }
        if ( class_exists( 'STRP_SUB_Email_Customizer' ) ) {
            STRP_SUB_Email_Customizer::get_instance();
        }
        if ( class_exists( 'STRP_SUB_Help_Tabs' ) ) {
            STRP_SUB_Help_Tabs::get_instance();
        }
        if ( class_exists( 'STRP_SUB_Cancellation_Flow' ) ) {
            STRP_SUB_Cancellation_Flow::get_instance();
        }

        // — Affiliate System (Professional+ only, premium build only) ————
        if ( function_exists( 'strp_sub_fs' ) && strp_sub_fs() && strp_sub_fs()->is__premium_only() && class_exists( 'STRP_SUB_Affiliate_Tracker' ) && STRP_SUB_Freemius::has_feature( 'professional' ) ) {
            STRP_SUB_Affiliate_Tracker::get_instance();
            STRP_SUB_Affiliate_Commission::get_instance();
            STRP_SUB_Affiliate_Registration::get_instance();
            STRP_SUB_Affiliate_Admin::get_instance();
            STRP_SUB_Affiliate_Payouts::get_instance();
            STRP_SUB_Affiliate_Ajax::get_instance();
            STRP_SUB_Affiliate_Stripe::get_instance();

            if ( ! wp_next_scheduled( 'strp_sub_cleanup_unverified_accounts' ) ) {
                wp_schedule_event( time(), 'daily', 'strp_sub_cleanup_unverified_accounts' );
            }
            add_action( 'strp_sub_cleanup_unverified_accounts', array( $this, 'cleanup_unverified_affiliate_accounts' ) );
        }
    }

    public function enqueue_frontend_scripts() {
        if ( ! function_exists( 'is_woocommerce' ) ) {
            return;
        }
        if ( ! is_woocommerce() && ! is_account_page() && ! is_checkout() ) {
            return;
        }

        wp_enqueue_style( 'strp-sub-frontend', STRP_SUB_PLUGIN_URL . 'assets/css/frontend.css', array(), STRP_SUB_VERSION );
        wp_enqueue_script( 'strp-sub-frontend', STRP_SUB_PLUGIN_URL . 'assets/js/frontend.js', array( 'jquery' ), STRP_SUB_VERSION, true );
        wp_localize_script( 'strp-sub-frontend', 'strp_sub_params', array(
            'ajax_url'              => admin_url( 'admin-ajax.php' ),
            'nonce'                 => wp_create_nonce( 'strp_sub_nonce' ),
            'i18n_no_checkout_url'  => __( 'Error: No checkout URL received.', 'subscription-manager-for-stripe' ),
            'i18n_error'            => __( 'An error occurred. Please try again.', 'subscription-manager-for-stripe' ),
            'i18n_confirm_cancel'   => __( 'Are you sure you want to cancel this subscription?', 'subscription-manager-for-stripe' ),
            'i18n_cancelled'        => __( 'Subscription cancelled successfully.', 'subscription-manager-for-stripe' ),
        ) );
    }

    public function enqueue_admin_scripts( $hook ) {
        // Load shared premium styles on WP Dashboard (for dashboard widget)
        if ( $hook === 'index.php' ) {
            if ( class_exists( 'STRP_SUB_Dashboard_Widget' ) ) {
                wp_enqueue_style( 'strp-sub-premium', STRP_SUB_PLUGIN_URL . 'assets/css/premium-shared.css', array(), STRP_SUB_VERSION );
            }
            return; // Only load shared CSS on dashboard, not full admin JS
        }

        if ( strpos( $hook, 'strp-sub' ) === false ) {
            return;
        }
        wp_enqueue_style( 'strp-sub-premium', STRP_SUB_PLUGIN_URL . 'assets/css/premium-shared.css', array(), STRP_SUB_VERSION );
        wp_enqueue_style( 'strp-sub-admin', STRP_SUB_PLUGIN_URL . 'assets/css/admin.css', array(), STRP_SUB_VERSION );
        wp_enqueue_script( 'strp-sub-admin', STRP_SUB_PLUGIN_URL . 'assets/js/admin.js', array( 'jquery' ), STRP_SUB_VERSION, true );
        wp_localize_script( 'strp-sub-admin', 'strp_sub_admin_params', array(
            'ajax_url'            => admin_url( 'admin-ajax.php' ),
            'nonce'               => wp_create_nonce( 'strp_sub_admin_nonce' ),
            'i18n_confirm_cancel' => __( 'Are you sure you want to cancel this subscription?', 'subscription-manager-for-stripe' ),
            'i18n_cancelling'     => __( 'Cancelling...', 'subscription-manager-for-stripe' ),
            'i18n_cancel'         => __( 'Cancel', 'subscription-manager-for-stripe' ),
            'i18n_cancelled'      => __( 'Subscription cancelled successfully.', 'subscription-manager-for-stripe' ),
            'i18n_error'          => __( 'An error occurred. Please try again.', 'subscription-manager-for-stripe' ),
        ) );

        // Webhook Health Monitor assets (Starter+).
        if ( class_exists( 'STRP_SUB_Webhook_Monitor' ) && strpos( $hook, 'strp-sub-webhook-health' ) !== false ) {
            wp_enqueue_script( 'strp-sub-chartjs', STRP_SUB_PLUGIN_URL . 'assets/js/vendor/chart.min.js', array(), STRP_SUB_VERSION, true );
            wp_enqueue_style( 'strp-sub-webhook-monitor', STRP_SUB_PLUGIN_URL . 'assets/css/webhook-monitor.css', array( 'strp-sub-premium' ), STRP_SUB_VERSION );
            wp_enqueue_script( 'strp-sub-webhook-monitor', STRP_SUB_PLUGIN_URL . 'assets/js/webhook-monitor.js', array( 'jquery', 'strp-sub-chartjs' ), STRP_SUB_VERSION, true );
            wp_localize_script( 'strp-sub-webhook-monitor', 'strp_sub_webhook_monitor_params', array(
                'ajax_url'           => admin_url( 'admin-ajax.php' ),
                'nonce'              => wp_create_nonce( 'strp_sub_webhook_monitor_nonce' ),
                'i18n_successful'    => __( 'Successful', 'subscription-manager-for-stripe' ),
                'i18n_failed'        => __( 'Failed', 'subscription-manager-for-stripe' ),
                'i18n_processing'    => __( 'Processing...', 'subscription-manager-for-stripe' ),
                'i18n_clearing'      => __( 'Clearing...', 'subscription-manager-for-stripe' ),
                'i18n_retry_all'     => __( 'Retry All Now', 'subscription-manager-for-stripe' ),
                'i18n_clear_resolved' => __( 'Clear Resolved', 'subscription-manager-for-stripe' ),
                'i18n_confirm_clear' => __( 'Are you sure you want to clear all resolved failures?', 'subscription-manager-for-stripe' ),
                'i18n_error'         => __( 'An error occurred. Please try again.', 'subscription-manager-for-stripe' ),
            ) );
        }

        // Email Template Customizer assets (Starter+).
        if ( class_exists( 'STRP_SUB_Email_Customizer' ) && strpos( $hook, 'strp-sub-email-templates' ) !== false ) {
            wp_enqueue_style( 'wp-color-picker' );
            wp_enqueue_style( 'strp-sub-email-customizer', STRP_SUB_PLUGIN_URL . 'assets/css/email-customizer.css', array( 'strp-sub-premium', 'wp-color-picker' ), STRP_SUB_VERSION );
            wp_enqueue_script( 'strp-sub-email-customizer', STRP_SUB_PLUGIN_URL . 'assets/js/email-customizer.js', array( 'jquery', 'wp-color-picker' ), STRP_SUB_VERSION, true );
            wp_localize_script( 'strp-sub-email-customizer', 'strp_sub_email_customizer_params', array(
                'ajax_url'       => admin_url( 'admin-ajax.php' ),
                'nonce'          => wp_create_nonce( 'strp_sub_email_preview' ),
                'i18n_saving'    => __( 'Saving...', 'subscription-manager-for-stripe' ),
                'i18n_saved'     => __( 'Template saved successfully.', 'subscription-manager-for-stripe' ),
                'i18n_error'     => __( 'An error occurred. Please try again.', 'subscription-manager-for-stripe' ),
            ) );
        }

        // Analytics Dashboard assets (Starter+).
        if ( class_exists( 'STRP_SUB_Analytics_Dashboard' ) && strpos( $hook, 'strp-sub-dashboard' ) !== false ) {
            wp_enqueue_script( 'strp-sub-chartjs', STRP_SUB_PLUGIN_URL . 'assets/js/vendor/chart.min.js', array(), STRP_SUB_VERSION, true );
            wp_enqueue_style( 'strp-sub-analytics-dashboard', STRP_SUB_PLUGIN_URL . 'assets/css/analytics-dashboard.css', array( 'strp-sub-premium' ), STRP_SUB_VERSION );
            wp_enqueue_script( 'strp-sub-analytics-dashboard', STRP_SUB_PLUGIN_URL . 'assets/js/analytics-dashboard.js', array( 'jquery', 'strp-sub-chartjs' ), STRP_SUB_VERSION, true );
            wp_localize_script( 'strp-sub-analytics-dashboard', 'strp_sub_dashboard_ajax', array(
                'ajax_url'          => admin_url( 'admin-ajax.php' ),
                'nonce'             => wp_create_nonce( 'strp_sub_dashboard_nonce' ),
                'currency_symbol'   => function_exists( 'get_woocommerce_currency_symbol' ) ? html_entity_decode( get_woocommerce_currency_symbol() ) : '$',
                'i18n_vs_prior'     => __( 'vs prior period', 'subscription-manager-for-stripe' ),
                'i18n_revenue'      => __( 'Revenue', 'subscription-manager-for-stripe' ),
                'i18n_monthly'      => __( 'Monthly', 'subscription-manager-for-stripe' ),
                'i18n_annual'       => __( 'Annual', 'subscription-manager-for-stripe' ),
                'i18n_lifetime'     => __( 'Lifetime', 'subscription-manager-for-stripe' ),
            ) );
        }
    }

    public function activate() {
        update_option( 'strp_sub_version', STRP_SUB_VERSION );

        // Webhook Retry table (Starter+).
        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-webhook-retry__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }
        require_once STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-webhook-handler.php';
        if ( class_exists( 'STRP_SUB_Webhook_Retry' ) ) {
            STRP_SUB_Webhook_Retry::get_instance()->create_table();
            STRP_SUB_Webhook_Retry::schedule_cron();
        }
        STRP_SUB_Webhook_Handler::create_sessions_table();
        STRP_SUB_Webhook_Handler::schedule_cleanup_cron();
        self::create_order_index_table();

        require_once STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-stripe-api.php';
        STRP_SUB_Stripe_API::create_fingerprints_table();

        // Webhook Monitor table (Starter+).
        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-webhook-monitor__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }
        if ( class_exists( 'STRP_SUB_Webhook_Monitor' ) ) {
            STRP_SUB_Webhook_Monitor::create_log_table();
        }

        self::create_retention_events_table();

        // Affiliate system tables (Professional+).
        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-affiliate-stats__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }
        $_f = STRP_SUB_PLUGIN_DIR . 'includes/class-strp-sub-affiliate-payouts__premium_only.php';
        if ( file_exists( $_f ) ) {
            require_once $_f;
        }
        if ( class_exists( 'STRP_SUB_Affiliate_Stats' ) ) {
            STRP_SUB_Affiliate_Stats::create_table();
        }
        if ( class_exists( 'STRP_SUB_Affiliate_Payouts' ) ) {
            STRP_SUB_Affiliate_Payouts::create_table();
        }

        // Flush rewrite rules so the customer portal subscriptions endpoint works.
        add_rewrite_endpoint( 'subscriptions', EP_ROOT | EP_PAGES );
        if ( get_option( 'strp_sub_flush_rewrite_rules' ) !== 'done' ) {
            flush_rewrite_rules();
            update_option( 'strp_sub_flush_rewrite_rules', 'done', true );
        }

        // Set activation redirect transient for setup wizard.
        set_transient( 'strp_sub_activation_redirect', true, 30 );
    }

    public function deactivate() {
        wp_clear_scheduled_hook( 'strp_sub_process_webhook_retries' );
        wp_clear_scheduled_hook( 'strp_sub_cleanup_unverified_accounts' );
        wp_clear_scheduled_hook( 'strp_sub_cleanup_processed_sessions' );
        wp_clear_scheduled_hook( 'affp_cleanup_unverified_accounts' ); // Clear legacy hook name.
        delete_option( 'strp_sub_flush_rewrite_rules' );
    }

    /**
     * Clean up unverified affiliate accounts older than 72 hours.
     */
    public function cleanup_unverified_affiliate_accounts() {
        $cutoff = gmdate( 'Y-m-d H:i:s', strtotime( '-72 hours' ) );
        $users  = get_users( array(
            'meta_query' => array(
                array( 'key' => '_strp_sub_email_verified', 'value' => 'no' ),
                array( 'key' => '_strp_sub_verification_token', 'compare' => 'EXISTS' ),
            ),
            'fields'     => 'ID',
            'number'     => 50,
            'orderby'    => 'registered',
            'order'      => 'ASC',
        ) );
        foreach ( $users as $user_id ) {
            $registered = get_userdata( $user_id );
            if ( ! $registered ) {
                continue;
            }
            if ( array_intersect( $registered->roles, array( 'administrator', 'editor', 'shop_manager' ) ) ) {
                continue;
            }
            if ( $registered->user_registered < $cutoff && count_user_posts( $user_id ) === 0 ) {
                if ( function_exists( 'wc_get_orders' ) ) {
                    $user_orders = wc_get_orders( array( 'customer_id' => $user_id, 'limit' => 1, 'return' => 'ids' ) );
                    if ( ! empty( $user_orders ) ) {
                        continue;
                    }
                }
                wp_delete_user( $user_id );
            }
        }
    }

    /**
     * Create O(1) order resolution index table for fast webhook lookups
     */
    public static function create_order_index_table() {
        global $wpdb;

        $table_name      = $wpdb->prefix . 'strp_sub_order_index';
        $charset_collate = $wpdb->get_charset_collate();

        $sql = "CREATE TABLE $table_name (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            session_id varchar(191) NOT NULL,
            subscription_id varchar(191) NOT NULL,
            order_id bigint(20) unsigned NOT NULL,
            created_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
            PRIMARY KEY  (id),
            UNIQUE KEY session_id (session_id),
            KEY subscription_id (subscription_id),
            KEY order_id (order_id)
        ) $charset_collate;";

        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta( $sql );
    }

    /**
     * Create dedicated retention events table for scalable analytics.
     */
    public static function create_retention_events_table() {
        global $wpdb;

        $table_name      = $wpdb->prefix . 'strp_sub_retention_events';
        $charset_collate = $wpdb->get_charset_collate();

        $sql = "CREATE TABLE $table_name (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            user_id bigint(20) unsigned NOT NULL,
            timestamp datetime NOT NULL,
            outcome varchar(20) NOT NULL,
            reason_label varchar(255) NOT NULL DEFAULT '',
            offer_type varchar(50) NOT NULL DEFAULT '',
            PRIMARY KEY  (id),
            KEY user_id (user_id),
            KEY timestamp (timestamp)
        ) $charset_collate;";

        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta( $sql );
    }
}

/**
 * Bootstrap.
 *
 * Wrapped in function_exists to prevent fatal errors if another plugin or theme
 * defines a function with the same name in the global scope.
 */
if ( ! function_exists( 'strp_sub_init' ) ) {
    function strp_sub_init() {
        return STRP_SUB_Manager::get_instance();
    }
}

// Backward-compat alias for any code that calls strp_sub_init() directly.
strp_sub_init();

} // End of Freemius free/premium version switch.
