<?php
/**
 * This file is part of the Wisewand WordPress plugin.
 *
 * Migration Executor - Executes brief config migration with transaction safety.
 *
 * @package Wisewand
 * @since 2.8.0
 * @license GPL-2.0-or-later
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */
namespace Wisewand\Migration;

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

use Wisewand\Utils\Options;
use Wisewand\Logging\Logger;

// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_error_log, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- error_log() only used in WP_DEBUG; direct queries required for migration operations

/**
 * Migration Executor
 *
 * Executes brief config migration with:
 * - Transaction-wrapped operations
 * - Automatic rollback on failure
 * - Batch processing for large datasets
 * - Progress tracking and resumption
 *
 * @package Wisewand
 * @since 2.8.0
 */
class Migration_Executor {

	/**
	 * Options manager
	 *
	 * @since 2.8.0
	 * @var Options
	 */
	private $options;

	/**
	 * Logger instance
	 *
	 * @since 2.8.0
	 * @var Logger
	 */
	private $logger;

	/**
	 * WordPress database instance
	 *
	 * @since 2.8.0
	 * @var \wpdb
	 */
	private $wpdb;

	/**
	 * Migration validator
	 *
	 * @since 2.8.0
	 * @var Migration_Validator
	 */
	private $validator;

	/**
	 * Batch size for processing calendar entries
	 *
	 * @since 2.8.0
	 * @var int
	 */
	const BATCH_SIZE = 50;

	/**
	 * Constructor
	 *
	 * @since 2.8.0
	 * @param Options             $options   Options manager.
	 * @param Logger              $logger    Logger instance.
	 * @param Migration_Validator $validator Validator instance.
	 */
	public function __construct( Options $options, Logger $logger, Migration_Validator $validator ) {
		global $wpdb;

		$this->options   = $options;
		$this->logger    = $logger;
		$this->wpdb      = $wpdb;
		$this->validator = $validator;
	}

	/**
	 * Execute complete migration
	 *
	 * Performs migration in transaction-wrapped steps:
	 * 1. Pre-validation
	 * 2. Backup current state
	 * 3. Convert calendar entries (batched)
	 * 4. Delete legacy options
	 * 5. Mark migration complete
	 *
	 * @since 2.8.0
	 * @param bool $dry_run If true, simulates migration without changes.
	 * @return true|WP_Error True on success, WP_Error on failure.
	 */
	public function execute_migration( $dry_run = false ) {
		// Step 1: Check if already migrated
		if ( ! $this->validator->is_migration_needed() ) {
			return new \WP_Error(
				'already_migrated',
				__( 'Migration already completed', 'wisewand-ai-content-writer-seo-content-generator' )
			);
		}

		// Step 3: Backup current state (before any changes)
		if ( ! $dry_run ) {
			$backup_result = $this->backup_current_state();
			if ( is_wp_error( $backup_result ) ) {
				return $backup_result;
			}
		}

		// Step 4: Execute migration in transaction
		try {
			if ( $dry_run ) {
				$this->logger->info( 'Starting DRY RUN migration (no changes will be made)' );
				$result = $this->simulate_migration();
			} else {
				$this->logger->info( 'Starting LIVE migration' );
				$result = $this->execute_migration_transaction();
			}

			if ( is_wp_error( $result ) ) {
				return $result;
			}

			// Step 5: Mark migration complete (only if not dry run)
			if ( ! $dry_run ) {
				update_option( 'wisewand_brief_config_migration_completed', true );
				update_option( 'wisewand_brief_config_migration_date', current_time( 'mysql' ) );

				$this->logger->info(
					'Migration completed successfully',
					array(
						'entries_converted' => $result['entries_converted'],
						'options_deleted'   => $result['options_deleted'],
						'duration_seconds'  => $result['duration_seconds'],
					)
				);
			}

			return true;

		} catch ( \Exception $e ) {
			$this->logger->error(
				'Migration exception caught',
				array(
					'exception' => $e->getMessage(),
					'trace'     => $e->getTraceAsString(),
				)
			);

			return new \WP_Error(
				'migration_exception',
				sprintf(
					/* translators: %s: Exception message */
					__( 'Migration failed: %s', 'wisewand-ai-content-writer-seo-content-generator' ),
					$e->getMessage()
				)
			);
		}
	}

