<?php

/**
 * Plugin Name:       SlideMind
 * Plugin URI:        https://slidemind.app/wordpress
 * Description:       Your control center for AI-enhanced e-learning: manage LLM interactions and persistent learner memory across Storyline, Captivate, Lectora & DominKnow courses. Secure proxy, SCORM/xAPI, prompt templates, and sandbox.
 * Version:           1.0.0
 * Author:            Zaprène - Solutions E-learning
 * Author URI:        https://zaprene.ca
 * License:           GPL-2.0-or-later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       slidemind
 * Domain Path:       /languages
 * Requires at least: 6.2
 * Requires PHP:      8.0
 * Tested up to:      7.0
 *
 */
// Prevent direct access
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
// Composer autoloader (Freemius SDK).
if ( file_exists( __DIR__ . '/vendor/autoload.php' ) ) {
    require_once __DIR__ . '/vendor/autoload.php';
}
/**
 * Freemius SDK integration.
 */
if ( !function_exists( 'slidemind_fs' ) ) {
    /**
     * Create a helper function for easy SDK access.
     *
     * @return \Freemius
     */
    function slidemind_fs() {
        global $slidemind_fs;
        if ( !isset( $slidemind_fs ) ) {
            $slidemind_fs = fs_dynamic_init( array(
                'id'               => '25647',
                'slug'             => 'slidemind',
                'type'             => 'plugin',
                'public_key'       => 'pk_84875c8535aa9bcc26f274b77ace8',
                'is_premium'       => false,
                'has_addons'       => true,
                'has_paid_plans'   => true,
                'is_org_compliant' => true,
                'menu'             => array(
                    'slug'       => 'slidemind',
                    'first-path' => 'admin.php?page=slidemind',
                    'support'    => false,
                    'contact'    => false,
                    'account'    => false,
                    'pricing'    => false,
                    'addons'     => false,
                ),
                'is_live'          => true,
            ) );
        }
        return $slidemind_fs;
    }

    // Init Freemius.
    slidemind_fs();
    // Signal that SDK was initiated.
    do_action( 'slidemind_fs_loaded' );
    // Force-hide Freemius menu items replaced by custom pages.
    slidemind_fs()->add_filter(
        'is_submenu_item_visible',
        function ( $is_visible, $menu_id ) {
            // 'contact' — replaced by support link in settings.
            // 'pricing' — every upgrade CTA points to external slidemind.app/pricing/ (no internal pricing page).
            // 'addons'  — replaced by custom Bridge menu item.
            if ( in_array( $menu_id, ['contact', 'pricing', 'addons'], true ) ) {
                return false;
            }
            return $is_visible;
        },
        10,
        2
    );
    // Uninstall cleanup — hooked into Freemius so it runs after their uninstall tracking.
    slidemind_fs()->add_action( 'after_uninstall', 'slidemind_uninstall_cleanup' );
}
/**
 * SLIDEMIND_LOCAL_DEV — opt-in dev mode.
 *
 * Set to true in wp-config.php to allow the [slidemind_embed] shortcode
 * to render iframes pointing to localhost / 127.0.0.1 / ::1 / file://.
 * Without this flag, the free version enforces same-origin URLs only.
 *
 * Example:
 *     define('SLIDEMIND_LOCAL_DEV', true);
 *
 * Default: false (same-origin enforced).
 */
if ( !defined( 'SLIDEMIND_LOCAL_DEV' ) ) {
    define( 'SLIDEMIND_LOCAL_DEV', false );
}
/**
 * Cleanup all plugin data on uninstall.
 *
 * Called by Freemius after_uninstall action (replaces uninstall.php).
 */
