<?php
/**
 * Import tool admin controller
 *
 * Hosts the AJAX endpoints that back the Export & Import page's import form
 * and the per-listing modals. Preview normalizes any supported format into
 * an ImportManifest and enriches it with collision metadata; Execute applies
 * user decisions via FullImporter.
 *
 * @package SlideMind\Admin
 * @since   0.6.0
 */

namespace SlideMind\Admin;

use SlideMind\Core\ImportManifest;
use SlideMind\Core\ImportParser;
use SlideMind\Core\FullImporter;

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

class ImportToolPage {

	const CAPABILITY      = 'manage_slidemind_settings';
	const TRANSIENT_TTL   = HOUR_IN_SECONDS;
	const ACTION_PREVIEW  = 'slidemind_import_preview';
	const ACTION_EXECUTE  = 'slidemind_import_execute';

	/** @var ImportParser */
	private $parser;

	/** @var FullImporter */
	private $importer;

	public function __construct( ImportParser $parser, FullImporter $importer ) {
		$this->parser   = $parser;
		$this->importer = $importer;

		add_action( 'wp_ajax_' . self::ACTION_PREVIEW, [ $this, 'ajax_preview' ] );
		add_action( 'wp_ajax_' . self::ACTION_EXECUTE, [ $this, 'ajax_execute' ] );
	}

	public function ajax_preview(): void {
		check_ajax_referer( 'slidemind_admin_nonce', 'nonce' );
		if ( ! current_user_can( self::CAPABILITY ) ) {
			wp_send_json_error( [ 'message' => __( 'Unauthorized access', 'slidemind' ) ] );
		}

		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON validated after decode.
		$json = isset( $_POST['json'] ) ? wp_unslash( $_POST['json'] ) : '';
		if ( ! is_string( $json ) || $json === '' ) {
			wp_send_json_error( [ 'message' => __( 'No data provided', 'slidemind' ) ] );
		}

		$manifest = $this->parser->parse( $json );
		if ( is_wp_error( $manifest ) ) {
			wp_send_json_error( [ 'message' => $manifest->get_error_message() ] );
		}

		$manifest = $this->importer->analyze_collisions( $manifest );

		// Sanitize before persisting to the transient and echoing the preview.
		$clean    = \SlideMind\Core\ImportSanitizer::sanitize( $manifest->to_array() );
		$manifest = ImportManifest::from_array( $clean );

		$key = $this->transient_key();
		set_transient( $key, $manifest->to_array(), self::TRANSIENT_TTL );

		wp_send_json_success( [
			'manifest'      => $manifest->to_array(),
			'transient_key' => $key,
		] );
	}

