<?php
/**
 * Orchestrates competitor detection and applying an ImportPlan to StoreSheet.
 *
 * @package StoreSheet
 */

declare( strict_types=1 );

namespace StoreSheet\Importer;

defined( 'ABSPATH' ) || exit;

use StoreSheet\Rest\SettingsController;

final class Importer {

	private AdapterRegistry $registry;

	public function __construct( ?AdapterRegistry $registry = null ) {
		$this->registry = $registry ?? new AdapterRegistry();
	}

	/**
	 * Cheap list for seeding the banner — is_present() only, no read_config().
	 *
	 * @return array<int,array{id:string,label:string,supports_adopt:bool}>
	 */
	public function detected_summaries(): array {
		$out = array();
		foreach ( $this->registry->detected() as $adapter ) {
			$out[] = array(
				'id'             => $adapter->id(),
				'label'          => $adapter->label(),
				'supports_adopt' => $adapter->supports_sheet_adopt(),
			);
		}
		return $out;
	}

	/**
	 * Full preview plans for the wizard.
	 *
	 * @return array<int,array<string,mixed>>
	 */
	public function sources(): array {
		$out = array();
		foreach ( $this->registry->detected() as $adapter ) {
			$out[] = $adapter->read_config()->to_array();
		}
		return $out;
	}

	/**
	 * Apply a plan. The client secret and spreadsheet id are taken from a fresh
	 * server-side adapter read (authoritative) — never from the browser payload.
	 * Only non-secret settings overrides are honored from $overrides.
	 *
	 * @param string                                                                          $source_id Adapter id.
	 * @param array{settings?:array<string,mixed>,spreadsheet?:array{id?:string,url?:string}} $overrides Non-secret overrides from the browser.
	 * @param array{adopt_sheet?:bool,deactivate_after?:bool}                                 $flags Behavior flags from the browser.
	 * @return array{applied_settings:string[],warnings:string[],adopt:bool}
	 */
	public function apply( string $source_id, array $overrides, array $flags ): array {
		$adapter = $this->registry->get( $source_id );
		if ( null === $adapter ) {
			return array(
				'applied_settings' => array(),
				'warnings'         => array( __( 'Unknown migration source.', 'storesheet' ) ),
				'adopt'            => false,
			);
		}

		$plan    = $adapter->read_config(); // Authoritative — this is the ONLY place a secret is read.
		$current = get_option( 'storesheet_settings', array() );

		// Snapshot for undo — captured once, before the first apply, so a repeat
		// apply (switching sources, retry) cannot clobber the true pre-import state.
		if ( null === get_option( 'storesheet_settings_backup_pre_import', null ) ) {
			update_option( 'storesheet_settings_backup_pre_import', $current );
		}

		// Non-secret settings: adapter defaults, overlaid by user edits, then sanitized.
		// Secret-shaped keys are stripped from the browser payload here so they can
		// ONLY be populated by the server-side $plan->credentials block below — the
		// wizard never receives these values (masked in ImportPlan::to_array()), so
		// any secret in the payload is a bug or a replay and must not be trusted.
		$overrides_settings = is_array( $overrides['settings'] ?? null ) ? $overrides['settings'] : array();
		$overrides_settings = array_diff_key(
			$overrides_settings,
			array_flip( array( 'oauth_client_id', 'oauth_client_secret', 'oauth_refresh_token', 'webhook_secret' ) )
		);
		$settings_in        = array_merge( $plan->settings, $overrides_settings );
		$clean              = ( new SettingsController() )->sanitize( $settings_in );

		// Credentials from the server-side read, never the browser.
		if ( null !== $plan->credentials ) {
			if ( ! empty( $plan->credentials['oauth_client_id'] ) ) {
				$clean['oauth_client_id'] = sanitize_text_field( $plan->credentials['oauth_client_id'] );
			}
			if ( ! empty( $plan->credentials['oauth_client_secret'] ) ) {
				$clean['oauth_client_secret'] = sanitize_text_field( $plan->credentials['oauth_client_secret'] );
			}
		}

		update_option( 'storesheet_settings', array_merge( $current, $clean ) );

		$report = array(
			'applied_settings' => array_keys( $clean ),
			'warnings'         => $plan->warnings,
			'adopt'            => false,
		);

		// Adopt-sheet seam. Free has no listener (Pro does). Prefer a user-chosen
		// spreadsheet override, else the adapter's discovered spreadsheet.
		if ( ! empty( $flags['adopt_sheet'] ) ) {
			$override_sheet = is_array( $overrides['spreadsheet'] ?? null ) ? $overrides['spreadsheet'] : null;
			if ( null !== $override_sheet ) {
				$plan->spreadsheet = array(
					'id'  => (string) ( $override_sheet['id'] ?? '' ),
					'url' => (string) ( $override_sheet['url'] ?? '' ),
				);
			}
			if ( null !== $plan->spreadsheet ) {
				/**
				 * Fires when the user opts to adopt the competitor's spreadsheet.
				 * Pro's SheetAdopter + ReconciliationRunner listen here.
				 *
				 * @since 1.1.0
				 * @param ImportPlan           $plan  The import plan (may carry a browser-chosen spreadsheet override).
				 * @param array<string,mixed>  $flags Behavior flags from the browser.
				 */
				do_action( 'storesheet_import_adopt_sheet', $plan, $flags );
				$report['adopt'] = (bool) has_action( 'storesheet_import_adopt_sheet' );
			}
		}

		update_option(
			'storesheet_import_state',
			array(
				'last_import' => time(),
				'source'      => $source_id,
				'dismissed'   => true,
			)
		);
		delete_transient( 'storesheet_detected_sources' );
		wp_cache_delete( 'storesheet_overview_stats', 'storesheet' );

		return $report;
	}

	/**
	 * Restore the pre-import settings snapshot.
	 */
	public function undo(): bool {
		$backup = get_option( 'storesheet_settings_backup_pre_import', null );
		if ( null === $backup ) {
			return false;
		}
		update_option( 'storesheet_settings', $backup );
		delete_option( 'storesheet_settings_backup_pre_import' );
		// Clear the import/dismiss state and the cached detection so the
		// auto-detect banner can reappear — the competitor is still active and
		// its settings were just rolled back.
		delete_option( 'storesheet_import_state' );
		delete_transient( 'storesheet_detected_sources' );
		wp_cache_delete( 'storesheet_overview_stats', 'storesheet' );
		return true;
	}
}
