<?php
/**
 * プラグインアンインストール処理
 *
 * 既定は「データ保持モード」。テーブル・オプション・暗号化キー・
 * アップロードファイルをすべて残す。これにより Free 版から Pro 版への
 * 入れ替えや、プラグインの一時的な削除を行っても、申込・会員・決済データが
 * 失われない（Free→Pro 移行を安全にするための仕様）。
 *
 * テーブル DROP を含む完全削除は、ユーザーが明示的にオプトインした場合のみ
 * 実行する:
 *   - wp-config.php: define( 'CONF_MANAGER_PURGE_ON_UNINSTALL', true );
 *   - または conf_manager_settings['purge_data_on_uninstall'] === true
 *
 * @package ConfManager
 * @since   1.0.0
 */

declare(strict_types=1);

// WordPress からの呼び出し以外は拒否
if ( ! defined( 'ABSPATH' ) || ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
	exit;
}

/*
 * データ完全削除のオプトイン判定。
 * 既定（オプトインなし）ではデータを保持し、何も削除せずに終了する。
 * これにより、Free→Pro 入れ替えやプラグインの一時削除でデータが失われない。
 */
$conf_should_purge = ( defined( 'CONF_MANAGER_PURGE_ON_UNINSTALL' ) && CONF_MANAGER_PURGE_ON_UNINSTALL );
if ( ! $conf_should_purge ) {
	$conf_settings     = get_option( 'conf_manager_settings', [] );
	$conf_should_purge = is_array( $conf_settings ) && ! empty( $conf_settings['purge_data_on_uninstall'] );
}
if ( ! $conf_should_purge ) {
	// データ保持モード: テーブル・オプション・ファイルを残してそのまま終了
	return;
}

// ===== ここから下はオプトイン時のみ実行: 全データ・テーブルを物理削除 =====

global $wpdb;

$conf_prefix = $wpdb->prefix . 'conf_';

// 全テーブルを削除（依存関係の逆順）
$conf_tables = [
	'promotion_channels',
	'online_settings',
	'notifications',
	'audit_log',
	'security_keys',
	'sponsor_downloads',
	'sponsor_staff',
	'sponsor_ranks',
	'sponsors',
	'coi_declarations',
	'reviews',
	'coauthors',
	'submissions',
	'receipts',
	'invoices',
	'coupons',
	'payment_adjustments',
	'payments',
	'group_members',
	'registrations',
	'sessions',
	'events',
	'members',
];

// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Uninstall drops custom tables; prefix from $wpdb->prefix and table names are a hardcoded whitelist.
foreach ( $conf_tables as $conf_table ) {
	$wpdb->query( "DROP TABLE IF EXISTS {$conf_prefix}{$conf_table}" );
}
// phpcs:enable

// カスタムロールを削除
remove_role( 'conf_editor' );
remove_role( 'conf_viewer' );
remove_role( 'conf_staff' );

// administrator からカスタムケイパビリティを除去
$conf_admin_role = get_role( 'administrator' );
if ( $conf_admin_role ) {
	$conf_custom_caps = [
		'conf_manager_access',
		'conf_manager_manage_settings',
		'conf_manager_view_dashboard',
		'conf_manager_view_events',
		'conf_manager_edit_events',
		'conf_manager_view_members',
		'conf_manager_edit_members',
		'conf_manager_import_members',
		'conf_manager_export_members',
		'conf_manager_view_registrations',
		'conf_manager_edit_registrations',
		'conf_manager_checkin',
		'conf_manager_view_payments',
		'conf_manager_edit_payments',
		'conf_manager_export_payments',
		'conf_manager_view_notifications',
		'conf_manager_edit_notifications',
		'conf_manager_view_sponsors',
		'conf_manager_edit_sponsors',
		'conf_manager_view_submissions',
		'conf_manager_edit_submissions',
		'conf_manager_view_promotions',
		'conf_manager_edit_promotions',
	];
	foreach ( $conf_custom_caps as $conf_cap ) {
		$conf_admin_role->remove_cap( $conf_cap );
	}
}

