<?php
/**
 * SEO Forge — uninstall cleanup.
 *
 * Fired when the user clicks "Delete" on the Plugins screen, NOT on
 * deactivation. We drop the two SEO-Forge–owned tables, every option
 * we registered, every transient we cached, and every postmeta key
 * we stored on user content.
 *
 * To preserve data across an uninstall (e.g. for a planned reinstall
 * onto the same site), set:
 * update_option( 'seoforge_keep_data_on_uninstall', 1 );
 * before deleting the plugin.
 */

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

// Opt-out flag — read directly to avoid bootstrapping helper classes.
$seoforge_keep = (int) get_option( 'seoforge_keep_data_on_uninstall', 0 );
if ( $seoforge_keep === 1 ) {
    return;
}

global $wpdb;


// 1. Tables — only the two we actually create. (Forge_Queue is shared with
// the rest of the Forge Suite, so we leave its table alone.)

$seoforge_tables = [
    $wpdb->prefix . 'seoforge_analysis',
    $wpdb->prefix . 'seoforge_redirects',
];

foreach ( $seoforge_tables as $seoforge_table ) {
    // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter -- own table from $wpdb->prefix; one-time uninstall cleanup
    $wpdb->query( "DROP TABLE IF EXISTS `{$seoforge_table}`" );
}


// 2. Options — explicit list of every key we ever set. Any key we miss
// here will linger as orphaned wp_options rows after uninstall.

$seoforge_options = [
    // Settings + state
    'seoforge_settings',
    'seoforge_default_og_image',
    'seoforge_brand_name',
    'seoforge_title_separator',
    'seoforge_title_templates',
    'seoforge_tax_templates',
    'seoforge_local_seo',
    'seoforge_sitemap_settings',
    'seoforge_robots_txt',
    'seoforge_archive_robots',
    'seoforge_posttype_robots',
    'seoforge_noindex_paginated',
    'seoforge_breadcrumbs',
    'seoforge_auto_links_enabled',
    'seoforge_auto_links_max',
    'seoforge_suppress_competitors',
    'seoforge_ab_advanced_acknowledged',
    'seoforge_site_audit_result',

    // Lifecycle / install metadata
    'seoforge_installed_at',
    'seoforge_previous_version',
    'seoforge_db_version',
    'seoforge_analysis_table_ver',
    'seoforge_redirects_table_ver',

    // Broken-links scanner state
    'seoforge_broken_links_batch_done',
    'seoforge_broken_links_batch_total',
    'seoforge_broken_links_last_scan',
    'seoforge_broken_links_scanning',
    'seoforge_broken_links_total_checked',

    // GSC integration tokens + cached metrics
    'seoforge_gsc_access_token',
    'seoforge_gsc_refresh_token',
    'seoforge_gsc_token_expires',
    'seoforge_gsc_property',
    'seoforge_gsc_client_id',
    'seoforge_gsc_client_secret',
    'seoforge_gsc_last_sync',
    'seoforge_gsc_decay_cache',
    'seoforge_gsc_rankings_cache',

    // License (plugin-side mirror; Freemius keeps its own copy under fs_*)
    'seoforge_license_data',
];

foreach ( $seoforge_options as $seoforge_option ) {
    delete_option( $seoforge_option );
    delete_site_option( $seoforge_option ); // multisite safety
}


// 3. Transients — name-prefix sweep is the only safe option here because
// several keys are dynamic ('seoforge_optimize_<hash>',
// 'seoforge_sitemap_<hash>', '_transient_seoforge_404_rl_<ip>'). We
// delete by prefix instead of enumerating known names.

// 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 uninstall cleanup
$wpdb->query(
    "DELETE FROM {$wpdb->options}
     WHERE option_name LIKE '\\_transient\\_seoforge\\_%'
        OR option_name LIKE '\\_transient\\_timeout\\_seoforge\\_%'"
);
// 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 uninstall cleanup
$wpdb->query(
    "DELETE FROM {$wpdb->options}
     WHERE option_name LIKE '\\_site\\_transient\\_seoforge\\_%'
        OR option_name LIKE '\\_site\\_transient\\_timeout\\_seoforge\\_%'"
);


// 4. Postmeta — every key we wrote to user content. delete_post_meta_by_key
// uses an indexed query, so this is fast even on large sites.

$seoforge_postmeta_keys = [
    // Analyzer + meta overrides
    '_seoforge_analysis',
    '_seoforge_focus_keyword',
    '_seoforge_meta_title',
    '_seoforge_meta_description',
    '_seoforge_noindex',
    '_seoforge_nofollow',

    // Schema overrides
    '_seoforge_manual_schema',
    '_seoforge_ai_schema',
    '_seoforge_schema_type',

    // Social (OG / Twitter) per-post overrides
    '_seoforge_og_title',
    '_seoforge_og_description',
    '_seoforge_og_image',
    '_seoforge_og_image_id',
    '_seoforge_twitter_title',
    '_seoforge_twitter_description',

    // Robots.txt per-post override
    '_seoforge_robots_txt',

    // A/B testing meta
    '_seoforge_ab_enabled',
    '_seoforge_ab_title_b',
    '_seoforge_ab_desc_b',
    '_seoforge_ab_current',
    '_seoforge_ab_start',
    '_seoforge_ab_served_a',
    '_seoforge_ab_served_b',
    '_seoforge_ab_acknowledge',

    // Brief / video SEO / events / recipes / reviews
    '_seoforge_brief_history',
    '_seoforge_video_url',
    '_seoforge_video_thumbnail',
    '_seoforge_video_duration',
    '_seoforge_video_description',
    '_seoforge_video_embed_url',
    '_seoforge_videos',
    '_seoforge_event_start',
    '_seoforge_event_end',
    '_seoforge_event_address',
    '_seoforge_event_location',
    '_seoforge_recipe_ingredients',
    '_seoforge_recipe_total_time',
    '_seoforge_recipe_yield',
    '_seoforge_review_item',
    '_seoforge_review_rating',

    // Indexing API submission history
    '_seoforge_indexing_history',
];

foreach ( $seoforge_postmeta_keys as $seoforge_key ) {
    delete_metadata( 'post', 0, $seoforge_key, '', true );
}


// 5. Scheduled cron jobs — belt-and-suspenders. The deactivation hook
// already clears these, but uninstall can fire on a still-active plugin
// via WP-CLI, so re-clear here.

wp_clear_scheduled_hook( 'seoforge_daily_gsc_sync' );
wp_clear_scheduled_hook( 'seoforge_cron_scan_links' );
