<?php
/**
 * NeccoSpeak uninstall handler.
 *
 * Removes all saved options, the encryption key, and related transients when the plugin is deleted.
 *
 * @package NeccoSpeak
 */

// Prevent direct execution outside of the WordPress uninstall context.
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
	exit;
}

/**
 * Perform cleanup for a single site.
 *
 * @return void
 */
function neccospeak_uninstall_cleanup() {
	global $wpdb;

	// Phase 5: drop the 4 custom tables and delete the Phase 5 options/cron.
	// Delegate to includes/db.php when it can be loaded; fall back to an
	// inline equivalent otherwise so uninstall still completes cleanly.
	if ( file_exists( plugin_dir_path( __FILE__ ) . 'includes/db.php' ) ) {
		require_once plugin_dir_path( __FILE__ ) . 'includes/db.php';
	}
	if ( function_exists( 'neccospeak_db_uninstall' ) ) {
		neccospeak_db_uninstall();
	} else {
		$neccospeak_tables = array(
			$wpdb->prefix . 'neccospeak_events',
			// 2026-08 レガシー: 会話ログ機能撤去(neccospeak_db_legacy_tables()参照)
			// 後もアップグレード済みサイトにテーブル自体が残り得るため、この
			// フォールバック経路(db.php が読み込めない場合)でも引き続き DROP 対象
			// として残している。
			$wpdb->prefix . 'neccospeak_conversations',
			$wpdb->prefix . 'neccospeak_usage_daily',
			$wpdb->prefix . 'neccospeak_versions',
			$wpdb->prefix . 'neccospeak_chunks', // Phase 6 (G26).
		);
		foreach ( $neccospeak_tables as $neccospeak_table ) {
			// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name derived from $wpdb->prefix; DROP TABLE cannot be parameterized.
			$wpdb->query( 'DROP TABLE IF EXISTS `' . str_replace( '`', '', $neccospeak_table ) . '`' );
		}
		wp_clear_scheduled_hook( 'neccospeak_daily_maintenance' );
		delete_option( 'neccospeak_options_draft' );
		delete_option( 'neccospeak_db_version' );
		delete_option( 'neccospeak_runtime' );
		delete_option( 'neccospeak_prune_last' );
		delete_option( 'neccospeak_rag_engine' ); // Phase 6.
		delete_option( 'neccospeak_rag_state' ); // Phase 6.
		delete_option( 'neccospeak_http_last_error' ); // Phase 6.
		delete_option( 'neccospeak_db_seen_version' ); // Phase 6 (§6.1).
	}

	// Phase 6 (design phase6 §4.1/§6, G15/G26): per-page settings stored as
	// post meta. delete_post_meta_by_key() is the only API that removes a
	// meta key across every post without an unbounded query of our own.
	if ( function_exists( 'delete_post_meta_by_key' ) ) {
		foreach ( array(
			'_neccospeak_page_prompt',
			'_neccospeak_page_welcome',
			'_neccospeak_page_widget',
			'_neccospeak_rag_exclude',
		) as $neccospeak_meta_key ) {
			delete_post_meta_by_key( $neccospeak_meta_key );
		}
	}

	// Delete options (pre-Phase-5).
	delete_option( 'neccospeak_options' );
	delete_option( 'neccospeak_crypto_key' );

	// Delete related transients (rate limiting, etc.).
	// Clean up transients stored in the options table directly.
	$like_value   = $wpdb->esc_like( '_transient_neccospeak_' ) . '%';
	$like_timeout = $wpdb->esc_like( '_transient_timeout_neccospeak_' ) . '%';

	// Bulk-delete our transients directly: WordPress has no API to remove
	// transients by prefix, and caching is irrelevant during uninstall.
	// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
	$wpdb->query(
		$wpdb->prepare(
			"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s",
			$like_value,
			$like_timeout
		)
	);

	// Fallback cleanup for when an external object cache is in use.
	wp_cache_flush();
}

// Multisite support: run cleanup on each site.
if ( is_multisite() ) {
	// Use the core API instead of a direct query on {$wpdb->blogs}.
	$neccospeak_site_ids = get_sites(
		array(
			'fields' => 'ids',
			'number' => 0,
		)
	);

	foreach ( (array) $neccospeak_site_ids as $neccospeak_site_id ) {
		switch_to_blog( (int) $neccospeak_site_id );
		neccospeak_uninstall_cleanup();
		restore_current_blog();
	}
} else {
	neccospeak_uninstall_cleanup();
}
