<?php
/**
 * Schema and settings migrations.
 *
 * Two version trackers live in site-options:
 *   - shieldforge_db_version       — the DB schema version on disk
 *   - shieldforge_plugin_version   — the plugin code version that last ran
 *
 * On every load we compare the constants (SHIELDFORGE_DB_VERSION /
 * SHIELDFORGE_VERSION) against what's stored. If newer, we run any
 * registered migration callbacks in order, then update the stored version.
 *
 * Each migration callback MUST be idempotent (safe to re-run). dbDelta
 * handles additive column changes well; for renames / drops / data fix-ups
 * write explicit SQL or option_get/set logic in the callback.
 *
 * @package Brainwerk Security Suite
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class Shieldforge_Migrations {

    const DB_VERSION_OPT     = 'shieldforge_db_version';
    const PLUGIN_VERSION_OPT = 'shieldforge_plugin_version';

    public static function register( Shieldforge_Loader $loader ) {
        // admin_init runs after WP is fully loaded; cheap unless we actually
        // have something to migrate (versioned no-op if up-to-date).
        $loader->add_action( 'admin_init', __CLASS__, 'maybe_run' );
    }

    public static function get_db_version() {
        return is_multisite()
            ? (string) get_site_option( self::DB_VERSION_OPT, '0' )
            : (string) get_option( self::DB_VERSION_OPT, '0' );
    }

    public static function set_db_version( $version ) {
        if ( is_multisite() ) {
            update_site_option( self::DB_VERSION_OPT, (string) $version );
        } else {
            update_option( self::DB_VERSION_OPT, (string) $version );
        }
    }

    public static function get_plugin_version() {
        return is_multisite()
            ? (string) get_site_option( self::PLUGIN_VERSION_OPT, '0.0.0' )
            : (string) get_option( self::PLUGIN_VERSION_OPT, '0.0.0' );
    }

    public static function set_plugin_version( $version ) {
        if ( is_multisite() ) {
            update_site_option( self::PLUGIN_VERSION_OPT, (string) $version );
        } else {
            update_option( self::PLUGIN_VERSION_OPT, (string) $version );
        }
    }

    /**
     * Map of "DB version on or before X" => callable. Run in numeric order
     * for any version higher than the current stored db_version.
     *
     * The string key is the DB version this migration TARGETS — i.e. after
     * the callback completes, db_version becomes that key.
     */
    public static function db_migrations() {
        return array(
            '2' => array( __CLASS__, 'migrate_to_v2' ),
            '3' => array( __CLASS__, 'migrate_to_v3' ),
            '4' => array( __CLASS__, 'migrate_to_v4' ),
        );
    }

    /**
     * v2: add the scanner-baseline table (shieldforge_files) and schedule
     * the daily integrity sweep on existing installs that upgraded over an
     * older version which didn't have either.
     */
    public static function migrate_to_v2( $from, $to ) {
        if ( ! class_exists( 'Shieldforge_Scanner' ) ) {
            require_once SHIELDFORGE_PATH . 'includes/class-shieldforge-scanner.php';
        }
        Shieldforge_Scanner::create_table();
        Shieldforge_Scanner::ensure_scheduled();
    }

    /**
     * v3 (Phase 7 stage 2): typed-events table + per-site salt bootstrap.
     */
    public static function migrate_to_v3( $from, $to ) {
        if ( ! class_exists( 'Shieldforge_Event' ) ) {
            require_once SHIELDFORGE_PATH . 'includes/class-shieldforge-event.php';
        }
        Shieldforge_Event::create_table();
        Shieldforge_Event::get_salt(); // generates the salt the first time it's called
    }

    /**
     * v4 (Phase 7 stage 3): incident-response table (Post-Incident Forensics).
     */
    public static function migrate_to_v4( $from, $to ) {
        if ( ! class_exists( 'Shieldforge_Incident' ) ) {
            require_once SHIELDFORGE_PATH . 'includes/class-shieldforge-incident.php';
        }
        Shieldforge_Incident::create_table();
    }

    /**
     * Map of plugin-version => callable for settings/data migrations that
     * are NOT schema changes (e.g. setting-key renames, default flips for
     * existing installs, log-cleanup on update).
     */
    public static function plugin_migrations() {
        return array(
            // '0.2.0' => array( __CLASS__, 'plugin_migrate_to_0_2_0' ),
        );
    }

    public static function maybe_run() {
        $changed = false;

        // ---- DB migrations ----
        $stored_db_version = self::get_db_version();
        $target_db_version = (string) ( defined( 'SHIELDFORGE_DB_VERSION' ) ? SHIELDFORGE_DB_VERSION : '1' );
        if ( version_compare( $stored_db_version, $target_db_version, '<' ) ) {
            self::run_db_migrations( $stored_db_version, $target_db_version );
            self::set_db_version( $target_db_version );
            $changed = true;
        }

        // ---- Plugin migrations (settings/data) ----
        $stored_plugin = self::get_plugin_version();
        $target_plugin = (string) ( defined( 'SHIELDFORGE_VERSION' ) ? SHIELDFORGE_VERSION : '0.0.0' );
        if ( version_compare( $stored_plugin, $target_plugin, '<' ) ) {
            self::run_plugin_migrations( $stored_plugin, $target_plugin );
            self::set_plugin_version( $target_plugin );
            $changed = true;
        }

        if ( $changed ) {
            Shieldforge_Logger::log( 'plugin_migrated', array(
                'severity' => Shieldforge_Logger::SEV_INFO,
                'context'  => array(
                    'from_db_version'     => $stored_db_version,
                    'to_db_version'       => $target_db_version,
                    'from_plugin_version' => $stored_plugin,
                    'to_plugin_version'   => $target_plugin,
                ),
            ) );
        }
    }

    private static function run_db_migrations( $from, $to ) {
        foreach ( self::db_migrations() as $target_version => $callback ) {
            if ( version_compare( $target_version, $from, '>' )
                && version_compare( $target_version, $to, '<=' )
                && is_callable( $callback ) ) {
                call_user_func( $callback, $from, $to );
            }
        }
    }

    private static function run_plugin_migrations( $from, $to ) {
        foreach ( self::plugin_migrations() as $target_version => $callback ) {
            if ( version_compare( $target_version, $from, '>' )
                && version_compare( $target_version, $to, '<=' )
                && is_callable( $callback ) ) {
                call_user_func( $callback, $from, $to );
            }
        }
    }

    /* ------------------------------------------------------------------
     * Reference migration template (commented out — copy when needed).
     * ------------------------------------------------------------------ */

    // private static function migrate_to_v2( $from, $to ) {
    //     global $wpdb;
    //     // Example: add a column the dbDelta-on-activation didn't cover for
    //     // installs that activated before v2.
    //     $table = Shieldforge_Utils::table( 'log' );
    //     $exists = $wpdb->get_var( "SHOW COLUMNS FROM {$table} LIKE 'plugin_version'" );
    //     if ( ! $exists ) {
    //         $wpdb->query( "ALTER TABLE {$table} ADD COLUMN plugin_version VARCHAR(20) NULL DEFAULT NULL" );
    //     }
    // }

    // private static function plugin_migrate_to_0_2_0( $from, $to ) {
    //     // Example: rename setting key bf_threshold -> bruteforce_threshold
    //     $opts = get_site_option( SHIELDFORGE_OPTIONS_KEY, array() );
    //     if ( isset( $opts['bf_threshold'] ) && ! isset( $opts['bruteforce_threshold'] ) ) {
    //         $opts['bruteforce_threshold'] = $opts['bf_threshold'];
    //         unset( $opts['bf_threshold'] );
    //         update_site_option( SHIELDFORGE_OPTIONS_KEY, $opts );
    //         Shieldforge_Settings::flush_cache();
    //     }
    // }
}