function slidemind_uninstall_cleanup() {
    global $wpdb;
    // 1. Delete configurations (CPT slidemind_config)
    $posts = get_posts( array(
        'post_type'      => 'slidemind_config',
        'post_status'    => 'any',
        'posts_per_page' => -1,
        'fields'         => 'ids',
    ) );
    foreach ( $posts as $post_id ) {
        wp_delete_post( $post_id, true );
    }
    // 2. Delete custom tables
    // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    $wpdb->query( "DROP TABLE IF EXISTS `{$wpdb->prefix}slidemind_logs`" );
    $wpdb->query( "DROP TABLE IF EXISTS `{$wpdb->prefix}slidemind_learner_memory`" );
    // phpcs:enable
    // 3. Delete options
    $options = array(
        'slidemind_db_version',
        'slidemind_plugin_version',
        'slidemind_encryption_key',
        'slidemind_global_api_key',
        'slidemind_settings',
        'slidemind_cors_notice_dismissed',
        'slidemind_encryption_key_notice_dismissed',
        'slidemind_license_key',
        'slidemind_license_plan',
        'slidemind_license_status',
        'slidemind_license_last_check'
    );
    foreach ( $options as $option ) {
        delete_option( $option );
    }
    // 4. Delete transients
    delete_transient( 'slidemind_openrouter_models' );
    // 5. Remove role capabilities
    $capabilities = array(
        'manage_slidemind_configs',
        'edit_slidemind_config',
        'edit_slidemind_configs',
        'edit_others_slidemind_configs',
        'publish_slidemind_configs',
        'read_slidemind_config',
        'read_private_slidemind_configs',
        'delete_slidemind_config',
        'delete_slidemind_configs',
        'manage_slidemind_settings'
    );
    foreach ( array('administrator', 'editor') as $role_name ) {
        $role = get_role( $role_name );
        if ( $role ) {
            foreach ( $capabilities as $cap ) {
                $role->remove_cap( $cap );
            }
        }
    }
    // 6. Delete orphaned post meta
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE %s", $wpdb->esc_like( '_slidemind_' ) . '%' ) );
    // 7. Flush the cache
    wp_cache_flush();
}

/**
 * Plugin version
 */
define( 'SLIDEMIND_VERSION', '1.0.0' );
/**
 * Plugin absolute path
 */