	/**
	 * Execute migration in database transaction
	 *
	 * @since 2.8.0
	 * @return array|WP_Error Migration results or error.
	 */
	private function execute_migration_transaction() {
		$start_time = time();

		// Start transaction
		$this->wpdb->query( 'START TRANSACTION' );

		try {
			// Step 1: Convert calendar entries (batched)
			$conversion_result = $this->convert_calendar_entries_batched();

			if ( is_wp_error( $conversion_result ) ) {
				throw new \Exception( $conversion_result->get_error_message() );
			}

			// Step 2: Delete legacy options
			$deletion_result = $this->delete_legacy_options();

			if ( is_wp_error( $deletion_result ) ) {
				throw new \Exception( $deletion_result->get_error_message() );
			}

			// Commit transaction
			$this->wpdb->query( 'COMMIT' );

			$duration = time() - $start_time;

			return array(
				'entries_converted' => $conversion_result['total_converted'],
				'options_deleted'   => $deletion_result['total_deleted'],
				'duration_seconds'  => $duration,
			);

		} catch ( \Exception $e ) {
			// Rollback on any failure
			$this->wpdb->query( 'ROLLBACK' );

			$this->logger->error(
				'Migration rolled back due to error',
				array(
					'error'    => $e->getMessage(),
					'duration' => time() - $start_time,
				)
			);

			return new \WP_Error(
				'migration_failed',
				$e->getMessage()
			);
		}
	}

	/**
	 * Convert calendar entries to use brief config (batched processing)
	 *
	 * Processes entries in batches to handle large datasets.
	 * Per user requirement Q3 (Option B): Convert ALL entries automatically.
	 *
	 * @since 2.8.0
	 * @return array|WP_Error Conversion results.
	 */
	private function convert_calendar_entries_batched() {
		$calendar_table = $this->wpdb->prefix . 'wisewand_editorial_calendar';

		// Get total count
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$total_entries = $this->wpdb->get_var(
			$wpdb->prepare(
				'SELECT COUNT(*) FROM %i',
				$calendar_table
			)
		);

		if ( ! $total_entries || 0 === $total_entries ) {
			return array(
				'total_converted' => 0,
				'batches'         => 0,
			);
		}

		$total_converted = 0;
		$batch_number    = 0;
		$offset          = 0;

		while ( $offset < $total_entries ) {
			$batch_number++;

			// Fetch batch
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$entries = $this->wpdb->get_results(
				$this->wpdb->prepare(
					'SELECT id, wisewand_payload, keyword_source FROM %i ORDER BY id ASC LIMIT %d OFFSET %d',
					$calendar_table,
					self::BATCH_SIZE,
					$offset
				),
				ARRAY_A
			);

			if ( empty( $entries ) ) {
				break; // No more entries
			}

			// Convert each entry in batch
			foreach ( $entries as $entry ) {
				$converted = $this->convert_single_entry( $entry );

				if ( ! is_wp_error( $converted ) ) {
					$total_converted++;
				} else {
					// Log error but continue (don't fail entire migration)
					$this->logger->warning(
						'Failed to convert calendar entry',
						array(
							'entry_id' => $entry['id'],
							'error'    => $converted->get_error_message(),
						)
					);
				}
			}

			$offset += self::BATCH_SIZE;

			// Update progress
			update_option(
				'wisewand_migration_progress',
				array(
					'processed' => $offset,
					'total'     => $total_entries,
					'percent'   => round( ( $offset / $total_entries ) * 100 ),
					'batch'     => $batch_number,
				),
				false
			);

			$this->logger->info(
				'Migration batch completed',
				array(
					'batch'          => $batch_number,
					'entries'        => count( $entries ),
					'total_progress' => $offset . '/' . $total_entries,
				)
			);
		}

		return array(
			'total_converted' => $total_converted,
			'batches'         => $batch_number,
		);
	}

	/**
	 * Convert single calendar entry payload to brief config format
	 *
	 * Transformation per user requirement Q3 (Option B):
	 * - Add apply_project_brief_config: true
	 * - Remove ALL old default settings (26 options)
	 * - Keep ONLY: search_intention_answer, additional_information,
	 *   avoid_information, target_keyword, keep_title
	 *
	 * @since 2.8.0
	 * @param array $entry Calendar entry database row.
	 * @return true|WP_Error True on success, WP_Error on failure.
	 */
	private function convert_single_entry( $entry ) {
		// Skip RSS entries (they use empty payload intentionally)
		if ( 'rss' === $entry['keyword_source'] ) {
			return true; // No conversion needed
		}

		// Decode existing payload
		$payload = json_decode( $entry['wisewand_payload'], true );

		if ( null === $payload ) {
			// Invalid JSON - use empty array
			$payload = array();
		}

		// Fields to KEEP (AI Instructions + WordPress settings handled separately)
		$keep_fields = array(
			'search_intention_answer',
			'additional_information',
			'avoid_information',
			'target_keyword',
			'keep_title',
			'subject', // Article subject/title
			'lang',    // Language
			'country', // Country
			'type',    // Content type (blog, news, etc.)
			'length',  // Word count
			'project_id', // Project ID
		);

		// Create new minimal payload
		$new_payload = array(
			'apply_project_brief_config' => true,
		);

		// Preserve allowed fields from old payload
		foreach ( $keep_fields as $field ) {
			if ( isset( $payload[ $field ] ) ) {
				$new_payload[ $field ] = $payload[ $field ];
			}
		}

		// Ensure project_id is set (fallback to global setting)
		if ( empty( $new_payload['project_id'] ) ) {
			$new_payload['project_id'] = $this->options->get( 'wisewand_default_project_id', '' );
		}

		// Encode new payload
		$new_payload_json = wp_json_encode( $new_payload );

		if ( false === $new_payload_json ) {
			return new \WP_Error(
				'json_encode_failed',
				sprintf(
					/* translators: %d: Entry ID */
					__( 'Failed to encode payload for entry %d', 'wisewand-ai-content-writer-seo-content-generator' ),
					$entry['id']
				)
			);
		}

		// Update database
		$calendar_table = $this->wpdb->prefix . 'wisewand_editorial_calendar';

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$updated = $this->wpdb->update(
			$calendar_table,
			array( 'wisewand_payload' => $new_payload_json ),
			array( 'id' => $entry['id'] ),
			array( '%s' ),
			array( '%d' )
		);

		if ( false === $updated ) {
			return new \WP_Error(
				'database_update_failed',
				sprintf(
					/* translators: 1: Entry ID, 2: Database error */
					__( 'Failed to update entry %1$d: %2$s', 'wisewand-ai-content-writer-seo-content-generator' ),
					$entry['id'],
					$this->wpdb->last_error
				)
			);
		}

		return true;
	}

