<?php
/**
 * Parcselect_Migration — one-time data migration from legacy prefixes to parcselect_*.
 *
 * Maps option keys, user meta keys and WooCommerce shipping-method instance settings
 * from old prefixes (parcsepa_*, ps_*, ps_label_*, inpost_*, etyk_*) to the new
 * canonical prefix parcselect_*.
 *
 * Idempotent: writes parcselect_migration_v1_done = '1' on success and short-circuits
 * on subsequent runs. Safe to call from activation hook AND admin_init (latter handles
 * users who upgrade via WP auto-update without triggering activation).
 *
 * Strategy per key:
 *   1. If new key already exists in DB — leave it alone (user explicitly set or already migrated)
 *   2. Else if old key exists — copy value to new key, then delete old
 *   3. Else — no-op
 *
 * @package ParcelSelector
 */

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

if ( ! class_exists( 'Parcselect_Migration' ) ) :

class Parcselect_Migration {

	const FLAG_OPTION = 'parcselect_migration_v1_done';

	/**
	 * Entry point. Idempotent — runs once per installation.
	 */
	public static function maybe_run() {
		if ( '1' === (string) get_option( self::FLAG_OPTION, '' ) ) {
			return;
		}
		self::migrate_options();
		self::migrate_user_meta();
		self::migrate_wc_instance_settings();
		self::cleanup_legacy_dead_keys();
		update_option( self::FLAG_OPTION, '1', true );
	}