define( 'SLIDEMIND_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
/**
 * Plugin URL
 */
define( 'SLIDEMIND_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
/**
 * Main file basename
 */
define( 'SLIDEMIND_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
/**
 * Check PHP and WordPress prerequisites
 */
function slidemind_check_requirements() {
    $errors = [];
    // PHP 8.0+
    if ( version_compare( PHP_VERSION, '8.0', '<' ) ) {
        $errors[] = sprintf( 
            /* translators: %s: current PHP version number */
            __( 'SlideMind requires PHP 8.0 or higher. Current version: %s', 'slidemind' ),
            PHP_VERSION
         );
    }
    // WordPress 6.0+
    global $wp_version;
    if ( version_compare( $wp_version, '6.0', '<' ) ) {
        $errors[] = sprintf( 
            /* translators: %s: current WordPress version number */
            __( 'SlideMind requires WordPress 6.0 or higher. Current version: %s', 'slidemind' ),
            $wp_version
         );
    }
    // OpenSSL extension
    if ( !extension_loaded( 'openssl' ) ) {
        $errors[] = __( 'SlideMind requires the PHP OpenSSL extension for API key encryption.', 'slidemind' );
    }
    return $errors;
}

/**
 * Display prerequisite errors
 */
function slidemind_admin_notice_requirements() {
    $errors = slidemind_check_requirements();
    if ( !empty( $errors ) ) {
        echo '<div class="notice notice-error is-dismissible">';
        printf( '<p><strong>SlideMind</strong> %s</p>', esc_html__( 'cannot be activated:', 'slidemind' ) );
        echo '<ul>';
        foreach ( $errors as $error ) {
            echo '<li>' . esc_html( $error ) . '</li>';
        }
        echo '</ul>';
        echo '</div>';
    }
}

/**
 * Plugin activation
 */
function slidemind_activate() {
    $errors = slidemind_check_requirements();
    if ( !empty( $errors ) ) {
        deactivate_plugins( plugin_basename( __FILE__ ) );
        wp_die( implode( '<br>', array_map( 'esc_html', $errors ) ), esc_html( __( 'Activation Error', 'slidemind' ) ), [
            'back_link' => true,
        ] );
    }
    require_once SLIDEMIND_PLUGIN_PATH . 'includes/class-activator.php';
    \SlideMind\Activator::activate();
}

register_activation_hook( __FILE__, 'slidemind_activate' );
/**
 * Plugin deactivation
 */
function slidemind_deactivate() {
    require_once SLIDEMIND_PLUGIN_PATH . 'includes/class-activator.php';
    \SlideMind\Activator::deactivate();
}

register_deactivation_hook( __FILE__, 'slidemind_deactivate' );
/**
 * Initialize the plugin
 */
function slidemind_init() {
    // Redirect all fr_* locales (fr_CA, fr_BE, etc.) to fr_FR when no specific file exists.
    add_filter(
        'load_textdomain_mofile',
        function ( $mofile, $domain ) {
            if ( 'slidemind' !== $domain || file_exists( $mofile ) ) {
                return $mofile;
            }
            $locale = get_locale();
            if ( strpos( $locale, 'fr_' ) === 0 ) {
                $mofile = str_replace( $locale, 'fr_FR', $mofile );
            }
            return $mofile;
        },
        10,
        2
    );
    // Check prerequisites
    $errors = slidemind_check_requirements();
    if ( !empty( $errors ) ) {
        add_action( 'admin_notices', 'slidemind_admin_notice_requirements' );
        return;
    }
    // Load the main plugin class
    require_once SLIDEMIND_PLUGIN_PATH . 'includes/class-plugin.php';
    \SlideMind\Plugin::instance();
    // Migrate old sm_ prefixes to slidemind_ for existing installations.
    slidemind_maybe_migrate_prefix();
    // Automatic database migration
    slidemind_maybe_upgrade_db();
}

add_action( 'plugins_loaded', 'slidemind_init' );
/**
 * One-time migration from old sm_ prefix to slidemind_ prefix.
 *
 * Detects existing installations by checking for the old sm_db_version option
 * and renames all database-stored names (options, meta, CPT, tables).
 */
function slidemind_maybe_migrate_prefix() {
    if ( get_option( 'slidemind_prefix_migrated' ) ) {
        return;
    }
    // Only run if old-prefix data exists (existing installation).
    $old_db_version = get_option( 'sm_db_version' );
    if ( false === $old_db_version ) {
        // Fresh install — nothing to migrate, mark as done.
        update_option( 'slidemind_prefix_migrated', '1' );
        return;
    }
    global $wpdb;
    // 1. Rename options: read old, write new, delete old.
    $option_map = array(
        'sm_db_version'                      => 'slidemind_db_version',
        'sm_plugin_version'                  => 'slidemind_plugin_version',
        'sm_encryption_key'                  => 'slidemind_encryption_key',
        'sm_global_api_key'                  => 'slidemind_global_api_key',
        'sm_settings'                        => 'slidemind_settings',
        'sm_global_provider'                 => 'slidemind_global_provider',
        'sm_cors_notice_dismissed'           => 'slidemind_cors_notice_dismissed',
        'sm_encryption_key_notice_dismissed' => 'slidemind_encryption_key_notice_dismissed',
        'sm_license_key'                     => 'slidemind_license_key',
        'sm_license_plan'                    => 'slidemind_license_plan',
        'sm_license_status'                  => 'slidemind_license_status',
        'sm_license_last_check'              => 'slidemind_license_last_check',
        'sm_memory_definitions'              => 'slidemind_memory_definitions',
        'sm_global_memory_config_id'         => 'slidemind_global_memory_config_id',
        'sm_session_id_migrated'             => 'slidemind_session_id_migrated',
        'sm_formation_renamed'               => 'slidemind_formation_renamed',
        'sm_memory_mode_migrated'            => 'slidemind_memory_mode_migrated',
        'sm_profile_fields_cache'            => 'slidemind_profile_fields_cache',
    );
    foreach ( $option_map as $old_key => $new_key ) {
        $value = get_option( $old_key );
        if ( false !== $value ) {
            update_option( $new_key, $value );
            delete_option( $old_key );
        }
    }
    // Also migrate dynamic API key options (sm_api_key_{provider}).
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    $api_key_options = $wpdb->get_col( "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE 'sm\\_api\\_key\\_%'" );
    foreach ( $api_key_options as $old_name ) {
        $new_name = str_replace( 'sm_api_key_', 'slidemind_api_key_', $old_name );
        $value = get_option( $old_name );
        if ( false !== $value ) {
            update_option( $new_name, $value );
            delete_option( $old_name );
        }
    }
    // 2. Rename post meta keys: _sm_* → _slidemind_*.
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    $wpdb->query( "UPDATE {$wpdb->postmeta} SET meta_key = REPLACE(meta_key, '_sm_', '_slidemind_') WHERE meta_key LIKE '\\_sm\\_%'" );
    // 3. Rename CPT slug: sm_configuration → slidemind_config.
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    $wpdb->query( "UPDATE {$wpdb->posts} SET post_type = 'slidemind_config' WHERE post_type = 'sm_configuration'" );
    // 4. Rename custom tables if they exist.
    $old_logs = $wpdb->prefix . 'sm_logs';
    $new_logs = $wpdb->prefix . 'slidemind_logs';
    $old_memory = $wpdb->prefix . 'sm_learner_memory';
    $new_memory = $wpdb->prefix . 'slidemind_learner_memory';
    // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange
    if ( $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $old_logs ) ) === $old_logs ) {
        $wpdb->query( $wpdb->prepare( "RENAME TABLE %i TO %i", $old_logs, $new_logs ) );
    }
    if ( $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $old_memory ) ) === $old_memory ) {
        $wpdb->query( $wpdb->prepare( "RENAME TABLE %i TO %i", $old_memory, $new_memory ) );
    }
    // phpcs:enable
    // 5. Rename capabilities on roles.
    $cap_map = array(
        'manage_sm_configurations'       => 'manage_slidemind_configs',
        'edit_sm_configuration'          => 'edit_slidemind_config',
        'edit_sm_configurations'         => 'edit_slidemind_configs',
        'edit_others_sm_configurations'  => 'edit_others_slidemind_configs',
        'publish_sm_configurations'      => 'publish_slidemind_configs',
        'read_sm_configuration'          => 'read_slidemind_config',
        'read_private_sm_configurations' => 'read_private_slidemind_configs',
        'delete_sm_configuration'        => 'delete_slidemind_config',
        'delete_sm_configurations'       => 'delete_slidemind_configs',
        'manage_sm_settings'             => 'manage_slidemind_settings',
    );
    foreach ( array('administrator', 'editor') as $role_name ) {
        $role = get_role( $role_name );
        if ( $role ) {
            foreach ( $cap_map as $old_cap => $new_cap ) {
                if ( $role->has_cap( $old_cap ) ) {
                    $role->add_cap( $new_cap );
                    $role->remove_cap( $old_cap );
                }
            }
        }
    }
    // 6. Flush rewrite rules (CPT slug changed).
    flush_rewrite_rules();
    // 7. Clean up old transients.
    delete_transient( 'sm_openrouter_models' );
    delete_transient( 'sm_migration_error' );
    delete_transient( 'sm_dashboard_projection' );
    delete_transient( 'sm_profile_fields_cache' );
    delete_transient( 'sm_memory_migration_notice' );
    // 8. Migrate cron hooks.
    wp_clear_scheduled_hook( 'sm_purge_logs_cron' );
    wp_clear_scheduled_hook( 'sm_purge_memory_cron' );
    if ( !wp_next_scheduled( 'slidemind_purge_logs_cron' ) ) {
        wp_schedule_event( time(), 'daily', 'slidemind_purge_logs_cron' );
    }
    if ( !wp_next_scheduled( 'slidemind_purge_memory_cron' ) ) {
        wp_schedule_event( time(), 'daily', 'slidemind_purge_memory_cron' );
    }
    // Mark migration as done.
    update_option( 'slidemind_prefix_migrated', '1' );
}

