<?php
/**
 * Brainwerk Security Suite uninstall — drops ALL plugin data.
 *
 * Triggered by WordPress when the user deletes the plugin from the admin.
 * Removes DB tables, all options (incl. user-meta-style keys), all transients,
 * and all scheduled cron events on every site of a Multisite.
 *
 * GDPR / data-minimization: nothing the plugin ever wrote may persist after
 * uninstall. Test by uninstalling, then grep'ing wp_options + wp_sitemeta +
 * wp_*_options for "brainwerk-security-suite" or "sf_" — must return zero rows.
 *
 * @package Brainwerk Security Suite
 */

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

global $wpdb;

/* ----------------------------------------------------------------------
 * 1. DB tables — network-wide, base_prefix
 * --------------------------------------------------------------------*/
$tables = array(
    $wpdb->base_prefix . 'shieldforge_log',
    $wpdb->base_prefix . 'shieldforge_blocks',
    $wpdb->base_prefix . 'shieldforge_baseline',
    $wpdb->base_prefix . 'shieldforge_files',
);
foreach ( $tables as $table ) {
    $wpdb->query( "DROP TABLE IF EXISTS `{$table}`" ); // phpcs:ignore WordPress.DB
}

/* ----------------------------------------------------------------------
 * 2. Per-site / per-network options the plugin writes.
 * --------------------------------------------------------------------*/
$option_keys = array(
    'shieldforge_settings',
    'shieldforge_db_version',
    'shieldforge_plugin_version',
    'shieldforge_license_state',
    'shieldforge_onboarding_pending',
    'shieldforge_onboarding_locale',
    'shieldforge_scan_progress',
    'shieldforge_vulns_logged',
);

/* ----------------------------------------------------------------------
 * 3. Cron events the plugin schedules.
 * --------------------------------------------------------------------*/
$cron_hooks = array(
    'shieldforge_daily_digest',
    'shieldforge_cleanup_logs',
    'shieldforge_license_refresh',
    'shieldforge_daily_scan',
    'shieldforge_daily_vulnscan',
);

/* ----------------------------------------------------------------------
 * 4. Transient name patterns (LIKE-based — covers per-user lockouts and
 *    healthcheck cache; we don't know the md5 hashes individually).
 * --------------------------------------------------------------------*/
$transient_prefixes = array(
    // The single 'shieldforge_' prefix covers every transient the plugin
    // creates, including shieldforge_healthcheck_v1, shieldforge_vulns_v1,
    // shieldforge_user_lock_<hash>, shieldforge_2fa_pending_<token>,
    // shieldforge_2fa_recovery_show_<uid> and shieldforge_2fa_attempts_<uid>.
    'shieldforge_',
);

/**
 * Helper: delete transients by name prefix from the standard wp_options
 * table layout, where transients are stored as
 *   _transient_<key>          (the value)
 *   _transient_timeout_<key>  (the expiry)
 * For site (network) transients the prefix is _site_transient_.
 */
function sf_uninstall_purge_transients( $prefixes ) {
    global $wpdb;
    foreach ( $prefixes as $prefix ) {
        $like_value   = $wpdb->esc_like( '_transient_' . $prefix ) . '%';
        $like_timeout = $wpdb->esc_like( '_transient_timeout_' . $prefix ) . '%';
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- uninstall cleanup; one-off LIKE-based purge with no core API equivalent, no caching applicable post-uninstall.
        $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s", $like_value, $like_timeout ) );
    }
}
function sf_uninstall_purge_site_transients( $prefixes ) {
    global $wpdb;
    if ( ! is_multisite() ) {
        return;
    }
    foreach ( $prefixes as $prefix ) {
        $like_value   = $wpdb->esc_like( '_site_transient_' . $prefix ) . '%';
        $like_timeout = $wpdb->esc_like( '_site_transient_timeout_' . $prefix ) . '%';
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- uninstall cleanup; one-off LIKE-based purge with no core API equivalent, no caching applicable post-uninstall.
        $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->sitemeta} WHERE meta_key LIKE %s OR meta_key LIKE %s", $like_value, $like_timeout ) );
    }
}

/* ----------------------------------------------------------------------
 * 5. Apply across all sites (Multisite) or just current site.
 * --------------------------------------------------------------------*/
if ( is_multisite() ) {
    // Network-level options + sitemeta transients
    foreach ( $option_keys as $key ) {
        delete_site_option( $key );
    }
    sf_uninstall_purge_site_transients( $transient_prefixes );

    // Per-site cleanup
    $sites = get_sites( array( 'number' => 0, 'fields' => 'ids' ) );
    foreach ( $sites as $blog_id ) {
        switch_to_blog( $blog_id );

        foreach ( $option_keys as $key ) {
            delete_option( $key );
        }
        foreach ( $cron_hooks as $hook ) {
            wp_clear_scheduled_hook( $hook );
        }
        sf_uninstall_purge_transients( $transient_prefixes );

        restore_current_blog();
    }
} else {
    foreach ( $option_keys as $key ) {
        delete_option( $key );
    }
    foreach ( $cron_hooks as $hook ) {
        wp_clear_scheduled_hook( $hook );
    }
    sf_uninstall_purge_transients( $transient_prefixes );
}

/* ----------------------------------------------------------------------
 * 6. User-meta keys — none currently, but leave hook for future Pro
 *    additions (e.g. per-user 2FA secret) so they don't leak.
 * --------------------------------------------------------------------*/
$user_meta_keys = array(
    'shieldforge_2fa_secret',
    'shieldforge_2fa_pending_secret',
    'shieldforge_2fa_enabled',
    'shieldforge_2fa_recovery',
    'shieldforge_2fa_last_used',
);
if ( ! empty( $user_meta_keys ) ) {
    foreach ( $user_meta_keys as $meta_key ) {
        delete_metadata( 'user', 0, $meta_key, '', true );
    }
}
