<?php

namespace SugarCalendarBookings\Migration;

use SugarCalendarBookings\Plugin;
use SugarCalendarBookings\Support\Ajax\AbstractController;
use Exception;

/**
 * Migration Controller.
 *
 * Handles AJAX requests for batch migration from external booking plugins.
 *
 * @since 1.3.0
 */
class MigrationController extends AbstractController {

	/**
	 * Migration service.
	 *
	 * @since 1.3.0
	 *
	 * @var MigrationService
	 */
	private MigrationService $service;

	/**
	 * Migration registry.
	 *
	 * @since 1.3.0
	 *
	 * @var MigrationRegistry
	 */
	private MigrationRegistry $registry;

	/**
	 * Constructor.
	 *
	 * @since 1.3.0
	 */
	public function __construct() {

		$this->service  = Plugin::get( MigrationService::class );
		$this->registry = Plugin::get( MigrationRegistry::class );
	}

	/**
	 * Start migration - initialize state and return entity counts.
	 *
	 * @since 1.3.0
	 *
	 * @return void
	 */
	public function start_migration() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh

		$this->validate_capabilities();

		$data = $this->get_input_data(
			[
				'source_id'        => 'string',
				'batch_size'       => 'numeric',
				'exclude_entities' => 'string',
			]
		);

		$source_id  = $data['source_id'] ?? 'amelia';
		$batch_size = ! empty( $data['batch_size'] ) ? (int) $data['batch_size'] : MigrationService::DEFAULT_BATCH_SIZE;

		// Parse comma-separated exclude_entities string (e.g., "appointment,customer").
		$exclude_entities = [];

		if ( ! empty( $data['exclude_entities'] ) ) {
			$exclude_entities = array_map( 'trim', explode( ',', $data['exclude_entities'] ) );
			$exclude_entities = array_filter( $exclude_entities );
		}

