<?php
/**
 * Database Upgrader
 *
 * Handles data transformations during upgrades.
 * Contains Migration 5 & 7 for existing installations.
 *
 * @package Wisewand
 * @since 1.0.2
 * @license GPL-2.0-or-later
 */

namespace Wisewand\Database;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_error_log, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange -- error_log() only used within WP_DEBUG conditionals; direct database calls and schema changes required for upgrades

/**
 * Database upgrade manager
 *
 * Handles data migrations for existing installations.
 * Only contains migrations that transform existing data.
 *
 * @since 1.0.2
 */
class Database_Upgrader {

	/**
	 * Run Migration 5: Add imported status sub-statuses
	 *
	 * Replaces 'draft' and 'published' with 'imported_draft' and 'imported_published'
	 * for entries that have wordpress_post_id set.
	 *
	 * Migration only runs if table already exists (upgrade scenario).
	 * For new installations, the base schema already has the correct ENUM.
	 *
	 * @since 1.0.2
	 * @global wpdb $wpdb WordPress database abstraction object.
	 * @return bool True if migration ran successfully or already completed.
	 */
	public static function run_migration_5() {
		global $wpdb;

		$table_name = $wpdb->prefix . 'wisewand_editorial_calendar';

		// Check if table exists (upgrade scenario only)
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from $wpdb->prefix is safe
		$table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) );

		if ( ! $table_exists ) {
			// Table doesn't exist yet - skip migration (fresh install)
			return true;
		}

		// Check current ENUM values
		$check_query = $wpdb->prepare(
			"SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS
			WHERE TABLE_SCHEMA = %s
			AND TABLE_NAME = %s
			AND COLUMN_NAME = 'wordpress_status'",
			DB_NAME,
			$table_name
		);

		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query already prepared above
		$current_enum = $wpdb->get_var( $check_query );

		// Check if migration already completed
		if ( $current_enum && strpos( $current_enum, 'imported_draft' ) !== false ) {
			if ( WP_DEBUG ) {
				error_log( 'Wisewand: Migration 5 already completed (imported_draft found in ENUM)' );
			}
			return true;
		}

		// Migration needed
		if ( WP_DEBUG ) {
			error_log( 'Wisewand: Starting Migration 5 - Adding imported status sub-statuses' );
			error_log( 'Wisewand: Current ENUM: ' . $current_enum );
		}

		// Step 1: Count existing entries for logging
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from $wpdb->prefix is safe
		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
		$draft_count = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(*) FROM `{$table_name}`
				WHERE wordpress_status = %s
				AND wordpress_post_id IS NOT NULL
				AND wordpress_post_id > 0",
				'draft'
			)
		);

		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
		$published_count = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(*) FROM `{$table_name}`
				WHERE wordpress_status = %s
				AND wordpress_post_id IS NOT NULL
				AND wordpress_post_id > 0",
				'published'
			)
		);
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

		if ( WP_DEBUG ) {
			error_log( 'Wisewand: Found ' . $draft_count . ' draft entries to migrate' );
			error_log( 'Wisewand: Found ' . $published_count . ' published entries to migrate' );
		}

		// Step 2: Migrate existing 'draft' to 'imported_draft'
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from $wpdb->prefix is safe
		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
		$wpdb->query(
			$wpdb->prepare(
				"UPDATE `{$table_name}`
				SET wordpress_status = %s
				WHERE wordpress_status = %s
				AND wordpress_post_id IS NOT NULL
				AND wordpress_post_id > 0",
				'imported_draft',
				'draft'
			)
		);
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

		if ( WP_DEBUG && ! $wpdb->last_error ) {
			error_log( 'Wisewand: Migrated ' . $wpdb->rows_affected . ' draft entries to imported_draft' );
		}

		// Step 3: Migrate existing 'published' to 'imported_published'
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from $wpdb->prefix is safe
		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
		$wpdb->query(
			$wpdb->prepare(
				"UPDATE `{$table_name}`
				SET wordpress_status = %s
				WHERE wordpress_status = %s
				AND wordpress_post_id IS NOT NULL
				AND wordpress_post_id > 0",
				'imported_published',
				'published'
			)
		);
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

		if ( WP_DEBUG && ! $wpdb->last_error ) {
			error_log( 'Wisewand: Migrated ' . $wpdb->rows_affected . ' published entries to imported_published' );
		}

		// Step 4: ALTER TABLE to update ENUM definition (schema already handled by Database_Schema)
		// This step is now redundant as Database_Schema creates table with correct ENUM
		// But we keep it for backward compatibility with existing installations

		if ( WP_DEBUG ) {
			if ( $wpdb->last_error ) {
				error_log( 'Wisewand: Migration 5 error: ' . $wpdb->last_error );
				return false;
			} else {
				error_log( 'Wisewand: Migration 5 completed successfully' );
				update_option( 'wisewand_migration_5_completed', current_time( 'mysql' ) );
			}
		}

		return true;
	}

	/**
	 * Run Migration 7: Add sync_status and migrate from enabled_for_site
	 *
	 * Adds sync_status column and migrates existing enabled_for_site values.
	 * Migration only runs if table already exists (upgrade scenario).
	 *
	 * @since 1.0.2
	 * @global wpdb $wpdb WordPress database abstraction object.
	 * @return bool True if migration ran successfully or already completed.
	 */
	public static function run_migration_7() {
		global $wpdb;

		$table_name = $wpdb->prefix . 'wisewand_rss_feeds';

		// Check if table exists (upgrade scenario only)
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from $wpdb->prefix is safe
		$table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) );

		if ( ! $table_exists ) {
			// Table doesn't exist yet - skip migration (fresh install)
			return true;
		}

		// Check if sync_status column already exists
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from $wpdb->prefix is safe
		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
		$sync_status_exists = $wpdb->get_results(
			$wpdb->prepare( "SHOW COLUMNS FROM `{$table_name}` LIKE %s", 'sync_status' )
		);
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

		if ( ! empty( $sync_status_exists ) ) {
			if ( WP_DEBUG ) {
				error_log( 'Wisewand: Migration 7 already completed (sync_status column exists)' );
			}
			return true;
		}

		// Migration needed
		if ( WP_DEBUG ) {
			error_log( 'Wisewand: Starting Migration 7 - Adding RSS sync_status column' );
		}

		// Check if enabled_for_site exists to migrate data
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from $wpdb->prefix is safe
		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
		$enabled_for_site_exists = $wpdb->get_results(
			$wpdb->prepare( "SHOW COLUMNS FROM `{$table_name}` LIKE %s", 'enabled_for_site' )
		);
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

		if ( ! empty( $enabled_for_site_exists ) ) {
			// Migrate existing enabled_for_site values to sync_status
			// Note: sync_status column is already added by Database_Schema
			// We just need to migrate the data
			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from $wpdb->prefix is safe
			// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
			$wpdb->query(
				"UPDATE `{$table_name}`
				SET sync_status = CASE
					WHEN enabled_for_site = 1 THEN 'enabled'
					ELSE 'disabled'
				END
				WHERE sync_status IS NULL OR sync_status = ''"
			);
			// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

			if ( WP_DEBUG && ! $wpdb->last_error ) {
				error_log( 'Wisewand: Migrated enabled_for_site values to sync_status' );
			}
		}

		if ( WP_DEBUG ) {
			if ( $wpdb->last_error ) {
				error_log( 'Wisewand: Migration 7 error: ' . $wpdb->last_error );
				return false;
			} else {
				error_log( 'Wisewand: Migration 7 completed successfully - RSS sync_status ready' );
				update_option( 'wisewand_migration_7_completed', current_time( 'mysql' ) );
			}
		}

		return true;
	}

	/**
	 * Run Migration 9: Add last_output_id column to wisewand_rss_articles
	 *
	 * Adds last_output_id column for caching discoverarticle output IDs.
	 * This eliminates redundant API calls during import, cutting API usage in half.
	 *
	 * Migration only runs if table already exists (upgrade scenario).
	 * For new installations, the base schema already has the column.
	 *
	 * @since 1.2.0
	 * @global wpdb $wpdb WordPress database abstraction object.
	 * @return bool True if migration ran successfully or already completed.
	 */
	public static function run_migration_9() {
		global $wpdb;

		$table_name = $wpdb->prefix . 'wisewand_rss_articles';

		// Check if table exists (upgrade scenario only)
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from $wpdb->prefix is safe
		$table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) );

		if ( ! $table_exists ) {
			// Table doesn't exist yet - skip migration (fresh install)
			return true;
		}

		// Check if last_output_id column already exists
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from $wpdb->prefix is safe
		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
		$column_exists = $wpdb->get_results(
			$wpdb->prepare( "SHOW COLUMNS FROM `{$table_name}` LIKE %s", 'last_output_id' )
		);
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

		if ( ! empty( $column_exists ) ) {
			if ( WP_DEBUG ) {
				error_log( 'Wisewand: Migration 9 already completed (last_output_id column exists)' );
			}
			return true;
		}

		// Migration needed
		if ( WP_DEBUG ) {
			error_log( 'Wisewand: Starting Migration 9 - Adding last_output_id column for API optimization' );
		}

		// Add last_output_id column after has_cover_image
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from $wpdb->prefix is safe
		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
		$result = $wpdb->query(
			"ALTER TABLE `{$table_name}`
			ADD COLUMN last_output_id varchar(100) DEFAULT NULL COMMENT 'Cached output ID from discovery (eliminates redundant API calls)'
			AFTER has_cover_image"
		);
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

		if ( WP_DEBUG ) {
			if ( $wpdb->last_error ) {
				error_log( 'Wisewand: Migration 9 error: ' . $wpdb->last_error );
				return false;
			} else {
				error_log( 'Wisewand: Migration 9 completed successfully - last_output_id column added' );
				error_log( 'Wisewand: API optimization active - discoverarticle imports will now use cached output IDs' );
				update_option( 'wisewand_migration_9_completed', current_time( 'mysql' ) );
			}
		}

		return $result !== false;
	}

	/**
	 * Run Migration 10: Update UNIQUE constraint to include language_code
	 *
	 * Updates the UNIQUE KEY from (keyword) to (keyword, language_code, location_code)
	 * This allows same keyword text in different languages without conflicts.
	 *
	 * Migration only runs if table already exists (upgrade scenario).
	 * For new installations, the base schema already has the correct constraint.
	 *
	 * @since 1.3.0
	 * @global wpdb $wpdb WordPress database abstraction object.
	 * @return bool True if migration ran successfully or already completed.
	 */
	public static function run_migration_10() {
		global $wpdb;

		$table_name = $wpdb->prefix . 'wisewand_saved_keywords';

		// Check if table exists (upgrade scenario only)
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from $wpdb->prefix is safe
		$table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) );

		if ( ! $table_exists ) {
			// Table doesn't exist yet - skip migration (fresh install)
			return true;
		}

		// Check if new UNIQUE constraint already exists
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from $wpdb->prefix is safe
		$check_query = $wpdb->prepare(
			"SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS
			WHERE TABLE_SCHEMA = %s
			AND TABLE_NAME = %s
			AND INDEX_NAME = 'keyword_language'",
			DB_NAME,
			$table_name
		);
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query already prepared above
		$new_constraint_exists = (int) $wpdb->get_var( $check_query );
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

		if ( $new_constraint_exists > 0 ) {
			if ( WP_DEBUG ) {
				error_log( 'Wisewand: Migration 10 already completed (keyword_language constraint exists)' );
			}
			return true;
		}

		// Migration needed
		if ( WP_DEBUG ) {
			error_log( 'Wisewand: Starting Migration 10 - Updating UNIQUE constraint to include language_code' );
		}

		// Step 1: Check if old constraint exists
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from $wpdb->prefix is safe
		$old_constraint_check = $wpdb->prepare(
			"SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS
			WHERE TABLE_SCHEMA = %s
			AND TABLE_NAME = %s
			AND INDEX_NAME = 'keyword'
			AND NON_UNIQUE = 0",
			DB_NAME,
			$table_name
		);
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query already prepared above
		$old_constraint_exists = (int) $wpdb->get_var( $old_constraint_check );

		// Step 2: Drop old UNIQUE constraint if it exists
		if ( $old_constraint_exists > 0 ) {
			// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
			$wpdb->query( "ALTER TABLE `{$table_name}` DROP INDEX keyword" );
			// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

			if ( WP_DEBUG ) {
				if ( $wpdb->last_error ) {
					error_log( 'Wisewand: Error dropping old constraint: ' . $wpdb->last_error );
				} else {
					error_log( 'Wisewand: Dropped old UNIQUE constraint on keyword' );
				}
			}
		}

		// Step 3: Add new UNIQUE constraint (keyword, language_code, location_code)
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from $wpdb->prefix is safe
		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
		$result = $wpdb->query(
			"ALTER TABLE `{$table_name}`
			ADD UNIQUE KEY keyword_language (keyword, language_code, location_code)"
		);
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

		if ( WP_DEBUG ) {
			if ( $wpdb->last_error ) {
				error_log( 'Wisewand: Migration 10 error: ' . $wpdb->last_error );
				return false;
			} else {
				error_log( 'Wisewand: Migration 10 completed successfully - UNIQUE constraint now includes language_code' );
				error_log( 'Wisewand: Multilingual keywords are now supported (e.g., "cat" in EN and FR)' );
				update_option( 'wisewand_migration_10_completed', current_time( 'mysql' ) );
			}
		}

		return $result !== false;
	}

	/**
	 * Run Migration 11: Add publish_mode column to wisewand_rss_feeds
	 *
	 * Adds publish_mode column for controlling publication behavior per feed.
	 * Supports three modes: schedule (future), publish (immediate), draft (review).
	 *
	 * Migration only runs if table already exists (upgrade scenario).
	 * For new installations, the base schema already has the column.
	 *
	 * @since 1.4.0
	 * @global wpdb $wpdb WordPress database abstraction object.
	 * @return bool True if migration ran successfully or already completed.
	 */
	public static function run_migration_11() {
		// Load migration class.
		if ( ! class_exists( 'Wisewand\\Database\\Migrations\\Migration_011_Add_Publish_Mode' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Database/migrations/Migration_011_Add_Publish_Mode.php';
		}

		return \Wisewand\Database\Migrations\Migration_011_Add_Publish_Mode::run();
	}

	/**
	 * Run Migration 12: Add embedding column to saved_keywords
	 *
	 * Adds embedding column for AI Semantic Deduplication.
	 * Stores 256-dimensional vectors as binary data (mediumblob).
	 *
	 * @since 1.6.16
	 * @global wpdb $wpdb WordPress database abstraction object.
	 * @return bool True if migration ran successfully or already completed.
	 */
	public static function run_migration_12() {
		global $wpdb;

		$table_name = $wpdb->prefix . 'wisewand_saved_keywords';

		// Check if table exists (upgrade scenario only)
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		$table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) );

		if ( ! $table_exists ) {
			return true;
		}

		// Check if embedding column already exists
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter
		$column_exists = $wpdb->get_results(
			$wpdb->prepare( "SHOW COLUMNS FROM `{$table_name}` LIKE %s", 'embedding' )
		);
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

		if ( ! empty( $column_exists ) ) {
			if ( WP_DEBUG ) {
				error_log( 'Wisewand: Migration 12 already completed (embedding column exists)' );
			}
			return true;
		}

		// Migration needed
		if ( WP_DEBUG ) {
			error_log( 'Wisewand: Starting Migration 12 - Adding embedding column for AI Semantic Deduplication' );
		}

		// Add embedding column
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter
		$result = $wpdb->query(
			"ALTER TABLE `{$table_name}`
			ADD COLUMN embedding mediumblob DEFAULT NULL COMMENT 'Binary storage of 256-float vector'"
		);
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

		if ( WP_DEBUG ) {
			if ( $wpdb->last_error ) {
				error_log( 'Wisewand: Migration 12 error: ' . $wpdb->last_error );
				return false;
			} else {
				error_log( 'Wisewand: Migration 12 completed successfully - embedding column added' );
				update_option( 'wisewand_migration_12_completed', current_time( 'mysql' ) );
			}
		}

		return $result !== false;
	}

	/**
	 * Run Migration 13: Retroactive External ID Fix
	 *
	 * Syncs _wisewand_external_id meta from calendar and RSS tables to WordPress posts.
	 *
	 * @since 1.6.36
	 * @return bool True if migration ran successfully or already completed.
	 */
	public static function run_migration_13() {
		if ( ! class_exists( 'Wisewand\\Database\\Migrations\\Migration_013_Retroactive_External_Id_Fix' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Database/migrations/Migration_013_Retroactive_External_Id_Fix.php';
		}

		$result = \Wisewand\Database\Migrations\Migration_013_Retroactive_External_Id_Fix::run();

		if ( $result ) {
			update_option( 'wisewand_migration_13_completed', current_time( 'mysql' ) );
		}

		return $result;
	}

	/**
	 * Run Migration 14: Add Global Settings Flag
	 *
	 * @since 1.6.37
	 * @return bool
	 */
	public static function run_migration_14() {
		if ( ! class_exists( 'Wisewand\\Database\\Migrations\\Migration_014_Add_Global_Settings_Flag' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Database/migrations/Migration_014_Add_Global_Settings_Flag.php';
		}

		$result = \Wisewand\Database\Migrations\Migration_014_Add_Global_Settings_Flag::run();

		if ( $result ) {
			update_option( 'wisewand_migration_14_completed', current_time( 'mysql' ) );
		}

		return $result;
	}

	/**
	 * Run Migration 15: Restore Custom Settings
	 *
	 * @since 1.7.2
	 * @return bool
	 */
	public static function run_migration_15() {
		if ( ! class_exists( 'Wisewand\\Database\\Migrations\\Migration_015_Restore_Custom_Settings' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Database/migrations/Migration_015_Restore_Custom_Settings.php';
		}

		$result = \Wisewand\Database\Migrations\Migration_015_Restore_Custom_Settings::run();

		if ( $result ) {
			update_option( 'wisewand_migration_15_completed', current_time( 'mysql' ) );
		}

		return $result;
	}

	/**
	 * Run Migration 16: Add RSS Schedule Fields
	 *
	 * Adds schedule_mode and schedule_time columns to RSS feeds table
	 * for global and per-feed scheduling control.
	 *
	 * @since 2.1.3
	 * @return bool True if migration ran successfully or already completed.
	 */
	public static function run_migration_16() {
		if ( ! class_exists( 'Wisewand\\Database\\Migrations\\Migration_016_Add_RSS_Schedule_Fields' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Database/migrations/Migration_016_Add_RSS_Schedule_Fields.php';
		}

		$result = \Wisewand\Database\Migrations\Migration_016_Add_RSS_Schedule_Fields::run();

		if ( $result ) {
			update_option( 'wisewand_migration_16_completed', current_time( 'mysql' ) );
		}

		return $result;
	}

	/**
	 * Run migration 17
	 *
	 * Consolidates duplicate publish mode settings into unified option.
	 *
	 * @since 2.1.4
	 * @return bool True if migration ran successfully or already completed.
	 */
	public static function run_migration_17() {
		if ( ! class_exists( 'Wisewand\\Database\\Migrations\\Migration_017_Consolidate_Publish_Mode_Options' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Database/migrations/Migration_017_Consolidate_Publish_Mode_Options.php';
		}

		$migration = new \Wisewand\Database\Migrations\Migration_017_Consolidate_Publish_Mode_Options();
		$result    = $migration->up();

		if ( $result && ! is_wp_error( $result ) ) {
			update_option( 'wisewand_migration_17_completed', current_time( 'mysql' ) );
			return true;
		}

		if ( is_wp_error( $result ) && defined( 'WP_DEBUG' ) && WP_DEBUG ) {
			error_log( 'Wisewand Migration 017 failed: ' . $result->get_error_message() );
		}

		return false;
	}

	/**
	 * Run Migration 18: Add Inheritance Flags
	 *
	 * @since 2.1.5
	 * @return bool True if migration ran successfully or already completed.
	 */
	public static function run_migration_18() {
		if ( ! class_exists( 'Wisewand\\Database\\Migrations\\Migration_018_Add_Inheritance_Flags' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Database/migrations/Migration_018_Add_Inheritance_Flags.php';
		}

		$result = \Wisewand\Database\Migrations\Migration_018_Add_Inheritance_Flags::run();

		if ( $result ) {
			update_option( 'wisewand_migration_18_completed', current_time( 'mysql' ) );
		}

		return $result;
	}

	/**
	 * Run Migration 19: Migrate Legacy Data
	 *
	 * @since 2.1.5
	 * @return bool True if migration ran successfully or already completed.
	 */
	public static function run_migration_19() {
		if ( ! class_exists( 'Wisewand\\Database\\Migrations\\Migration_019_Migrate_Legacy_Data' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Database/migrations/Migration_019_Migrate_Legacy_Data.php';
		}

		return \Wisewand\Database\Migrations\Migration_019_Migrate_Legacy_Data::run();
	}

	/**
	 * Run Migration 20: Remove Redundancies
	 *
	 * @since 2.1.5
	 * @return bool True if migration ran successfully or already completed.
	 */
	public static function run_migration_20() {
		if ( ! class_exists( 'Wisewand\\Database\\Migrations\\Migration_020_Remove_Redundancies' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Database/migrations/Migration_020_Remove_Redundancies.php';
		}

		$result = \Wisewand\Database\Migrations\Migration_020_Remove_Redundancies::run();

		if ( $result ) {
			update_option( 'wisewand_migration_20_completed', current_time( 'mysql' ) );
		}

		return $result;
	}

	/**
	 * Run all data migrations
	 *
	 * Executes all data transformation migrations in order.
	 * Safe to run multiple times - migrations check if already completed.
	 *
	 * @since 1.0.2
	 * @return bool True if all migrations completed successfully.
	 */

	/**
	 * Run Migration 21: Add Import Frequency Inheritance
	 *
	 * @since 2.2.1
	 * @return bool True if migration ran successfully or already completed.
	 */
	public static function run_migration_21() {
		if ( ! class_exists( 'Wisewand\\Database\\Migrations\\Migration_021_Add_Import_Frequency_Inheritance' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Database/migrations/Migration_021_Add_Import_Frequency_Inheritance.php';
		}

		$result = \Wisewand\Database\Migrations\Migration_021_Add_Import_Frequency_Inheritance::run();

		if ( $result ) {
			update_option( 'wisewand_migration_21_completed', current_time( 'mysql' ) );
		}

		return $result;
	}

	/**
	 * Run Migration 22: Fix Internal Links Status
	 *
	 * @since 2.4.2
	 * @return bool True if migration ran successfully or already completed.
	 */
	public static function run_migration_22() {
		if ( ! class_exists( 'Wisewand\\Database\\Migrations\\Migration_022_Fix_Internal_Links_Status' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Database/migrations/Migration_022_Fix_Internal_Links_Status.php';
		}

		$result = \Wisewand\Database\Migrations\Migration_022_Fix_Internal_Links_Status::up( $GLOBALS['wpdb'] );

		if ( $result ) {
			update_option( 'wisewand_migration_22_completed', current_time( 'mysql' ) );
		}

		return $result;
	}

	/**
	 * Run Migration 23: Add RSS Article ID to Calendar
	 *
	 * Adds rss_article_id column and index to editorial calendar table.
	 *
	 * @since 2.5.0
	 * @return bool True if migration ran successfully or already completed.
	 */
	public static function run_migration_23() {
		if ( ! class_exists( 'Wisewand\\Database\\Migrations\\Migration_023_Add_RSS_Article_ID_To_Calendar' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Database/migrations/Migration_023_Add_RSS_Article_ID_To_Calendar.php';
		}

		$result = \Wisewand\Database\Migrations\Migration_023_Add_RSS_Article_ID_To_Calendar::up( $GLOBALS['wpdb'] );

		if ( $result ) {
			update_option( 'wisewand_migration_23_completed', current_time( 'mysql' ) );
		}

		return $result;
	}

	/**
	 * Run Migration 24: Add 'imported_scheduled' to wordpress_status ENUM
	 *
	 * Adds 'imported_scheduled' status value to editorial calendar wordpress_status ENUM
	 * and fixes existing mismatched entries.
	 *
	 * @since 2.7.1
	 * @return bool True if migration ran successfully or already completed.
	 */
	public static function run_migration_24() {
		if ( ! class_exists( 'Wisewand\\Database\\Migrations\\Migration_024_Add_Imported_Scheduled_Status' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Database/migrations/Migration_024_Add_Imported_Scheduled_Status.php';
		}

		$result = \Wisewand\Database\Migrations\Migration_024_Add_Imported_Scheduled_Status::up( $GLOBALS['wpdb'] );

		if ( $result ) {
			update_option( 'wisewand_migration_24_completed', current_time( 'mysql' ) );
		}

		return $result;
	}

	/**
	 * Run Migration 25: Remove Same-Site Canonical Overrides
	 *
	 * Cleans up incorrect canonical_url meta set by WiseWand on same-site posts
	 * and invalidates sitemap caches.
	 *
	 * @since 2.8.0
	 * @return bool True if migration ran successfully or already completed.
	 */
	public static function run_migration_25() {
		if ( ! class_exists( 'Wisewand\\Database\\Migrations\\Migration_025_Remove_Same_Site_Canonicals' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Database/migrations/Migration_025_Remove_Same_Site_Canonicals.php';
		}

		$result = \Wisewand\Database\Migrations\Migration_025_Remove_Same_Site_Canonicals::up( $GLOBALS['wpdb'] );

		if ( $result ) {
			update_option( 'wisewand_migration_25_completed', current_time( 'mysql' ) );
		}

		return $result;
	}

	/**
	 * Run Migration 26: Add RSS Calendar Columns
	 *
	 * Adds rss_feed_id, source_metadata columns and 'rss' ENUM value
	 * to the editorial calendar table.
	 *
	 * @since 2.8.2
	 * @return bool True if migration ran successfully or already completed.
	 */
	public static function run_migration_26() {
		if ( ! class_exists( 'Wisewand\\Database\\Migrations\\Migration_026_Add_RSS_Calendar_Columns' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Database/migrations/Migration_026_Add_RSS_Calendar_Columns.php';
		}

		$result = \Wisewand\Database\Migrations\Migration_026_Add_RSS_Calendar_Columns::up( $GLOBALS['wpdb'] );

		if ( $result ) {
			update_option( 'wisewand_migration_26_completed', current_time( 'mysql' ) );
		}

		return $result;
	}

	public static function run_all_migrations() {
		$migration_5_success  = self::run_migration_5();
		$migration_7_success  = self::run_migration_7();
		$migration_9_success  = self::run_migration_9();
		$migration_10_success = self::run_migration_10();
		$migration_11_success = self::run_migration_11();
		$migration_12_success = self::run_migration_12();
		$migration_13_success = self::run_migration_13();
		$migration_14_success = self::run_migration_14();
		$migration_15_success = self::run_migration_15();
		$migration_16_success = self::run_migration_16();
		$migration_17_success = self::run_migration_17();
		$migration_18_success = self::run_migration_18();
		$migration_19_success = self::run_migration_19();
		$migration_20_success = self::run_migration_20();
		$migration_21_success = self::run_migration_21();
		$migration_22_success = self::run_migration_22();
		$migration_23_success = self::run_migration_23();
		$migration_24_success = self::run_migration_24();
		$migration_25_success = self::run_migration_25();
		$migration_26_success = self::run_migration_26();

		return $migration_5_success && $migration_7_success && $migration_9_success && $migration_10_success && $migration_11_success && $migration_12_success && $migration_13_success && $migration_14_success && $migration_15_success && $migration_16_success && $migration_17_success && $migration_18_success && $migration_19_success && $migration_20_success && $migration_21_success && $migration_22_success && $migration_23_success && $migration_24_success && $migration_25_success && $migration_26_success;
	}

	/**
	 * Check if migrations are needed
	 *
	 * @since 1.0.2
	 * @return bool True if any migration needs to run.
	 */
	public static function needs_migration() {
		// Check if migration tracking options exist
		$migration_5_done  = get_option( 'wisewand_migration_5_completed' );
		$migration_7_done  = get_option( 'wisewand_migration_7_completed' );
		$migration_9_done  = get_option( 'wisewand_migration_9_completed' );
		$migration_10_done = get_option( 'wisewand_migration_10_completed' );
		$migration_11_done = get_option( 'wisewand_migration_11_completed' );
		$migration_12_done = get_option( 'wisewand_migration_12_completed' );
		$migration_13_done = get_option( 'wisewand_migration_13_completed' );
		$migration_14_done = get_option( 'wisewand_migration_14_completed' );
		$migration_15_done = get_option( 'wisewand_migration_15_completed' );
		$migration_16_done = get_option( 'wisewand_migration_16_completed' );
		$migration_17_done = get_option( 'wisewand_migration_17_completed' );
		$migration_18_done = get_option( 'wisewand_migration_18_completed' );
		$migration_19_done = get_option( 'wisewand_migration_19_completed' );
		$migration_20_done = get_option( 'wisewand_migration_20_completed' );
		$migration_21_done = get_option( 'wisewand_migration_21_completed' );
		$migration_22_done = get_option( 'wisewand_migration_22_completed' );
		$migration_23_done = get_option( 'wisewand_migration_23_completed' );
		$migration_24_done = get_option( 'wisewand_migration_24_completed' );
		$migration_25_done = get_option( 'wisewand_migration_25_completed' );
		$migration_26_done = get_option( 'wisewand_migration_26_completed' );

		// If any migration hasn't completed, we need to run migrations
		return ! $migration_5_done || ! $migration_7_done || ! $migration_9_done || ! $migration_10_done || ! $migration_11_done || ! $migration_12_done || ! $migration_13_done || ! $migration_14_done || ! $migration_15_done || ! $migration_16_done || ! $migration_17_done || ! $migration_18_done || ! $migration_19_done || ! $migration_20_done || ! $migration_21_done || ! $migration_22_done || ! $migration_23_done || ! $migration_24_done || ! $migration_25_done || ! $migration_26_done;
	}

	/**
	 * Get migration status
	 *
	 * Returns array with status of each migration.
	 *
	 * @since 1.0.2
	 * @return array Migration status for each migration.
	 */
	public static function get_migration_status() {
		return array(
			'migration_5'  => array(
				'completed'    => (bool) get_option( 'wisewand_migration_5_completed' ),
				'completed_at' => get_option( 'wisewand_migration_5_completed' ),
				'description'  => 'Add imported status sub-statuses (imported_draft, imported_published)',
			),
			'migration_7'  => array(
				'completed'    => (bool) get_option( 'wisewand_migration_7_completed' ),
				'completed_at' => get_option( 'wisewand_migration_7_completed' ),
				'description'  => 'Add sync_status column and migrate from enabled_for_site',
			),
			'migration_9'  => array(
				'completed'    => (bool) get_option( 'wisewand_migration_9_completed' ),
				'completed_at' => get_option( 'wisewand_migration_9_completed' ),
				'description'  => 'Change auto_import_enabled default from 0 to 1 (WordPress.org ready)',
			),
			'migration_10' => array(
				'completed'    => (bool) get_option( 'wisewand_migration_10_completed' ),
				'completed_at' => get_option( 'wisewand_migration_10_completed' ),
				'description'  => 'Update UNIQUE constraint to include language_code for multilingual support',
			),
			'migration_11' => array(
				'completed'    => (bool) get_option( 'wisewand_migration_11_completed' ),
				'completed_at' => get_option( 'wisewand_migration_11_completed' ),
				'description'  => 'Add publish_mode column for RSS feeds publication control',
			),
			'migration_12' => array(
				'completed'    => (bool) get_option( 'wisewand_migration_12_completed' ),
				'completed_at' => get_option( 'wisewand_migration_12_completed' ),
				'description'  => 'Add embedding column for AI Semantic Deduplication',
			),
			'migration_13' => array(
				'completed'    => (bool) get_option( 'wisewand_migration_13_completed' ),
				'completed_at' => get_option( 'wisewand_migration_13_completed' ),
				'description'  => 'Retroactive fix for imported post detection (syncs external_id)',
			),
			'migration_14' => array(
				'completed'    => (bool) get_option( 'wisewand_migration_14_completed' ),
				'completed_at' => get_option( 'wisewand_migration_14_completed' ),
				'description'  => 'Add Global Settings Flag (use_global_settings) to RSS feeds',
			),
			'migration_16' => array(
				'completed'    => (bool) get_option( 'wisewand_migration_16_completed' ),
				'completed_at' => get_option( 'wisewand_migration_16_completed' ),
				'description'  => 'Add RSS scheduling fields (schedule_mode, schedule_time) for publish time control',
			),
			'migration_24' => array(
				'completed'    => (bool) get_option( 'wisewand_migration_24_completed' ),
				'completed_at' => get_option( 'wisewand_migration_24_completed' ),
				'description'  => 'Add imported_scheduled status to ENUM and fix calendar display',
			),
			'migration_25' => array(
				'completed'    => (bool) get_option( 'wisewand_migration_25_completed' ),
				'completed_at' => get_option( 'wisewand_migration_25_completed' ),
				'description'  => 'Remove same-site canonical URL overrides and invalidate sitemap caches',
			),
			'migration_26' => array(
				'completed'    => (bool) get_option( 'wisewand_migration_26_completed' ),
				'completed_at' => get_option( 'wisewand_migration_26_completed' ),
				'description'  => 'Add rss_feed_id, source_metadata columns and rss ENUM value to editorial calendar',
			),
		);
	}
}
