<?php
/**
 * LinkQuiver uninstall script.
 *
 * Executed automatically by WordPress when the user deletes the plugin from
 * the admin (Plugins → Delete). Wipes every option/table the plugin owns so
 * a fresh install starts from a clean slate.
 */

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

// Every option the plugin persists. This MUST stay exhaustive: a leftover
// `linkquiver_api_key_hash` means a reinstall is silently still authenticated,
// and leftover `linkquiver_theme_*` (with `linkquiver_theme_active` autoloaded)
// means a reinstall instantly re-hijacks the whole frontend with stale HTML.
$linkquiver_options = array(
    // API key + connection markers.
    'linkquiver_api_key',            // legacy cleartext (pre-hash installs)
    'linkquiver_api_key_hash',       // current salted-hash credential store
    'linkquiver_activated_at',
    'linkquiver_last_validated_at',
    'linkquiver_db_version',
    // Redirect engine knobs.
    'linkquiver_catchall_404',
    // AI crawler visibility log (settings + cached vendor CIDR feed).
    'linkquiver_ai_crawler_enabled',
    'linkquiver_ai_crawler_ranges',
    'linkquiver_ai_crawler_ranges_at',
    'linkquiver_ai_crawler_retention_days',
    // Theme Engine (HTML/CSS + toggles; three are autoloaded).
    'linkquiver_theme_header_html',
    'linkquiver_theme_footer_html',
    'linkquiver_theme_global_css',
    'linkquiver_theme_home_body_html',
    'linkquiver_theme_home_css',
    'linkquiver_theme_active',
    'linkquiver_theme_version',
    'linkquiver_theme_updated_at',
);

/**
 * Wipe options + redirects table + transients + orphaned lock/throttle rows
 * for a single blog. Called once for single-site installs and once per network
 * site on multisite.
 */
function linkquiver_uninstall_blog( $options ) {
    foreach ( $options as $option ) {
        delete_option( $option );
    }

    global $wpdb;
    // After switch_to_blog() on multisite, $wpdb->prefix is the per-site prefix
    // (e.g. wp_2_), so this drops the correct per-blog table. On single-site it
    // is just $wpdb->prefix.
    $table = $wpdb->prefix . 'linkquiver_redirects';
    $wpdb->query( "DROP TABLE IF EXISTS {$table}" );

    $ai_table = $wpdb->prefix . 'linkquiver_ai_hits';
    $wpdb->query( "DROP TABLE IF EXISTS {$ai_table}" );

    // Idempotency locks (lq_idem_*) are plain option rows; brute-force throttles
    // (lq_fail_*) and the AI-crawler rate buckets (lq_ai_*) are transients
    // (option rows _transient_/_transient_timeout_). Sweep any that a crash left
    // behind so a clean reinstall starts empty.
    $wpdb->query(
        "DELETE FROM {$wpdb->options} WHERE option_name LIKE 'lq_idem_%'"
        . " OR option_name LIKE '\\_transient\\_lq_fail\\_%'"
        . " OR option_name LIKE '\\_transient\\_timeout\\_lq_fail\\_%'"
        . " OR option_name LIKE '\\_transient\\_lq_ai\\_%'"
        . " OR option_name LIKE '\\_transient\\_timeout\\_lq_ai\\_%'"
    );

    delete_transient( 'linkquiver_activation_redirect' );
    delete_site_transient( 'linkquiver_update_manifest' );

    // Scheduled hooks outlive the plugin files: a leftover event fires forever
    // against a callback that no longer exists.
    wp_clear_scheduled_hook( 'linkquiver_ai_crawler_refresh_ranges' );
    wp_clear_scheduled_hook( 'linkquiver_ai_crawler_prune' );
}

if ( is_multisite() ) {
    $sites = function_exists( 'get_sites' ) ? get_sites( array( 'number' => 0 ) ) : array();
    foreach ( $sites as $site ) {
        switch_to_blog( (int) $site->blog_id );
        linkquiver_uninstall_blog( $linkquiver_options );
        restore_current_blog();
    }
    // Also clean network-wide options.
    foreach ( $linkquiver_options as $option ) {
        delete_site_option( $option );
    }
} else {
    linkquiver_uninstall_blog( $linkquiver_options );
}
