<?php
/**
 * FormForge uninstall — fired by WP when the user clicks "Delete" on the
 * Plugins screen (NOT on simple Deactivate). Wipes everything FormForge
 * created so an uninstall + clean reinstall is a true factory reset.
 *
 * Opt-out: a user who wants to keep their forms / submissions through a
 * Reinstall can set `formforge_keep_data_on_uninstall = '1'` in
 * `wp_options` before deleting. The uninstall script honors the flag and
 * exits early — preserving every wp_formforge_* table + every wp_options
 * row + every postmeta key.
 *
 * @package FormForge
 */

// WP defines this when calling uninstall.php. Bail otherwise so the file
// can never be loaded directly via HTTP.
if (!defined('WP_UNINSTALL_PLUGIN')) {
    exit;
}

// Honor the opt-out flag — read it BEFORE we touch anything.
$formforge_keep = get_option('formforge_keep_data_on_uninstall', '');
if ($formforge_keep === '1' || $formforge_keep === 1 || $formforge_keep === true) {
    return;
}

global $wpdb;

// =============================================================================
// 1. Drop FormForge tables.
// =============================================================================

// Order matters only for FK constraints; FF tables are FK-free, so we drop
// in any order. `IF EXISTS` keeps reinstall-after-failed-install scenarios
// noiseless.
$formforge_tables = [
    $wpdb->prefix . 'formforge_forms',
    $wpdb->prefix . 'formforge_submissions',
    $wpdb->prefix . 'formforge_analytics',
];

foreach ($formforge_tables as $formforge_table) {
    // Static identifier — wpdb->prefix is server-controlled, $formforge_table is built
    // from a literal allowlist above. Safe from injection. Concatenation is
    // required because $wpdb->prepare does not parameterise identifiers.
    $wpdb->query("DROP TABLE IF EXISTS `{$formforge_table}`"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange -- own table from $wpdb->prefix; one-time uninstall
}

// =============================================================================
// 2. Delete FormForge options.
// =============================================================================

// One specific list (so we don't accidentally take options created by other
// plugins that happen to share a prefix substring) plus a wildcard sweep
// for anything we created via update_option('formforge_*', ...) outside
// this list (telemetry counters, integration status caches, etc.).

$formforge_specific_options = [
    // Versioning / install marker
    'formforge_db_version',
    'formforge_previous_version',
    'formforge_installed_at',

    // Settings root
    'formforge_settings',

    // Encrypted secrets — gone with the table, but listed for clarity.
    'formforge_api_key_enc',

    // Integration credentials & per-form caches
    'formforge_stripe_settings',
    'formforge_telegram_chat_id',
    'formforge_telegram_log',
    'formforge_slack_webhook_url',
    'formforge_discord_webhook_url',
    'formforge_gsheets_log',
    'formforge_gcal_log',
    'formforge_mailchimp_settings',

    // Telemetry / dashboard widget state
    'formforge_telemetry_enabled',
    'formforge_telemetry_last_flush',
    'formforge_telemetry_last_error_signature',

    // Forge Suite shared housekeeping (only the FF-scoped copy)
    'formforge_license_data',

    // Opt-out flag itself — uninstall really should mean uninstall.
    'formforge_keep_data_on_uninstall',
];

foreach ($formforge_specific_options as $formforge_opt) {
    delete_option($formforge_opt);
    // Multisite: per-site delete above handles single-site; for multisite
    // the loop below catches anything network-stored.
    delete_site_option($formforge_opt);
}

// Wildcard sweep for anything beginning with `formforge_` we might have
// missed. LIKE on option_name is a full table scan but uninstall runs
// once at the end of a plugin's life; the cost is acceptable.
$wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own options cleanup; one-time uninstall
    $wpdb->prepare(
        "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
        $wpdb->esc_like('formforge_') . '%'
    )
);

// User meta we set (license-mgmt UI may stash dismiss flags here).
$wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own usermeta cleanup; one-time uninstall
    $wpdb->prepare(
        "DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE %s",
        $wpdb->esc_like('formforge_') . '%'
    )
);

// =============================================================================
// 3. Wipe submission/form-related postmeta.
// =============================================================================

// FormForge attaches `_formforge_*` postmeta to posts that embed forms via
// the [formforge] shortcode tracker. Strip those rows so reinstall starts
// from a clean slate.
$wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own postmeta cleanup; one-time uninstall
    $wpdb->prepare(
        "DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE %s",
        $wpdb->esc_like('_formforge_') . '%'
    )
);

// =============================================================================
// 4. Clear scheduled crons.
// =============================================================================

// Belt-and-suspenders: the deactivation hook already clears these, but a
// "Delete" without a prior Deactivate (rare WP path) wouldn't have run it.
$formforge_cron_hooks = [
    'formforge_retry_sheets_append',
    'formforge_retry_calendar_event',
];
foreach ($formforge_cron_hooks as $formforge_hook) {
    wp_clear_scheduled_hook($formforge_hook);
}

// =============================================================================
// 5. Drop transients (rate-limit counters, busy slots, ad-hoc caches).
// =============================================================================
$wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own transient cleanup; one-time uninstall
    $wpdb->prepare(
        "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s",
        $wpdb->esc_like('_transient_ff_') . '%',
        $wpdb->esc_like('_transient_timeout_ff_') . '%'
    )
);