	public function ajax_execute(): void {
		check_ajax_referer( 'slidemind_admin_nonce', 'nonce' );
		if ( ! current_user_can( self::CAPABILITY ) ) {
			wp_send_json_error( [ 'message' => __( 'Unauthorized access', 'slidemind' ) ] );
		}

		$stored = get_transient( $this->transient_key() );
		if ( ! is_array( $stored ) ) {
			wp_send_json_error( [ 'message' => __( 'Import session expired. Please re-select the file.', 'slidemind' ) ] );
		}
		$manifest = ImportManifest::from_array( $stored );

		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- validated below.
		$raw_decisions = isset( $_POST['decisions'] ) ? wp_unslash( $_POST['decisions'] ) : '';
		$decoded       = is_string( $raw_decisions ) ? json_decode( $raw_decisions, true ) : $raw_decisions;
		if ( ! is_array( $decoded ) ) {
			wp_send_json_error( [ 'message' => __( 'Invalid decisions payload.', 'slidemind' ) ] );
		}
		$decisions = $this->sanitize_decisions( $decoded );

		$limit_check = $this->importer->validate_limits( $manifest, $decisions );
		if ( is_wp_error( $limit_check ) ) {
			$data = $limit_check->get_error_data();
			wp_send_json_error( array_merge(
				[ 'message' => $limit_check->get_error_message() ],
				is_array( $data ) ? $data : []
			) );
		}

		// Scoped imports (per-listing modals) override parent refs to the context parent.
		if ( $decisions['scope'] !== 'bulk' ) {
			$context_module = (int) $decisions['parent_module_id'];
			$context_course = (int) $decisions['parent_course_id'];
			foreach ( $manifest->modules as &$module ) {
				$module['parent_course_ref'] = null; // force holder if scope=module and no context course
			}
			unset( $module );
			foreach ( $manifest->slides as &$slide ) {
				$slide['parent_module_ref'] = null;
				$slide['parent_course_ref'] = null;
			}
			unset( $slide );
			// Inject context parents by pre-seeding the ref_to_post_id map.
			if ( $context_module > 0 ) {
				$manifest->slides = array_map( function( $s ) use ( $context_module ) {
					$s['parent_module_ref'] = 'scope_module';
					return $s;
				}, $manifest->slides );
			}
			if ( $context_course > 0 ) {
				$manifest->modules = array_map( function( $m ) use ( $context_course ) {
					$m['parent_course_ref'] = 'scope_course';
					return $m;
				}, $manifest->modules );
				$manifest->slides = array_map( function( $s ) use ( $context_course ) {
					$s['parent_course_ref'] = 'scope_course';
					return $s;
				}, $manifest->slides );
			}
			// Pre-seed the ref_to_post_id map via a dedicated decisions field.
			$decisions['seeded_refs'] = [];
			if ( $context_module > 0 ) {
				$decisions['seeded_refs']['scope_module'] = $context_module;
			}
			if ( $context_course > 0 ) {
				$decisions['seeded_refs']['scope_course'] = $context_course;
			}
		}

		$report = $this->importer->execute( $manifest, $decisions );
		delete_transient( $this->transient_key() );

		wp_send_json_success( [ 'report' => $report ] );
	}

	private function transient_key(): string {
		return 'slidemind_import_manifest_' . get_current_user_id();
	}

	/**
	 * Sanitize the decisions payload. Tolerates missing keys, drops unknown
	 * sections, coerces actions to the allowlist.
	 *
	 * @param array<string,mixed> $raw
	 * @return array<string,mixed>
	 */
	private function sanitize_decisions( array $raw ): array {
		$out = [
			'decisions'                   => [
				'courses'   => [],
				'modules'   => [],
				'slides'    => [],
				'templates' => [],
			],
			'apply_settings'              => ! empty( $raw['apply_settings'] ),
			'selected_memory_definitions' => [],
			'scope'                       => isset( $raw['scope'] ) ? sanitize_key( (string) $raw['scope'] ) : 'bulk',
			'parent_module_id'            => isset( $raw['parent_module_id'] ) ? absint( $raw['parent_module_id'] ) : 0,
			'parent_course_id'            => isset( $raw['parent_course_id'] ) ? absint( $raw['parent_course_id'] ) : 0,
		];

		$actions_allowed = [ 'new', 'duplicate', 'overwrite', 'skip' ];
		$raw_decisions   = isset( $raw['decisions'] ) && is_array( $raw['decisions'] ) ? $raw['decisions'] : [];

		foreach ( [ 'courses', 'modules', 'slides', 'templates' ] as $section ) {
			$section_decisions = isset( $raw_decisions[ $section ] ) && is_array( $raw_decisions[ $section ] ) ? $raw_decisions[ $section ] : [];
			foreach ( $section_decisions as $file_ref => $action ) {
				$file_ref = (string) $file_ref;
				if ( ! preg_match( '/^(course|module|slide|template)_[A-Za-z0-9_]+$/', $file_ref ) ) {
					continue;
				}
				$action = sanitize_key( (string) $action );
				if ( ! in_array( $action, $actions_allowed, true ) ) {
					continue;
				}
				$out['decisions'][ $section ][ $file_ref ] = $action;
			}
		}

		if ( isset( $raw['selected_memory_definitions'] ) && is_array( $raw['selected_memory_definitions'] ) ) {
			foreach ( $raw['selected_memory_definitions'] as $id ) {
				$out['selected_memory_definitions'][] = sanitize_text_field( (string) $id );
			}
		}

		return $out;
	}
}
