<?php
/**
 * One-time migration from the legacy standalone plugins.
 *
 * Runs on plugin activation. If `bw_dev_settings` does not yet exist (or has
 * no module data), reads the legacy options from the five source plugins and
 * writes them into the unified `bw_dev_settings` schema.
 *
 * Legacy options are NOT deleted — the user manually deactivates each source
 * plugin once they've verified the corresponding module is doing the job.
 * The source plugins' own uninstall hooks clean up their options when they
 * are deleted.
 *
 * Idempotent: re-running activation never clobbers existing bw-dev data.
 *
 * @package BW_Dev
 */

defined( 'ABSPATH' ) || exit;

class BW_Dev_Migration {

	const VERSION_OPTION = 'bw_dev_migration_version';
	// v2 (2026-05-28): added inline_search, animate_row, scroll_down legacy reads.
	// Bumping the version re-runs the (conservative, fill-missing-only) migration
	// on sites that already ran v1, so they pick up the three new sections.
	const CURRENT_VERSION = 2;

	/**
	 * Activation hook entry point. Wired via register_activation_hook in the
	 * main plugin file. Runs once on first activation, and is idempotent so
	 * subsequent reactivations are safe — the table install + cron schedule
	 * checks always execute (cheap; the helpers self-skip when up to date).
	 */
	public static function on_activation(): void {
		$ran = (int) get_option( self::VERSION_OPTION, 0 );
		if ( $ran < self::CURRENT_VERSION ) {
			self::migrate_legacy_options();
			update_option( self::VERSION_OPTION, self::CURRENT_VERSION );
		}

		// Login Activity Log infrastructure — table + daily prune cron. Both
		// helpers are idempotent (option-version check + wp_next_scheduled
		// check), so calling on every activation is safe.
		if ( class_exists( 'BW_Dev_Module_Login_Log' ) ) {
			BW_Dev_Module_Login_Log::install_table();
			BW_Dev_Module_Login_Log::schedule_cron();
		}

		// Scheduled Post Actions — hourly recurring poll. Idempotent
		// (wp_next_scheduled guard inside the helper).
		if ( class_exists( 'BW_Dev_Module_Scheduled_Actions' ) ) {
			BW_Dev_Module_Scheduled_Actions::schedule_cron();
		}
	}