/**
 * Check and run database migrations if needed
 */
function slidemind_maybe_upgrade_db() {
    $target_version = '1.1';
    $current_version = get_option( 'slidemind_db_version', '0' );
    if ( version_compare( $current_version, $target_version, '>=' ) ) {
        return;
    }
    // Transient lock to prevent concurrent migrations
    $lock_key = 'slidemind_db_migration_lock';
    if ( get_transient( $lock_key ) ) {
        return;
    }
    set_transient( $lock_key, true, 120 );
    try {
        require_once SLIDEMIND_PLUGIN_PATH . 'includes/class-activator.php';
        \SlideMind\Activator::activate();
        // Post-migration check: does the table exist?
        global $wpdb;
        $memory_table = $wpdb->prefix . 'slidemind_learner_memory';
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $table_exists = $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $memory_table ) );
        if ( $table_exists === $memory_table ) {
            update_option( 'slidemind_db_version', $target_version );
            delete_transient( 'slidemind_migration_error' );
        } else {
            set_transient( 'slidemind_migration_error', sprintf( 
                /* translators: 1: database table name, 2: target version number */
                __( 'The table %1$s could not be created during migration to version %2$s.', 'slidemind' ),
                $memory_table,
                $target_version
             ), 3600 );
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional error logging
            error_log( '[SM] Migration echouee : table ' . $memory_table . ' introuvable apres dbDelta.' );
        }
    } catch ( \Throwable $e ) {
        set_transient( 'slidemind_migration_error', sprintf( 
            /* translators: 1: target version number, 2: error message */
            __( 'Error during SM migration to %1$s: %2$s', 'slidemind' ),
            $target_version,
            $e->getMessage()
         ), 3600 );
        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional error logging
        error_log( '[SM] Exception migration : ' . $e->getMessage() );
    } finally {
        delete_transient( $lock_key );
    }
}