	/**
	 * Delete legacy content generation options
	 *
	 * Removes all 26 plugin-managed default settings from wp_options.
	 *
	 * @since 2.8.0
	 * @return array|WP_Error Deletion results.
	 */
	private function delete_legacy_options() {
		$deleted = 0;

		foreach ( Migration_Validator::LEGACY_OPTIONS as $option_name ) {
			$result = delete_option( $option_name );

			if ( $result ) {
				$deleted++;
			}
		}

		$this->logger->info(
			'Legacy options deleted',
			array(
				'deleted'       => $deleted,
				'total_options' => count( Migration_Validator::LEGACY_OPTIONS ),
			)
		);

		return array(
			'total_deleted' => $deleted,
		);
	}

	/**
	 * Backup current state before migration
	 *
	 * Creates safety backup of:
	 * - All legacy options (for potential recovery)
	 * - Migration timestamp
	 *
	 * @since 2.8.0
	 * @return true|WP_Error True on success, WP_Error on failure.
	 */
	private function backup_current_state() {
		$backup = array(
			'options'   => array(),
			'timestamp' => current_time( 'mysql' ),
			'version'   => WISEWAND_PLUGIN_VERSION,
		);

		// Backup all legacy options
		foreach ( Migration_Validator::LEGACY_OPTIONS as $option_name ) {
			$backup['options'][ $option_name ] = get_option( $option_name );
		}

		// Store backup
		$updated = update_option( 'wisewand_pre_migration_backup', $backup, false );

		if ( ! $updated ) {
			return new \WP_Error(
				'backup_failed',
				__( 'Failed to create migration backup', 'wisewand-ai-content-writer-seo-content-generator' )
			);
		}

		$this->logger->info(
			'Migration backup created',
			array(
				'options_backed_up' => count( $backup['options'] ),
				'timestamp'         => $backup['timestamp'],
			)
		);

		return true;
	}

	/**
	 * Simulate migration (dry run mode)
	 *
	 * @since 2.8.0
	 * @return array|WP_Error Simulation results.
	 */
	private function simulate_migration() {
		$calendar_table = $this->wpdb->prefix . 'wisewand_editorial_calendar';

		// Count entries to convert (excluding RSS)
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$entries_to_convert = $this->wpdb->get_var(
			$this->wpdb->prepare(
				"SELECT COUNT(*) FROM %i WHERE keyword_source != 'rss'",
				$calendar_table
			)
		);

		// Count legacy options to delete
		$options_to_delete = 0;
		foreach ( Migration_Validator::LEGACY_OPTIONS as $option_name ) {
			if ( false !== get_option( $option_name ) ) {
				$options_to_delete++;
			}
		}

		$this->logger->info(
			'Migration dry run completed',
			array(
				'entries_to_convert' => $entries_to_convert,
				'options_to_delete'  => $options_to_delete,
			)
		);

		return array(
			'entries_converted' => $entries_to_convert,
			'options_deleted'   => $options_to_delete,
			'duration_seconds'  => 0, // Dry run is instant
		);
	}

	/**
	 * Get migration progress
	 *
	 * @since 2.8.0
	 * @return array|false Progress data or false if not in progress.
	 */
	public function get_migration_progress() {
		return get_option( 'wisewand_migration_progress', false );
	}

	/**
	 * Clear migration progress tracking
	 *
	 * @since 2.8.0
	 * @return void
	 */
	public function clear_migration_progress() {
		delete_option( 'wisewand_migration_progress' );
	}
}