// プラグインオプションを削除
$conf_options_to_delete = [
	'conf_manager_db_version',
	'conf_manager_settings',
	'conf_manager_encryption_key',
	'conf_manager_encryption_key_hash',
	'conf_manager_roles_version',
	'conf_manager_payment_methods',
	'conf_manager_tax_registration_no',
	'conf_manager_stripe_secret_key',
	'conf_manager_stripe_publishable_key',
	'conf_manager_stripe_webhook_secret',
	'conf_manager_stripe_mode',
	// 転送ヘッダを無視した警告を閉じたかどうか
	'conf_manager_trusted_proxy_notice_dismissed',
	// バウンスの記録。**メールアドレスを保持する**ので消し忘れると
	// アンインストール後も個人情報が残る。
	'conf_manager_mail_bounces',
	// レガシー（以前のバージョンで書かれた可能性のあるもの）
	'conf_manager_license_key',
	'conf_manager_license_status',
	'conf_manager_license_expires',
	'conf_manager_peatix_url',
];
foreach ( $conf_options_to_delete as $conf_option_name ) {
	delete_option( $conf_option_name );
}

// ---------------------------------------------------------------------------
// 前方一致での一掃
// ---------------------------------------------------------------------------
// ⚠️ 上の配列列挙だけだと、**次に足した option がまた同じ形で漏れる**。
// 実際 conf_manager_mail_bounces（キーがバウンスしたメールアドレス）は
// 列挙から漏れていて、アンインストール後も個人情報が wp_options に残っていた。
// Pro 線からの逆提案（handoff notes/2026-08-19-pro-to-free-6.md）を受けて追加。
//
// CLAUDE.md 規則 12 のとおり wp_options キーは conf_manager_* で統一しているので、
// この前方一致で自分のものだけが確実に当たる（WP コアや他プラグインには当たらない）。
// $wpdb->esc_like() でアンダースコアを退避すること（LIKE の _ は 1 文字ワイルドカード）。
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Uninstall cleanup; scanning options table.
$conf_option_names = $wpdb->get_col(
	$wpdb->prepare(
		"SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s",
		$wpdb->esc_like( 'conf_manager_' ) . '%'
	)
);
foreach ( (array) $conf_option_names as $conf_option_name ) {
	delete_option( $conf_option_name );
}

// Transient も掃く。conf_manager_* の命名から外れるものがあるため、
// 実際に使っている接頭辞を明示する（`conf_` だけで掃くと他プラグインに当たりうる）。
// ⚠️ conf_otp_session_* / conf_passkey_token_* は**認証に使えるトークン**なので、
// 残すと削除後もマイページに入れてしまう。
$conf_transient_prefixes = [
	'conf_manager_',      // conf_manager_published_events ほか
	'conf_rl_',           // RateLimiter のカウンタ
	'conf_rlock_',        // RateLimiter のロックアウト
	'conf_otp_',          // OTP 本体・試行回数・セッション
	'conf_passkey_token_',
	'conf_selfreg_',      // 自己登録の OTP・試行回数・トークン
];
foreach ( $conf_transient_prefixes as $conf_transient_prefix ) {
	$conf_transient_rows = $wpdb->get_col(
		$wpdb->prepare(
			"SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s",
			'_transient_' . $wpdb->esc_like( $conf_transient_prefix ) . '%',
			'_transient_timeout_' . $wpdb->esc_like( $conf_transient_prefix ) . '%'
		)
	);
	foreach ( (array) $conf_transient_rows as $conf_transient_row ) {
		// delete_transient() ではなく option を直に消す。timeout 行も同じ走査で拾うため。
		delete_option( $conf_transient_row );
	}
}
// phpcs:enable

// 旧プログラム PDF オプション（レガシー）を削除
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Uninstall cleanup; scanning options table for legacy keys.
$conf_legacy_options = $wpdb->get_col(
	"SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE 'conf_manager_program_pdfs_%'"
);
// phpcs:enable
foreach ( $conf_legacy_options as $conf_option_name ) {
	delete_option( $conf_option_name );
}

// アップロードディレクトリ内のプラグインファイルを削除
$conf_upload_dir = wp_upload_dir();
$conf_plugin_dir = $conf_upload_dir['basedir'] . '/conf-manager';
if ( is_dir( $conf_plugin_dir ) ) {
	// PDF・証明書等の生成ファイルを再帰削除
	$conf_iterator = new RecursiveDirectoryIterator( $conf_plugin_dir, RecursiveDirectoryIterator::SKIP_DOTS );
	$conf_files    = new RecursiveIteratorIterator( $conf_iterator, RecursiveIteratorIterator::CHILD_FIRST );
	foreach ( $conf_files as $conf_file ) {
		if ( $conf_file->isDir() ) {
			// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_rmdir -- Uninstall cleanup; WP_Filesystem requires FS credentials not available during uninstall.
			rmdir( $conf_file->getRealPath() );
		} else {
			wp_delete_file( $conf_file->getRealPath() );
		}
	}
	// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_rmdir -- Uninstall cleanup; WP_Filesystem requires FS credentials not available during uninstall.
	rmdir( $conf_plugin_dir );
}