	/**
	 * Map of legacy option keys -> new parcselect_ keys.
	 * Order matters: when multiple legacy keys map to the same new key,
	 * the FIRST one in this map wins (parcsepa_* preferred over ps_label_* over inpost_*).
	 */
	protected static function option_map() {
		return array(
			// ── parcsepa_* (most common legacy prefix) ──
			'parcsepa_activated'                        => 'parcselect_activated',
			'parcsepa_activation_consent'               => 'parcselect_activation_consent',
			'parcsepa_activation_notice_dismissed'      => 'parcselect_activation_notice_dismissed',
			'parcsepa_consent_given'                    => 'parcselect_consent_given',
			'parcsepa_crash_reports_enabled'            => 'parcselect_crash_reports_enabled',
			'parcsepa_custom_pickup'                    => 'parcselect_custom_pickup',
			'parcsepa_default_cost_migration_v431'      => 'parcselect_default_cost_migration_v431',
			'parcsepa_default_country'                  => 'parcselect_default_country',
			'parcsepa_embed_secret'                     => 'parcselect_embed_secret',
			'parcsepa_enabled_carriers'                 => 'parcselect_enabled_carriers',
			'parcsepa_etherscan_api_key'                => 'parcselect_etherscan_api_key',
			'parcsepa_installation_id'                  => 'parcselect_installation_id',
			'parcsepa_label_api_key_id'                 => 'parcselect_label_api_key_id',
			'parcsepa_label_api_secret'                 => 'parcselect_label_api_secret',
			'parcsepa_label_ceb_salt'                   => 'parcselect_label_ceb_salt',
			'parcsepa_label_ceb_url'                    => 'parcselect_label_ceb_url',
			'parcsepa_label_client_id'                  => 'parcselect_label_client_id',
			'parcsepa_label_client_secret'              => 'parcselect_label_client_secret',
			'parcsepa_label_debug_enabled'              => 'parcselect_label_debug_enabled',
			'parcsepa_label_default_size'               => 'parcselect_label_default_size',
			'parcsepa_label_default_weight'             => 'parcselect_label_default_weight',
			'parcsepa_label_direct_api'                 => 'parcselect_label_direct_api',
			'parcsepa_label_enrolled_at'                => 'parcselect_label_enrolled_at',
			'parcsepa_label_installation_id'            => 'parcselect_label_installation_id',
			'parcsepa_label_mapserver_url'              => 'parcselect_label_mapserver_url',
			'parcsepa_label_margin'                     => 'parcselect_label_margin',
			'parcsepa_label_merchant_id'                => 'parcselect_label_merchant_id',
			'parcsepa_label_notify_email'               => 'parcselect_label_notify_email',
			'parcsepa_label_oracle_url'                 => 'parcselect_label_oracle_url',
			'parcsepa_label_password'                   => 'parcselect_label_password',
			'parcsepa_label_polling_purge_done'         => 'parcselect_label_polling_purge_done',
			'parcsepa_label_proxy_secret'               => 'parcselect_label_proxy_secret',
			'parcsepa_label_proxy_url'                  => 'parcselect_label_proxy_url',
			'parcsepa_label_refresh_token'              => 'parcselect_label_refresh_token',
			'parcsepa_label_sandbox'                    => 'parcselect_label_sandbox',
			'parcsepa_label_sender_city'                => 'parcselect_label_sender_city',
			'parcsepa_label_sender_company'             => 'parcselect_label_sender_company',
			'parcsepa_label_sender_email'               => 'parcselect_label_sender_email',
			'parcsepa_label_sender_locker_id'           => 'parcselect_label_sender_locker_id',
			'parcsepa_label_sender_name'                => 'parcselect_label_sender_name',
			'parcsepa_label_sender_phone'               => 'parcselect_label_sender_phone',
			'parcsepa_label_sender_postcode'            => 'parcselect_label_sender_postcode',
			'parcsepa_label_sender_street'              => 'parcselect_label_sender_street',
			'parcsepa_label_service_id'                 => 'parcselect_label_service_id',
			'parcsepa_label_skip_sender_stage'          => 'parcselect_label_skip_sender_stage',
			'parcsepa_label_sol_rate_pln'               => 'parcselect_label_sol_rate_pln',
			'parcsepa_label_sol_wallet'                 => 'parcselect_label_sol_wallet',
			'parcsepa_label_sweep_installation_id'      => 'parcselect_label_sweep_installation_id',
			'parcsepa_label_use_iframe'                 => 'parcselect_label_use_iframe',
			'parcsepa_label_username'                   => 'parcselect_label_username',
			'parcsepa_label_wallet_sol'                 => 'parcselect_label_wallet_sol',
			'parcsepa_label_webhook_secret'             => 'parcselect_label_webhook_secret',
			'parcsepa_local_seo_enabled'                => 'parcselect_local_seo_enabled',
			'parcsepa_map_engine'                       => 'parcselect_map_engine',
			'parcsepa_needs_zone_setup'                 => 'parcselect_needs_zone_setup',
			'parcsepa_paczkomaty_options'               => 'parcselect_paczkomaty_options',
			'parcsepa_routes_seeded'                    => 'parcselect_routes_seeded',
			'parcsepa_sender_city'                      => 'parcselect_sender_city',
			'parcsepa_sender_company'                   => 'parcselect_sender_company',
			'parcsepa_sender_email'                     => 'parcselect_sender_email',
			'parcsepa_sender_locker_id'                 => 'parcselect_sender_locker_id',
			'parcsepa_sender_name'                      => 'parcselect_sender_name',
			'parcsepa_sender_phone'                     => 'parcselect_sender_phone',
			'parcsepa_sender_postcode'                  => 'parcselect_sender_postcode',
			'parcsepa_sender_street'                    => 'parcselect_sender_street',
			'parcsepa_share_diagnostics'                => 'parcselect_share_diagnostics',
			'parcsepa_shipping_config'                  => 'parcselect_shipping_config',
			'parcsepa_shipping_zone_id'                 => 'parcselect_shipping_zone_id',
			'parcsepa_shop_origin_country'              => 'parcselect_shop_origin_country',
			'parcsepa_show_legend'                      => 'parcselect_show_legend',
			'parcsepa_show_point_types'                 => 'parcselect_show_point_types',
			'parcsepa_show_powered_by'                  => 'parcselect_show_powered_by',
			'parcsepa_solana_rpc_url'                   => 'parcselect_solana_rpc_url',
			'parcsepa_wizard_completed'                 => 'parcselect_wizard_completed',
			'parcsepa_zone_setup_complete'              => 'parcselect_zone_setup_complete',
			'parcsepa_zone_setup_version'               => 'parcselect_zone_setup_version',
			'parcsepa_prosiaczek_rules_v2'              => 'parcselect_prosiaczek_rules_v2',

			// ── ps_label_* (older legacy) — only used if parcsepa_* didn't migrate first ──
			'ps_label_debug_enabled'                    => 'parcselect_label_debug_enabled',
			'ps_label_default_weight'                   => 'parcselect_label_default_weight',
			'ps_label_enrolled_at'                      => 'parcselect_label_enrolled_at',
			'ps_label_mapserver_url'                    => 'parcselect_label_mapserver_url',
			'ps_label_proxy_secret'                     => 'parcselect_label_proxy_secret',
			'ps_label_proxy_url'                        => 'parcselect_label_proxy_url',
			'ps_label_sender_city'                      => 'parcselect_label_sender_city',
			'ps_label_sender_company'                   => 'parcselect_label_sender_company',
			'ps_label_sender_email'                     => 'parcselect_label_sender_email',
			'ps_label_sender_name'                      => 'parcselect_label_sender_name',
			'ps_label_sender_phone'                     => 'parcselect_label_sender_phone',
			'ps_label_sender_postcode'                  => 'parcselect_label_sender_postcode',
			'ps_label_sender_street'                    => 'parcselect_label_sender_street',
			'ps_label_webhook_secret'                   => 'parcselect_label_webhook_secret',

			// ── inpost_* (oldest, brand-name legacy keys reviewer flagged) ──
			'inpost_activated'                          => 'parcselect_activated',
			'inpost_installation_id'                    => 'parcselect_installation_id',
			'inpost_order_display_html'                 => 'parcselect_order_display_html',
			'inpost_paczkomaty_options'                 => 'parcselect_paczkomaty_options',
			'inpost_server_notice'                      => 'parcselect_server_notice',
		);
	}

