<?php
/**
 * File Path: includes/admin/tabs/imp-exp/class-mbs-tab-child-import-wxr.php
 *
 * Import / Export Tab — WXR Import Child
 *
 * Handles MBS native WXR import: Pass 1 (analyze) and Pass 2 (run).
 * Tier: FREE (competitor migration — no mbs_is_pro() gate).
 *
 * Split from MBS_Tab_Child_Import_API (REF-004, Session 137).
 *
 * @package MyBookShowroom
 * @since   1.1.0
 */

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

// phpcs:disable WordPress.Security.NonceVerification -- nonce verified in parent class verify_save() / check_admin_referer() before $_POST access; PHPCS cannot trace cross-method nonce verification
// phpcs:disable WordPress.Security.ValidatedSanitizedInput -- input sanitized at point of use (sanitize_text_field, absint, wp_kses_post, etc.); false positives from PHPCS not tracing sanitization helpers called before this method
class MBS_Tab_Child_Import_WXR {

	use MBS_Ajax_Import_Export_Guard;

	/**
	 * AJAX handler: Pass 1 — analyze a WXR file and return its postmeta key list.
	 *
	 * Accepts a .xml file upload, validates it, stores the file in a temp transient,
	 * and returns the unique postmeta keys for the mapping UI.
	 * Tier: FREE (competitor migration — no mbs_is_pro() gate).
	 *
	 * @return void
	 */
	public function handle_ajax_mbs_import_wxr_analyze(): void {
		$this->verify_import_export_request();

		$uploaded = $_FILES['file'] ?? null;
		if ( ! $uploaded || UPLOAD_ERR_OK !== (int) $uploaded['error'] ) {
			$err_code = (int) ( $uploaded['error'] ?? -1 );
			wp_send_json_error( array( 'message' => sprintf( 'File upload failed (code %d).', $err_code ) ) );
		}

		// Validate file extension.
		$upload_name = isset( $uploaded['name'] ) ? sanitize_file_name( wp_unslash( $uploaded['name'] ) ) : '';
		$ext         = strtolower( pathinfo( $upload_name, PATHINFO_EXTENSION ) );
		if ( 'xml' !== $ext ) {
			wp_send_json_error( array( 'message' => 'Only .xml WXR export files are accepted.' ) );
		}

		if ( ! is_uploaded_file( $uploaded['tmp_name'] ) ) {
			wp_send_json_error( array( 'message' => 'Invalid file upload.' ) );
		}

		// SEC-010: Inspect file content — do not rely on extension alone.
		$is_valid_xml = self::is_accepted_xml_upload( $uploaded['tmp_name'], $upload_name );
		if ( ! $is_valid_xml ) {
			wp_send_json_error( array( 'message' => 'Only .xml WXR export files are accepted.' ) );
		}

		// Stage to a persistent temp path — the tmp_name is gone after this request.
		$temp_dir = get_temp_dir() . 'mbs-temp-import/';
		wp_mkdir_p( $temp_dir );
		$uid       = get_current_user_id();
		$token     = wp_generate_password( 24, false );
		$temp_path = $temp_dir . 'wxr_' . $uid . '_' . $token . '.xml';

		if ( ! copy( $uploaded['tmp_name'], $temp_path ) ) {
			wp_send_json_error( array( 'message' => 'Failed to stage the uploaded file for analysis.' ) );
		}

		$analysis = MBS_Importer_WXR::analyze( $temp_path );

		if ( null !== $analysis['error'] ) {
			// phpcs:ignore WordPress.PHP.NoSilencedErrors
			wp_delete_file( $temp_path );
			wp_send_json_error( array( 'message' => $analysis['error'] ) );
		}

		// Store the staged file path against the token so Pass 2 can find it.
		set_transient( 'mbs_wxr_file_' . $uid . '_' . $token, $temp_path, HOUR_IN_SECONDS );

		// Return key list, CPT hint, source slug, and target-field options for the UI.
		wp_send_json_success( array(
			'token'         => $token,
			'keys'          => $analysis['keys'],
			'cpt'           => $analysis['cpt'],
			'source'        => $analysis['source'] ?? '',
			'target_fields' => MBS_Importer_WXR::MBS_TARGET_FIELDS,
		) );
	}

	/**
	 * AJAX handler: Pass 2 — run the WXR import with the user-supplied mapping.
	 *
	 * Retrieves the staged file via token, validates the mapping, and calls
	 * MBS_Importer_WXR::import(). Cleans up the temp file on completion.
	 * Tier: FREE (competitor migration — no mbs_is_pro() gate).
	 *
	 * @return void
	 */
	public function handle_ajax_mbs_import_wxr_run(): void {
		$this->verify_import_export_request();

		$uid   = get_current_user_id();
		$token = sanitize_text_field( wp_unslash( $_POST['token'] ?? '' ) );
		if ( '' === $token ) {
			wp_send_json_error( array( 'message' => 'Missing import session token.' ) );
		}

		$temp_path = get_transient( 'mbs_wxr_file_' . $uid . '_' . $token );
		if ( false === $temp_path || ! is_string( $temp_path ) || ! file_exists( $temp_path ) ) {
			wp_send_json_error( array( 'message' => 'Import session expired or not found. Please re-upload the file.' ) );
		}

		// Decode and sanitize the user-supplied mapping.
		// Shape expected: { "source_meta_key": "mbs_target_field", ... }
		$raw_mapping = wp_unslash( $_POST['mapping'] ?? '' );
		$decoded     = is_string( $raw_mapping ) ? json_decode( $raw_mapping, true ) : null;
		if ( ! is_array( $decoded ) ) {
			wp_send_json_error( array( 'message' => 'Invalid field mapping payload.' ) );
		}

		$valid_targets = array_keys( MBS_Importer_WXR::MBS_TARGET_FIELDS );
		$mapping       = array();
		foreach ( $decoded as $src => $tgt ) {
			$src = sanitize_key( (string) $src );
			$tgt = sanitize_key( (string) $tgt );
			if ( '' !== $src && in_array( $tgt, $valid_targets, true ) ) {
				$mapping[ $src ] = $tgt;
			}
		}

		$target_cpt = sanitize_key( wp_unslash( $_POST['target_cpt'] ?? '' ) );

		// Run import.
		$result = MBS_Importer_WXR::import( $temp_path, $mapping, $target_cpt );

		// Clean up temp file and transient.
		// phpcs:ignore WordPress.PHP.NoSilencedErrors
		wp_delete_file( $temp_path );
		delete_transient( 'mbs_wxr_file_' . $uid . '_' . $token );

		if ( 0 === $result['imported'] && 0 === $result['skipped'] && ! empty( $result['errors'] ) ) {
			wp_send_json_error( array(
				'message' => $result['errors'][0],
				'errors'  => $result['errors'],
			) );
		}

		wp_send_json_success( array(
			'imported' => $result['imported'],
			'skipped'  => $result['skipped'],
			'errors'   => $result['errors'],
		) );
	}
}