	/**
	 * Build a bw_dev_settings payload from any legacy options found on the
	 * site. Modules that have no legacy data are simply absent from the
	 * payload (so is_module_enabled() returns the default-true for them).
	 *
	 * The merge is conservative: only fills in keys that are NOT already
	 * populated in bw_dev_settings, so a partial migration plus a manual
	 * setting tweak survives a re-activation.
	 */
	public static function migrate_legacy_options(): void {
		$existing = (array) get_option( BW_Dev_Settings::OPTION, array() );

		// Favicon: bw_favicon_url (string).
		$legacy_favicon_url = get_option( 'bw_favicon_url', null );
		if ( null !== $legacy_favicon_url && ! isset( $existing['favicon'] ) ) {
			$existing['favicon'] = array( 'url' => (string) $legacy_favicon_url );
		}

		// Sticky: bw_sticky_settings (array with 'elements' key).
		$legacy_sticky = get_option( 'bw_sticky_settings', null );
		if ( is_array( $legacy_sticky ) && ! isset( $existing['sticky'] ) ) {
			$elements = isset( $legacy_sticky['elements'] ) && is_array( $legacy_sticky['elements'] )
				? array_values( $legacy_sticky['elements'] )
				: array();
			$existing['sticky'] = array( 'elements' => $elements );
		}

		// YouTube: bw_youtube_embed_settings (['acf_field' => string]).
		$legacy_youtube = get_option( 'bw_youtube_embed_settings', null );
		if ( is_array( $legacy_youtube ) && ! isset( $existing['youtube'] ) ) {
			$existing['youtube'] = array(
				'acf_field' => isset( $legacy_youtube['acf_field'] ) ? (string) $legacy_youtube['acf_field'] : 'youtube_link',
			);
		}

		// Admin Columns: bw_admin_column_settings (keyed by post type).
		$legacy_ac = get_option( 'bw_admin_column_settings', null );
		if ( is_array( $legacy_ac ) && ! isset( $existing['admin_columns'] ) ) {
			$existing['admin_columns'] = $legacy_ac;
		}

		// Admin Note: bw_admin_note_settings (['post_types' => [...]]).
		// The _bw_admin_note post-meta key is preserved — no data migration
		// needed for the individual notes; only the per-post-type enable map
		// moves to the new schema.
		$legacy_an = get_option( 'bw_admin_note_settings', null );
		if ( is_array( $legacy_an ) && ! isset( $existing['admin_note'] ) ) {
			$existing['admin_note'] = array(
				'post_types' => isset( $legacy_an['post_types'] ) && is_array( $legacy_an['post_types'] )
					? array_values( $legacy_an['post_types'] )
					: array( 'page' ),
			);
		}

		// Established Year: standalone `established-year` plugin's `bw_established_date`
		// (a YYYY-MM-DD string) → bw_dev_settings.established_year.date.
		$legacy_est = get_option( 'bw_established_date', null );
		if ( is_string( $legacy_est ) && '' !== trim( $legacy_est ) && ! isset( $existing['established_year'] ) ) {
			$existing['established_year'] = array( 'date' => trim( $legacy_est ) );
		}

		// Inline Search: standalone `bw-inline-search` plugin's single
		// `bw_search_container_selector` option (a string) → inline_search.
		$legacy_search = get_option( 'bw_search_container_selector', null );
		if ( null !== $legacy_search && ! isset( $existing['inline_search'] ) ) {
			$existing['inline_search'] = array( 'container_selector' => (string) $legacy_search );
		}

		// Animate Row: standalone `bw-animate-row` plugin's ten flat options
		// (`bw_animation_*` + `bw_target_*`) → the single animate_row section.
		// Migrate only when at least one legacy option exists.
		if ( ! isset( $existing['animate_row'] ) && false !== get_option( 'bw_animation_type', false ) ) {
			$existing['animate_row'] = array(
				'enabled'         => '1' === (string) get_option( 'bw_animation_enabled', '1' ) ? 1 : 0,
				'type'            => (string) get_option( 'bw_animation_type', 'fade-up' ),
				'duration'        => absint( get_option( 'bw_animation_duration', 1000 ) ),
				'easing'          => (string) get_option( 'bw_animation_easing', 'ease-in-out' ),
				'delay'           => absint( get_option( 'bw_animation_delay', 0 ) ),
				'offset'          => absint( get_option( 'bw_animation_offset', 100 ) ),
				'once'            => '1' === (string) get_option( 'bw_animation_once', '1' ) ? 1 : 0,
				'target_rows'     => '1' === (string) get_option( 'bw_target_rows', '1' ) ? 1 : 0,
				'target_columns'  => '1' === (string) get_option( 'bw_target_columns', '1' ) ? 1 : 0,
				'target_sections' => '1' === (string) get_option( 'bw_target_sections', '1' ) ? 1 : 0,
			);
		}

		// Scroll Down: standalone `bw-scroll-down` plugin's `bw_scroll_down_settings`
		// (an array) → scroll_down. Keys already line up; copied verbatim and
		// re-sanitized by the module's sanitize() the first time the tab is saved.
		$legacy_scroll = get_option( 'bw_scroll_down_settings', null );
		if ( is_array( $legacy_scroll ) && ! isset( $existing['scroll_down'] ) ) {
			$existing['scroll_down'] = $legacy_scroll;
		}

		update_option( BW_Dev_Settings::OPTION, $existing );
	}

	/**
	 * Map of source plugin basename → bw-dev module slug it replaces.
	 * Used by the diagnostics view (and ad-hoc by adi when deactivating
	 * legacy plugins) to confirm the new module is enabled before the
	 * old one is turned off.
	 */
	public static function legacy_map(): array {
		return array(
			'bw-favicon/bw-favicon.php'                   => 'favicon',
			'bw-sticky-settings/bw-sticky-settings.php'   => 'sticky',
			'bw-youtube-embed/bw-youtube-embed.php'       => 'youtube',
			'bw-pretty-post-link/bw-pretty-post-link.php' => 'post_link',
			'bw-admin-column/bw-admin-column.php'         => 'admin_columns',
			'bw-admin-note/bw-admin-note.php'             => 'admin_note',
			'bw-inline-search/bw-inline-search.php'       => 'inline_search',
			'bw-animate-row/bw-animate-row.php'           => 'animate_row',
			'bw-scroll-down/bw-scroll-down.php'           => 'scroll_down',
		);
	}
}
