<?php
/**
 * LangForge uninstall handler.
 *
 * WordPress fires this file when the user clicks "Delete" on the plugin row
 * (NOT on plain deactivation — that's the deactivation hook). It runs in a
 * minimal WP environment: $wpdb is available, but plugin classes are not.
 *
 * Behavior:
 * - DROP the 5 lf_* custom tables.
 * - delete_option everything matching lf_% in wp_options.
 * - delete postmeta keys (_lf_language, _lf_translation_group, etc.).
 * - delete user_meta keys with the lf_ / _lf_ prefix.
 *
 * Skip everything if:
 * - LF_KEEP_DATA_ON_UNINSTALL constant is defined (set in wp-config.php
 * by ops folks who want to preserve translation data across reinstall).
 * - lf_keep_data_on_uninstall option is truthy (set via a future toggle in
 * Settings → Advanced — UI not shipped yet but the gate is wired).
 *
 * @package LangForge
 */

// WordPress sets this constant before including uninstall.php. If it's
// missing we're being included by something else — refuse to drop anything.
if (!defined('WP_UNINSTALL_PLUGIN')) {
    exit;
}

// Honor opt-out: constant takes precedence (ops-friendly), then option.
if (defined('LF_KEEP_DATA_ON_UNINSTALL') && LF_KEEP_DATA_ON_UNINSTALL) {
    return;
}
if (get_option('lf_keep_data_on_uninstall')) {
    return;
}

global $wpdb;


// 1. DROP custom tables

$langforge_tables = [ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- LF_/lf_ is the established (short) plugin prefix; not auto-detected by Plugin Check
    $wpdb->prefix . 'lf_translations',
    $wpdb->prefix . 'lf_translation_memory',
    $wpdb->prefix . 'lf_glossary',
    $wpdb->prefix . 'lf_strings',
    $wpdb->prefix . 'lf_analytics',
];

foreach ($langforge_tables as $langforge_table) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- LF_/lf_ is the established (short) plugin prefix; not auto-detected by Plugin Check
    // Table identifier is built from $wpdb->prefix + a hard-coded literal —
    // safe to interpolate, no user input. Cannot use $wpdb->prepare for
    // identifiers anyway.
    $wpdb->query("DROP TABLE IF EXISTS `{$langforge_table}`"); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange -- Table name from $wpdb->prefix; custom plugin table; direct query intended.
}


// 2. Drop wp_options entries

// LIKE 'lf\_%' (escaped underscore) catches lf_default_language, lf_active_languages,
// lf_db_version, lf_installed_at, lf_136_*, lf_switcher_*, lf_translation_model,
// lf_url_format, lf_domain_map, lf_email_notifications, lf_license_data, etc. —
// the full list is dynamic across versions, so we sweep by pattern instead of
// enumerating. Excludes lf_keep_data_on_uninstall on purpose: if the user
// reinstalls AND opted in once, they probably want the opt-in to stick — but
// since we already returned above when it's set, this branch only runs when
// the option is falsy or missing, so it's safe to wipe.

$langforge_option_names = $wpdb->get_col("SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE 'lf\\_%'"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- own table from $wpdb->prefix; one-time/admin read; LF_/lf_ short plugin prefix not auto-detected
foreach ($langforge_option_names as $langforge_option_name) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- LF_/lf_ is the established (short) plugin prefix; not auto-detected by Plugin Check
    delete_option($langforge_option_name);
}

// Also clean lf_* transients (stored as `_transient_lf_*` and `_transient_timeout_lf_*`).
$langforge_transient_rows = $wpdb->get_col("SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE '\\_transient\\_lf\\_%' OR option_name LIKE '\\_transient\\_timeout\\_lf\\_%'"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- own table from $wpdb->prefix; one-time/admin read; LF_/lf_ short plugin prefix not auto-detected
foreach ($langforge_transient_rows as $langforge_option_name) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- LF_/lf_ is the established (short) plugin prefix; not auto-detected by Plugin Check
    delete_option($langforge_option_name);
}


// 3. Drop postmeta keys

// Discovered via grep _lf_ across the plugin source. Listed explicitly because
// `delete_metadata('post', 0, '_lf_%', '', true)` does NOT support wildcards
// (the underlying $wpdb->delete uses exact match).

$langforge_postmeta_keys = [ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- LF_/lf_ is the established (short) plugin prefix; not auto-detected by Plugin Check
    '_lf_language',
    '_lf_translation_group',
    '_lf_needs_update',
    '_lf_source_snapshot',
    '_lf_qa_result',
    '_lf_media_translations',
    '_lf_caption_en',  // pattern _lf_caption_<lang> — LIKE sweep below covers the rest
    '_lf_menu_item_language',
    '_lf_order_language',
];

foreach ($langforge_postmeta_keys as $langforge_meta_key) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- LF_/lf_ is the established (short) plugin prefix; not auto-detected by Plugin Check
    delete_metadata('post', 0, $langforge_meta_key, '', true); // $delete_all = true
}

// Sweep dynamic _lf_caption_<lang> postmeta — caption keys carry an arbitrary
// language suffix, so explicit listing isn't enough.
$wpdb->query("DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE '\\_lf\\_caption\\_%'"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- own table from $wpdb->prefix; one-time/admin read


// 4. Drop user_meta keys

// Currently only one: lf_upgrade_notice_dismissed (stored per user).
// Sweep by pattern to catch any new lf_* user_meta added later.

$wpdb->query("DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE 'lf\\_%' OR meta_key LIKE '\\_lf\\_%'"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- own table from $wpdb->prefix; one-time/admin read


// 5. Roles cleanup is intentionally NOT done here — `register_deactivation_hook`
// in langforge.php already calls LF_Roles::remove_roles at deactivation time,
// which fires before uninstall. By the time we get here, custom roles have
// been removed. If the deactivation hook somehow didn't run, leaving stale
// `lf_translator` capability assignments on users is harmless (the role
// definition is gone; capabilities resolve to false).


// Flush cache so admin UI doesn't show stale data after Reinstall.
wp_cache_flush();