	/**
	 * Dead legacy keys to delete outright (license/trialware artifacts).
	 * No new key — these features no longer exist.
	 */
	protected static function dead_keys() {
		return array(
			// License (cut in K.2.0c)
			'parcsepa_license_mnemonic',
			'parcsepa_seed_pending',
			'parcsepa_autoinstall_wc_done',
			'parcsepa_autoinstall_wc_error',
			'parcsepa_autoinstall_wc_pending',

			// inpost license (legacy from earlier version)
			'inpost_counter_limit',
			'inpost_counter_value',
			'inpost_license_ends_at',
			'inpost_license_mnemonic',
			'inpost_license_type',
			'inpost_order_limit',
			'inpost_server_confirmed_ends_at',
		);
	}

	/**
	 * Map of legacy user meta keys -> new parcselect_ keys.
	 */
	protected static function user_meta_map() {
		return array(
			'parcsepa_admin_theme'                      => 'parcselect_admin_theme',
			'parcsepa_autoconfig_dismissed'             => 'parcselect_autoconfig_dismissed',
			'parcsepa_consent_notice_dismissed'         => 'parcselect_consent_notice_dismissed',
			'parcsepa_upgrade_nag_dismissed'            => 'parcselect_upgrade_nag_dismissed',
		);
	}

	/**
	 * Migrate options (wp_options table). Per-key strategy:
	 *   - If new key already populated → leave it (don't clobber user changes)
	 *   - Else if old key exists → copy to new, delete old
	 */
	protected static function migrate_options() {
		foreach ( self::option_map() as $old => $new ) {
			$old_value = get_option( $old, '__parcselect_missing__' );
			if ( '__parcselect_missing__' === $old_value ) {
				continue; // old key not present
			}
			$new_value = get_option( $new, '__parcselect_missing__' );
			if ( '__parcselect_missing__' === $new_value ) {
				// New key truly absent — copy from old.
				update_option( $new, $old_value, false );
			}
			delete_option( $old );
		}
	}

	/**
	 * Bulk-rename user meta keys at the DB level.
	 * Single UPDATE per old key — handles all users at once.
	 */
	protected static function migrate_user_meta() {
		global $wpdb;
		foreach ( self::user_meta_map() as $old => $new ) {
			$wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- jednorazowa migracja/uninstall, brak API WC; cache zbedny
				$wpdb->prepare(
					"UPDATE {$wpdb->usermeta} SET meta_key = %s WHERE meta_key = %s",
					$new,
					$old
				)
			);
		}
	}

	/**
	 * WooCommerce shipping-method instance settings live under dynamic option keys:
	 *   woocommerce_<METHOD_ID>_<INSTANCE_ID>_settings
	 *
	 * Old method id was 'parcsepa_inpost_paczkomat'; new id is 'parcselect_inpost_paczkomat'.
	 * Rename every matching option key so existing zone instances retain their settings.
	 */
	protected static function migrate_wc_instance_settings() {
		global $wpdb;
		$rows = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- jednorazowa migracja/uninstall, brak API WC; cache zbedny
			$wpdb->prepare(
				"SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s",
				$wpdb->esc_like( 'woocommerce_parcsepa_inpost_paczkomat_' ) . '%' . $wpdb->esc_like( '_settings' )
			)
		);
		if ( empty( $rows ) ) {
			return;
		}
		foreach ( $rows as $row ) {
			$old_name = $row->option_name;
			$new_name = str_replace(
				'woocommerce_parcsepa_inpost_paczkomat_',
				'woocommerce_parcselect_inpost_paczkomat_',
				$old_name
			);
			if ( $old_name === $new_name ) {
				continue;
			}
			$value = get_option( $old_name );
			if ( false === get_option( $new_name, false ) ) {
				update_option( $new_name, $value );
			}
			delete_option( $old_name );
		}
	}

	/**
	 * Delete dead legacy options that have no replacement (license/trialware artifacts).
	 */
	protected static function cleanup_legacy_dead_keys() {
		foreach ( self::dead_keys() as $key ) {
			delete_option( $key );
		}
	}
}

endif;