/**
 * Display an admin notice on migration error
 */
function slidemind_migration_admin_notice() {
    $error = get_transient( 'slidemind_migration_error' );
    if ( $error ) {
        printf( '<div class="notice notice-error is-dismissible"><p><strong>SlideMind</strong> — %s</p></div>', esc_html( $error ) );
    }
}

add_action( 'admin_notices', 'slidemind_migration_admin_notice' );
/**
 * Recommend defining SM_ENCRYPTION_KEY in wp-config.php
 *
 * If the constant is not defined, the AES-256 encryption key is stored
 * in the database (wp_options), which reduces security if the DB is compromised.
 */
function slidemind_encryption_key_notice() {
    if ( !current_user_can( 'manage_slidemind_settings' ) ) {
        return;
    }
    if ( get_option( 'slidemind_encryption_key_notice_dismissed' ) ) {
        return;
    }
    if ( defined( 'SLIDEMIND_ENCRYPTION_KEY' ) || defined( 'SM_ENCRYPTION_KEY' ) ) {
        return;
    }
    // Handle dismiss action
    if ( isset( $_GET['slidemind_dismiss_encryption_notice'] ) && isset( $_GET['_wpnonce'] ) ) {
        if ( wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'slidemind_dismiss_encryption_notice' ) ) {
            update_option( 'slidemind_encryption_key_notice_dismissed', 1 );
            return;
        }
    }
    $dismiss_url = wp_nonce_url( add_query_arg( 'slidemind_dismiss_encryption_notice', '1' ), 'slidemind_dismiss_encryption_notice' );
    printf(
        '<div class="notice notice-info is-dismissible"><p><strong>SlideMind — %s</strong></p><p>%s</p><p><code>define( \'SLIDEMIND_ENCRYPTION_KEY\', \'%s\' );</code></p><p><a href="%s">%s</a></p></div>',
        esc_html__( 'Security recommendation', 'slidemind' ),
        esc_html__( 'Your encryption key for API keys is currently stored in the database. For better security, add the following line to your wp-config.php file:', 'slidemind' ),
        esc_attr( wp_generate_password( 32, false ) ),
        esc_url( $dismiss_url ),
        esc_html__( 'Dismiss', 'slidemind' )
    );
}

add_action( 'admin_notices', 'slidemind_encryption_key_notice' );
/**
 * Add a settings link in the plugins list
 */
function slidemind_plugin_action_links(  $links  ) {
    $settings_link = sprintf( '<a href="%s">%s</a>', admin_url( 'admin.php?page=slidemind-settings' ), esc_html__( 'Settings', 'slidemind' ) );
    array_unshift( $links, $settings_link );
    return $links;
}

add_filter( 'plugin_action_links_' . SLIDEMIND_PLUGIN_BASENAME, 'slidemind_plugin_action_links' );
/**
 * Clean up transients on plugin update
 */
function slidemind_upgrade_cleanup(  $upgrader_object, $options  ) {
    if ( $options['action'] === 'update' && $options['type'] === 'plugin' ) {
        if ( isset( $options['plugins'] ) && in_array( SLIDEMIND_PLUGIN_BASENAME, $options['plugins'] ) ) {
            delete_transient( 'slidemind_openrouter_models' );
        }
    }
}

add_action(
    'upgrader_process_complete',
    'slidemind_upgrade_cleanup',
    10,
    2
);