		try {
			// Get adapter from registry.
			$adapter = $this->registry->get_adapter( $source_id );

			if ( ! $adapter ) {
				wp_send_json_error(
					[
						'message' => esc_html__( 'Invalid migration source.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
					]
				);
			}

			// Validate using service.
			$validation = $this->service->validate_source( $adapter );

			if ( is_wp_error( $validation ) ) {
				wp_send_json_error(
					[
						'message' => $validation->get_error_message(),
					]
				);
			}

			// Get entity counts.
			$entity_data = $this->service->get_entity_counts( $adapter, $exclude_entities );

			// Initialize migration state.
			$this->service->initialize_state( $source_id, $batch_size, $entity_data );

			wp_send_json_success(
				[
					'source'        => $adapter->get_name(),
					'entities'      => $entity_data['entities'],
					'entity_order'  => $entity_data['entity_order'],
					'total_records' => $entity_data['total_records'],
					'batch_size'    => $batch_size,
				]
			);

		} catch ( Exception $e ) {
			wp_send_json_error(
				[
					'error_code'    => 500,
					'error_title'   => esc_html__( 'Migration Failed', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
					'error_message' => esc_html__( 'Failed to start migration.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
					'error_details' => $e->getMessage() . "\n\n" . $e->getTraceAsString(),
				]
			);
		}
	}

	/**
	 * Process a single batch of migration.
	 *
	 * @since 1.3.0
	 *
	 * @return void
	 */
	public function process_batch() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh

		$this->validate_capabilities();

		$state = $this->service->get_state();

		if ( ! $state || $state['status'] !== 'running' ) {
			wp_send_json_error(
				[
					'error_code'    => 400,
					'error_title'   => esc_html__( 'Migration Failed', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
					'error_message' => esc_html__( 'No active migration found.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
					'error_details' => '',
				]
			);
		}

		try {
			// Get adapter from registry using source_id from state.
			$adapter = $this->registry->get_adapter( $state['source_id'] );

			if ( ! $adapter ) {
				$this->abort_migration( esc_html__( 'Invalid migration source.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );

				return;
			}

			$result = $this->service->step_batch( $state, $adapter );

			if ( is_wp_error( $result ) ) {
				// step_batch() already called abort_migration().
				wp_send_json_error(
					[
						'error_code'    => 500,
						'error_title'   => esc_html__( 'Migration Failed', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
						'error_message' => $result->get_error_message(),
						'error_details' => '',
					]
				);
			}

			$state       = $result['state'];
			$is_complete = $result['is_complete'];

			if ( $is_complete ) {
				$this->complete_migration( $state );

				return;
			}

			// Calculate progress.
			$progress = $this->service->calculate_progress( $state['total_processed'], $state['total_records'] );

			// Get next entity info for UI.
			$next_entity = $state['entity_order'][ $state['current_entity'] ] ?? null;

			wp_send_json_success(
				[
					'status'          => 'running',
					'current_entity'  => $result['current_entity'],
					'next_entity'     => $next_entity,
					'entity_progress' => $state['entities'][ $result['current_entity'] ] ?? [],
					'progress'        => $progress,
					'total_processed' => $state['total_processed'],
					'total_migrated'  => $state['total_migrated'],
					'total_skipped'   => $state['total_skipped'],
					'total_failed'    => $state['total_failed'],
					'entities'        => $state['entities'],
				]
			);

		} catch ( Exception $e ) {
			$this->abort_migration(
				esc_html__( 'An error occurred while processing the migration.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				500,
				$e->getMessage() . "\n\n" . $e->getTraceAsString()
			);
		}
	}

	/**
	 * Get current migration status.
	 *
	 * @since 1.3.0
	 *
	 * @return void
	 */
	public function get_status() {

		$this->validate_capabilities();

		$state = $this->service->get_state();

		if ( ! $state ) {
			wp_send_json_error(
				[
					'message' => esc_html__( 'No migration state found.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				]
			);
		}

		$progress = $this->service->calculate_progress( $state['total_processed'], $state['total_records'] );

		wp_send_json_success(
			[
				'status'          => $state['status'],
				'progress'        => $progress,
				'total_records'   => $state['total_records'],
				'total_processed' => $state['total_processed'],
				'total_migrated'  => $state['total_migrated'],
				'total_skipped'   => $state['total_skipped'],
				'total_failed'    => $state['total_failed'],
				'entities'        => $state['entities'],
				'started_at'      => $state['started_at'],
			]
		);
	}

	/**
	 * Legacy single-request migration (kept for backwards compatibility).
	 *
	 * @since 1.3.0
	 *
	 * @return void
	 */
	public function migrate_amelia() {

		$this->validate_capabilities();

		$data = $this->get_input_data(
			[
				'dry_run'   => 'form_boolean',
				'source_id' => 'string',
			]
		);

		$dry_run   = ! empty( $data['dry_run'] );
		$source_id = $data['source_id'] ?? 'amelia';

		try {
			// Get adapter from registry.
			$adapter = $this->registry->get_adapter( $source_id );

			if ( ! $adapter ) {
				wp_send_json_error(
					[
						'message' => esc_html__( 'Invalid migration source.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
					]
				);
			}

			$validation = $this->service->validate_source( $adapter );

			if ( is_wp_error( $validation ) ) {
				wp_send_json_error(
					[
						'message' => $validation->get_error_message(),
					]
				);
			}

			// Run full migration using service with adapter.
			$reports = $this->service->run_full_migration(
				$adapter,
				[
					'dry_run'    => $dry_run,
					'batch_size' => MigrationService::DEFAULT_BATCH_SIZE,
				]
			);

			$results        = [];
			$total_migrated = 0;
			$total_errors   = 0;
			$total_skipped  = 0;

			foreach ( $reports as $entity_type => $report ) {
				$report_data             = $report->get_data();
				$results[ $entity_type ] = [
					'migrated' => $report_data['migrated'],
					'errors'   => $report_data['failed'],
					'skipped'  => $report_data['skipped'],
				];

				$total_migrated += $report_data['migrated'];
				$total_errors   += $report_data['failed'];
				$total_skipped  += $report_data['skipped'];
			}

			wp_send_json_success(
				[
					'dry_run'        => $dry_run,
					'source'         => $adapter->get_name(),
					'results'        => $results,
					'total_migrated' => $total_migrated,
					'total_errors'   => $total_errors,
					'total_skipped'  => $total_skipped,
					'has_errors'     => $total_errors > 0,
					'migration_date' => current_time( 'mysql' ),
					'message'        => sprintf(
						/* translators: %1$d - total migrated records; %2$s - source name. */
						__( 'Successfully migrated %1$d records from %2$s.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
						$total_migrated,
						$adapter->get_name()
					),
				]
			);

		} catch ( Exception $e ) {
			wp_send_json_error(
				[
					'message' => $e->getMessage(),
				]
			);
		}
	}

	/**
	 * Download migration report as CSV.
	 *
	 * @since 1.3.0
	 *
	 * @return void
	 */
	public function download_report() {

		$this->validate_capabilities();

		$data = $this->get_input_data(
			[
				'source_id' => 'string',
			]
		);

		$source_id = $data['source_id'] ?? 'amelia';
		$issues    = $this->service->get_stored_issues( $source_id );

		if ( empty( $issues ) ) {
			wp_send_json_error(
				[
					'message' => esc_html__( 'No migration issues found.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				]
			);
		}

		// Generate CSV content.
		$csv_content = $this->generate_csv( $issues );

		// Send headers for CSV download.
		$filename = $source_id . '-migration-report.csv';

		// Clear any output buffers.
		while ( ob_get_level() ) {
			ob_end_clean();
		}

		header( 'Content-Type: text/csv; charset=utf-8' );
		header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
		header( 'Cache-Control: no-cache, no-store, must-revalidate' );
		header( 'Pragma: no-cache' );
		header( 'Expires: 0' );

		// Output UTF-8 BOM for Excel compatibility.
		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		echo "\xEF\xBB\xBF";
		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		echo $csv_content;

		exit;
	}

	/**
	 * Generate CSV content from issues array.
	 *
	 * @since 1.3.0
	 *
	 * @param array<array<string, mixed>> $issues Migration issues.
	 *
	 * @return string CSV content.
	 */
	private function generate_csv( array $issues ): string { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh

		$rows = [];

		// Header row.
		$rows[] = $this->csv_row(
			[
				__( 'Status', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				__( 'Entity Type', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				__( 'Source ID', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				__( 'Name', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				__( 'Reason', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			]
		);

		// Data rows.
		foreach ( $issues as $issue ) {
			$rows[] = $this->csv_row(
				[
					ucfirst( $issue['status'] ?? '' ),
					ucfirst( $issue['entity_type'] ?? '' ),
					$issue['source_id'] ?? '',
					$issue['name'] ?? '',
					$issue['reason'] ?? '',
				]
			);
		}

		return implode( "\n", $rows );
	}

	/**
	 * Convert array to CSV row string.
	 *
	 * @since 1.3.0
	 *
	 * @param array<string> $fields Row fields.
	 *
	 * @return string CSV row.
	 */
	private function csv_row( array $fields ): string {

		$escaped = array_map(
			function ( $field ) {
				// Escape double quotes and wrap in quotes if needed.
				if ( strpos( $field, '"' ) !== false || strpos( $field, ',' ) !== false || strpos( $field, "\n" ) !== false ) {
					return '"' . str_replace( '"', '""', $field ) . '"';
				}

				return $field;
			},
			$fields
		);

		return implode( ',', $escaped );
	}

	/**
	 * Validate user capabilities.
	 *
	 * @since 1.3.0
	 *
	 * @return void
	 */
	private function validate_capabilities() {

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error(
				[
					'message' => esc_html__( 'You do not have permission to perform this action.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				]
			);
		}
	}

	/**
	 * Complete the migration and send success response.
	 *
	 * @since 1.3.0
	 *
	 * @param array<string, mixed> $state Migration state.
	 */
	private function complete_migration( array $state ) {

		$state = $this->service->complete_migration( $state );

		wp_send_json_success(
			[
				'status'          => 'completed',
				'progress'        => 100,
				'total_records'   => $state['total_records'],
				'total_processed' => $state['total_processed'],
				'total_migrated'  => $state['total_migrated'],
				'total_skipped'   => $state['total_skipped'],
				'total_failed'    => $state['total_failed'],
				'entities'        => $state['entities'],
				'started_at'      => $state['started_at'],
				'finished_at'     => $state['finished_at'],
				'message'         => sprintf(
					/* translators: %1$d - total migrated records. */
					__( 'Successfully migrated %1$d records.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
					$state['total_migrated']
				),
			]
		);
	}

	/**
	 * Abort the migration with error.
	 *
	 * @since 1.3.0
	 *
	 * @param string $message       Error message.
	 * @param int    $error_code    HTTP-style error code.
	 * @param string $error_details Technical error details.
	 */
	private function abort_migration( string $message, int $error_code = 500, string $error_details = '' ) {

		$this->service->abort_migration( $message );

		wp_send_json_error(
			[
				'error_code'    => $error_code,
				'error_title'   => esc_html__( 'Migration Failed', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				'error_message' => $message,
				'error_details' => $error_details,
			]
		);
	}
}
