<?php

/**
 * Plugin Name: Advanced Form Integration
 * Plugin URI: https://advancedformintegration.com/
 * Description: Connect any WordPress form or event to 200+ apps — CRMs, email marketing, sheets, webhooks — with zero code.
 * Author: nasirahmed
 * Author URI: https://advancedformintegration.com/
 * Version: 2.2.1
 * License: GPL2
 * Text Domain: advanced-form-integration
 * Domain Path: languages
 * Tags: form integration, crm, webhooks, automation, contact form 7
 * Requires at least: 3.0.1
 * Tested up to: 6.9
 * Requires PHP: 7.4
 *
 * Released under the GPL license
 * http://www.opensource.org/licenses/gpl-license.php
 *
 * This is an add-on for WordPress
 * http://wordpress.org/
 *
 * **********************************************************************
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 * **********************************************************************
 */
// don't call the file directly
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
if ( !function_exists( 'adfoin_fs' ) ) {
    // Create a helper function for easy SDK access.
    function adfoin_fs() {
        global $adfoin_fs;
        if ( !isset( $adfoin_fs ) ) {
            // Include Freemius SDK.
            require_once dirname( __FILE__ ) . '/freemius/start.php';
            $adfoin_fs = fs_dynamic_init( array(
                'id'               => '4417',
                'slug'             => 'advanced-form-integration',
                'type'             => 'plugin',
                'public_key'       => 'pk_f94bb401ae01ff3a79f438df51715',
                'is_premium'       => false,
                'premium_suffix'   => 'Professional',
                'has_addons'       => false,
                'has_paid_plans'   => true,
                'menu'             => array(
                    'slug'    => 'advanced-form-integration-settings',
                    'support' => false,
                    'parent'  => array(
                        'slug' => 'advanced-form-integration',
                    ),
                ),
                'is_live'          => true,
                'is_org_compliant' => true,
            ) );
        }
        return $adfoin_fs;
    }

    // Init Freemius.
    adfoin_fs();
    // Signal that SDK was initiated.
    do_action( 'adfoin_fs_loaded' );
    /**
     * Advanced Form Integration Main Class
     */
    class Advanced_Form_Integration {
        /**
         * Plugin Version
         *
         * @var  string
         */
        public $version = '2.2.1';

        /**
         * Initializes the Advanced_Form_Integration class
         *
         * Checks for an existing Advanced_Form_Integration instance
         * and if it doesn't find one, creates it.
         *
         * @since 1.0.0
         * @return mixed | bool
         */
        public static function init() {
            static $instance = false;
            if ( !$instance ) {
                $instance = new Advanced_Form_Integration();
            }
            return $instance;
        }

        /**
         * Constructor for the Advanced_Form_Integration class
         *
         * Sets up all the appropriate hooks and actions
         *
         * @since 1.0
         * @return void
         */
        public function __construct() {
            register_activation_hook( __FILE__, [$this, 'activate'] );
            register_deactivation_hook( __FILE__, [$this, 'deactivate'] );
            add_action(
                'wp_insert_site',
                array($this, 'new_site_added'),
                10,
                6
            );
            $this->init_plugin();
        }

        /**
         * Initialize plugin
         *
         * @since 1.0.0
         * @return void
         */
        public function init_plugin() {
            /* Define constats */
            $this->define_constants();
            /* Include files */
            $this->includes();
            /* Instantiate classes */
            $this->init_classes();
            /* Initialize the action hooks */
            $this->init_actions();
            /* Initialize the filter hooks */
            $this->init_filters();
        }

        /**
         * Function activate
         *
         * This function creates the database tables for the plugin.
         *
         * @param bool $networkwide Whether to activate the plugin network-wide.
         */
        public function activate( $networkwide ) {
            if ( function_exists( 'is_multisite' ) && is_multisite() ) {
                if ( $networkwide ) {
                    global $wpdb;
                    $blogids = $wpdb->get_col( "SELECT blog_id FROM {$wpdb->blogs}" );
                    foreach ( $blogids as $blog_id ) {
                        switch_to_blog( $blog_id );
                        $this->create_table();
                        restore_current_blog();
                    }
                    return;
                }
            }
            $this->create_table();
            // Create default tables when plugin activates
        }

        /**
         * Function new_site_added
         *
         * This function creates the database tables for the plugin on a newly added site in a multisite network.
         *
         * @param object $site The newly added site object.
         */
        public function new_site_added( $site ) {
            if ( is_plugin_active_for_network( plugin_basename( __FILE__ ) ) ) {
                switch_to_blog( $site->blog_id );
                $this->create_table();
                restore_current_blog();
            }
        }

        /**
         * Function create_table
         *
         * This function creates the database tables for the plugin.
         *
         * @return void
         */
        private function create_table() {
            global $wpdb;
            $collate = '';
            if ( $wpdb->has_cap( 'collation' ) ) {
                if ( !empty( $wpdb->charset ) ) {
                    $collate .= "DEFAULT CHARACTER SET {$wpdb->charset}";
                }
                if ( !empty( $wpdb->collate ) ) {
                    $collate .= " COLLATE {$wpdb->collate}";
                }
            }
            $table_schema = array("CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}adfoin_integration` (\n                    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n                    `title` text NOT NULL,\n                    `form_provider` varchar(255) NOT NULL,\n                    `form_id` varchar(255) NOT NULL,\n                    `form_name` varchar(255) DEFAULT NULL,\n                    `action_provider` varchar(255) NOT NULL,\n                    `task` varchar(255) NOT NULL,\n                    `data` longtext DEFAULT NULL,\n                    `extra_data` longtext DEFAULT NULL,\n                    `status` int(1) NOT NULL,\n                    `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n                    PRIMARY KEY  (`id`),\n                    KEY `form_provider_form_id_status` (`form_provider`(50),`form_id`(50),`status`),\n                    KEY `status` (`status`),\n                    KEY `action_provider` (`action_provider`(50))\n                ) {$collate};", "CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}adfoin_log` (\n                    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n                    `response_code` int(3) DEFAULT NULL,\n                    `response_message` varchar(255) DEFAULT NULL,\n                    `integration_id` bigint(20) DEFAULT NULL,\n                    `request_data` longtext DEFAULT NULL,\n                    `response_data` longtext DEFAULT NULL,\n                    `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n                    PRIMARY KEY  (`id`),\n                    KEY `integration_id_id` (`integration_id`,`id`),\n                    KEY `time` (`time`),\n                    KEY `response_code` (`response_code`)\n                ) {$collate};");
            require_once ABSPATH . 'wp-admin/includes/upgrade.php';
            foreach ( $table_schema as $table ) {
                dbDelta( $table );
            }
        }

        /**
         * One-time DB index migration.
         *
         * The original schema declared only `KEY id (id)` on adfoin_integration
         * and adfoin_log — no PRIMARY KEY, and none of the columns the plugin
         * actually filters by (form_provider, status, integration_id, time,
         * response_code) were indexed, so every read was a full table scan.
         * create_table() now ships the correct schema for fresh installs; this
         * brings already-activated sites up to the same shape on the first
         * wp-admin pageload after the update.
         *
         * Idempotent and version-stamped: it inspects the live indexes and only
         * adds what is missing, with each ALTER isolated (errors suppressed) so a
         * failure on one table or one index can't block the rest.
         *
         * @return void
         */
        public function run_db_index_migration() {
            if ( (int) get_option( 'adfoin_db_indexes_v1', 0 ) >= 1 ) {
                return;
            }
            global $wpdb;
            $tables = array(
                $wpdb->prefix . 'adfoin_integration' => array(
                    'form_provider_form_id_status' => 'ADD INDEX `form_provider_form_id_status` (`form_provider`(50),`form_id`(50),`status`)',
                    'status'                       => 'ADD INDEX `status` (`status`)',
                    'action_provider'              => 'ADD INDEX `action_provider` (`action_provider`(50))',
                ),
                $wpdb->prefix . 'adfoin_log'         => array(
                    'integration_id_id' => 'ADD INDEX `integration_id_id` (`integration_id`,`id`)',
                    'time'              => 'ADD INDEX `time` (`time`)',
                    'response_code'     => 'ADD INDEX `response_code` (`response_code`)',
                ),
            );
            // ALTER ... ADD INDEX on a duplicate name is a hard error; suppress
            // so the version stamp still gets written and the loop continues.
            $suppress = $wpdb->suppress_errors( true );
            foreach ( $tables as $table => $indexes ) {
                // Skip a table that was never created (activation never ran).
                if ( !$wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) ) {
                    continue;
                }
                $existing = $wpdb->get_col( "SHOW INDEX FROM `{$table}`", 2 );
                // col 2 = Key_name
                $existing = ( is_array( $existing ) ? array_map( 'strtolower', $existing ) : array() );
                // Promote `id` to a real PRIMARY KEY first — AUTO_INCREMENT
                // requires a key on the column at all times, so this must land
                // before the redundant secondary `KEY id` can be dropped.
                if ( !in_array( 'primary', $existing, true ) ) {
                    $wpdb->query( "ALTER TABLE `{$table}` ADD PRIMARY KEY (`id`)" );
                }
                foreach ( $indexes as $name => $add_clause ) {
                    if ( !in_array( strtolower( $name ), $existing, true ) ) {
                        $wpdb->query( "ALTER TABLE `{$table}` {$add_clause}" );
                    }
                }
                // Drop the now-redundant `KEY id (id)` once a PRIMARY KEY exists.
                $refreshed = $wpdb->get_col( "SHOW INDEX FROM `{$table}`", 2 );
                $refreshed = ( is_array( $refreshed ) ? array_map( 'strtolower', $refreshed ) : array() );
                if ( in_array( 'primary', $refreshed, true ) && in_array( 'id', $refreshed, true ) ) {
                    $wpdb->query( "ALTER TABLE `{$table}` DROP INDEX `id`" );
                }
            }
            $wpdb->suppress_errors( $suppress );
            update_option( 'adfoin_db_indexes_v1', 1, 'no' );
        }

        /**
         * Plugin deactivation function
         *
         * @since 1.0
         * @return void
         */
        public function deactivate() {
        }

        /**
         * Function define_constants
         *
         * This function defines the plugin's constants.
         *
         * @return void
         */
        public function define_constants() {
            define( 'ADVANCED_FORM_INTEGRATION_VERSION', $this->version );
            // Plugin Version
            define( 'ADVANCED_FORM_INTEGRATION_FILE', __FILE__ );
            // Plugin Main Folder Path
            define( 'ADVANCED_FORM_INTEGRATION_PATH', dirname( ADVANCED_FORM_INTEGRATION_FILE ) );
            // Parent Directory Path
            define( 'ADVANCED_FORM_INTEGRATION_INCLUDES', ADVANCED_FORM_INTEGRATION_PATH . '/includes' );
            // Include Folder Path
            define( 'ADVANCED_FORM_INTEGRATION_URL', plugins_url( '', ADVANCED_FORM_INTEGRATION_FILE ) );
            // URL Path
            define( 'ADVANCED_FORM_INTEGRATION_ASSETS', ADVANCED_FORM_INTEGRATION_URL . '/assets' );
            // Asset Folder Path
            define( 'ADVANCED_FORM_INTEGRATION_VIEWS', ADVANCED_FORM_INTEGRATION_PATH . '/views' );
            // View Folder Path
            define( 'ADVANCED_FORM_INTEGRATION_PLATFORMS', ADVANCED_FORM_INTEGRATION_PATH . '/platforms' );
            // View Folder Path
            define( 'ADVANCED_FORM_INTEGRATION_TEMPLATES', ADVANCED_FORM_INTEGRATION_PATH . '/templates' );
            // View Folder Path
            define( 'ADVANCED_FORM_INTEGRATION_PRO', ADVANCED_FORM_INTEGRATION_PATH . '/pro' );
            // View Folder Path
        }

        /**
         * Include the required files
         *
         * @since 1.0
         * @return void
         */
        public function includes() {
            require_once ADVANCED_FORM_INTEGRATION_INCLUDES . '/class-adfoin-db.php';
            require_once ADVANCED_FORM_INTEGRATION_INCLUDES . '/class-adfoin-admin-header.php';
            require_once ADVANCED_FORM_INTEGRATION_INCLUDES . '/class-adfoin-admin-menu.php';
            require_once ADVANCED_FORM_INTEGRATION_INCLUDES . '/class-adfoin-integration.php';
            require_once ADVANCED_FORM_INTEGRATION_INCLUDES . '/class-adfoin-log.php';
            require_once ADVANCED_FORM_INTEGRATION_INCLUDES . '/class-adfoin-submission.php';
            // Review nag (admin_notices) and Import/Export handlers
            // (admin_post_* + admin_notices) are only meaningful in admin
            // context. Skip them on front-end pageloads so the bottom of the
            // bootstrap stays cheap. is_admin() is true for wp-admin pages,
            // admin-ajax.php, and admin-post.php — covering every entry point
            // these classes hook into.
            if ( is_admin() ) {
                require_once ADVANCED_FORM_INTEGRATION_INCLUDES . '/class-adfoin-review.php';
                require_once ADVANCED_FORM_INTEGRATION_INCLUDES . '/class-adfoin-import-export.php';
            }
            require_once ADVANCED_FORM_INTEGRATION_INCLUDES . '/class-oauth.php';
            require_once ADVANCED_FORM_INTEGRATION_INCLUDES . '/class-adfoin-oauth-manager.php';
            require_once ADVANCED_FORM_INTEGRATION_INCLUDES . '/class-adfoin-account-manager.php';
            require_once ADVANCED_FORM_INTEGRATION_INCLUDES . '/functions-adfoin.php';
            require_once ADVANCED_FORM_INTEGRATION_INCLUDES . '/api/credentials.php';
            $trigger_keys = adfoin_get_trigger_keys();
            foreach ( $trigger_keys as $trigger_key ) {
                if ( file_exists( ADVANCED_FORM_INTEGRATION_INCLUDES . '/triggers/' . $trigger_key . '/' . $trigger_key . '.php' ) ) {
                    require_once ADVANCED_FORM_INTEGRATION_INCLUDES . '/triggers/' . $trigger_key . '/' . $trigger_key . '.php';
                }
            }
            $platform_settings = adfoin_get_action_platform_settings();
            foreach ( $platform_settings as $platform => $value ) {
                if ( true == $value ) {
                    if ( file_exists( ADVANCED_FORM_INTEGRATION_PLATFORMS . "/{$platform}/{$platform}.php" ) ) {
                        require_once ADVANCED_FORM_INTEGRATION_PLATFORMS . "/{$platform}/{$platform}.php";
                    }
                }
            }
        }

        /**
         * Instantiate classes
         *
         * @since 1.0
         * @return void
         */
        public function init_classes() {
            // Admin Menu Class
            new Advanced_Form_Integration_Admin_Menu();
            // Submission Handler Class
            new Advanced_Form_Integration_Submission();
        }

        /**
         * Initializes action hooks
         *
         * @since 1.0
         * @return  void
         */
        public function init_actions() {
            add_action( 'init', array($this, 'localization_setup') );
            add_action( 'admin_enqueue_scripts', array($this, 'register_scripts') );
            add_action( 'wp_enqueue_scripts', array($this, 'register_public_scripts') );
            add_action( 'plugins_loaded', array($this, 'load_action_scheduler') );
            // One-time DB index migration — admin_init keeps the (possibly slow)
            // ALTER off front-end requests; it runs once then version-stamps out.
            add_action( 'admin_init', array($this, 'run_db_index_migration') );
        }

        /**
         * Load Action Scheduler
         *
         * @since 1.0
         * @return void
         */
        public function load_action_scheduler() {
            require_once ADVANCED_FORM_INTEGRATION_PATH . '/library/action-scheduler/action-scheduler.php';
        }

        /**
         * Initialize plugin for localization
         *
         * @since 1.0
         *
         * @uses load_plugin_textdomain()
         *
         * @return void
         */
        public function localization_setup() {
            load_plugin_textdomain( 'advanced-form-integration', false, dirname( plugin_basename( ADVANCED_FORM_INTEGRATION_FILE ) ) . '/languages/' );
        }

        /**
         * Initializes action filters
         *
         * @since 1.0
         * @return  void
         */
        public function init_filters() {
        }

        /**
         * Register Scripts
         *
         * @since 1.0
         * @return mixed | void
         */
        public function register_scripts( $hook ) {
            // Vue 3.5.x production build (minified, ~160 KB, no dev warnings).
            // The dev build (vue3.global.js) was used for the migration spike
            // and has been removed. vue.min.js (Vue 2) is kept on disk for
            // instant rollback — flip this source back to it if needed.
            // jQuery dependency dropped: Vue does not use jQuery.
            wp_register_script(
                'adfoin-vuejs',
                ADVANCED_FORM_INTEGRATION_ASSETS . '/js/vue3.global.prod.js',
                array(),
                $this->version,
                1
            );
            // Vue 2 -> 3 compatibility shim — must load right after Vue 3 and
            // before any plugin script (re-creates `new Vue()` / `Vue.component()`).
            wp_register_script(
                'adfoin-vue3-shim',
                ADVANCED_FORM_INTEGRATION_ASSETS . '/js/vue3-compat-shim.js',
                array('adfoin-vuejs'),
                $this->version,
                1
            );
            // Register shared Vue components (registered globally before the app boots)
            wp_register_script(
                'adfoin-searchable-select',
                ADVANCED_FORM_INTEGRATION_ASSETS . '/js/components/searchable-select.js',
                array('adfoin-vue3-shim'),
                $this->version,
                1
            );
            // Register core utilities (helpers + per-platform lazy loader)
            wp_register_script(
                'adfoin-core',
                ADVANCED_FORM_INTEGRATION_ASSETS . '/js/core.js',
                array('adfoin-vue3-shim', 'adfoin-searchable-select'),
                $this->version,
                1
            );
            // Register trigger components
            wp_register_script(
                'adfoin-triggers',
                ADVANCED_FORM_INTEGRATION_ASSETS . '/js/triggers.js',
                array('adfoin-core'),
                $this->version,
                1
            );
            // Register Vue app initialization (lazy-loads one platform component on demand)
            wp_register_script(
                'adfoin-app',
                ADVANCED_FORM_INTEGRATION_ASSETS . '/js/app.js',
                array('adfoin-triggers'),
                $this->version,
                1
            );
            // Backward-compat handle: assets/js/script.js is now an empty stub
            // kept so any third-party code that explicitly enqueues it still
            // resolves. All component code lives in platforms/<name>/<name>-component.js
            // and is fetched on demand by adfoinComponentLoader.loadPlatform().
            wp_register_script(
                'adfoin-main-script',
                ADVANCED_FORM_INTEGRATION_ASSETS . '/js/script.js',
                array('adfoin-triggers'),
                $this->version,
                1
            );
            // Stylesheet, localized payload, and the log-page code editor are
            // only useful on plugin admin pages. Skipping the rest avoids:
            //   - the unused stylesheet hitting every admin pageload
            //   - the scandir() inside adfoin_get_platform_scripts()
            //   - shipping the localized adfoin object on screens that won't use it
            // wp_register_script() above remains universal so third-party code
            // that opts into adfoin-* handles still resolves them.
            if ( !$this->is_plugin_admin_page( $hook ) ) {
                return;
            }
            wp_enqueue_style(
                'adfoin-main-style',
                ADVANCED_FORM_INTEGRATION_ASSETS . '/css/asset.css',
                array(),
                $this->version
            );
            $platform_scripts = ( function_exists( 'adfoin_get_platform_scripts' ) ? adfoin_get_platform_scripts() : array() );
            $localize_scripts = array(
                'nonce'           => wp_create_nonce( 'advanced-form-integration' ),
                'delete_confirm'  => __( 'Are you sure to delete the integration?', 'advanced-form-integration' ),
                'list_url'        => admin_url( 'admin.php?page=advanced-form-integration&status=1' ),
                'ajaxurl'         => admin_url( 'admin-ajax.php' ),
                'siteurl'         => site_url(),
                'assetsUrl'       => ADVANCED_FORM_INTEGRATION_ASSETS,
                'version'         => $this->version,
                'loadingText'     => __( 'Loading...', 'advanced-form-integration' ),
                'platformScripts' => $platform_scripts,
            );
            // Localize to core script (loaded first)
            wp_localize_script( 'adfoin-core', 'adfoin', $localize_scripts );
            // Also localize to legacy main script for backward compatibility
            wp_localize_script( 'adfoin-main-script', 'adfoin', $localize_scripts );
            $this->add_log_code_editor();
        }

        /**
         * Whether the current admin pageload is one of this plugin's screens.
         *
         * Hook names follow WordPress's add_menu_page / add_submenu_page
         * convention for this plugin:
         *   - top-level:  `toplevel_page_advanced-form-integration`
         *   - submenus:   `afi_page_advanced-form-integration[-suffix]`
         * (The `afi_` prefix is derived from the menu title "AFI".)
         *
         * @since 1.131.2
         * @param string $hook Hook suffix passed to admin_enqueue_scripts.
         * @return bool
         */
        private function is_plugin_admin_page( $hook ) {
            if ( empty( $hook ) || !is_string( $hook ) ) {
                return false;
            }
            if ( 'toplevel_page_advanced-form-integration' === $hook ) {
                return true;
            }
            return 0 === strpos( $hook, 'afi_page_advanced-form-integration' );
        }

        /**
         * Function add_log_code_editor
         *
         * This function adds a code editor to the Advanced Form Integration log page.
         *
         * @return void
         */
        public function add_log_code_editor() {
            if ( !function_exists( 'get_current_screen' ) ) {
                return;
            }
            $screen = get_current_screen();
            if ( !$screen || 'afi_page_advanced-form-integration-log' !== $screen->id ) {
                return;
            }
            $settings = wp_enqueue_code_editor( array(
                'type' => 'application/json',
            ) );
            if ( false === $settings ) {
                return;
            }
            // wp.codeEditor.initialize() prepends "#" to a string id, so pass
            // the BARE id — "#adfoin-log-request-data" produced the invalid
            // "##adfoin-log-request-data" selector and threw in newer jQuery.
            // The existence guard matters because this inline script also runs
            // on the log *list* screen, which has no request-data textarea.
            wp_add_inline_script( 'code-editor', sprintf( 'jQuery( function() { if ( document.getElementById( "adfoin-log-request-data" ) ) { wp.codeEditor.initialize( "adfoin-log-request-data", %s ); } } );', wp_json_encode( $settings ) ) );
        }

        /**
         * Register Public Script
         *
         * @since 1.53.0
         * @return mixed | void
         */
        public function register_public_scripts() {
            if ( 1 == get_option( 'adfoin_general_settings_utm' ) ) {
                wp_enqueue_script(
                    'js.cookie',
                    ADVANCED_FORM_INTEGRATION_ASSETS . '/js/js.cookie.js',
                    array('jquery'),
                    $this->version,
                    1
                );
                wp_enqueue_script(
                    'afi-utm-grabber',
                    ADVANCED_FORM_INTEGRATION_ASSETS . '/js/utm-grabber.js',
                    array('jquery', 'js.cookie'),
                    $this->version,
                    1
                );
            }
        }

    }

    $adfoin = Advanced_Form_Integration::init();
}