<?php
/**
 * Handles the transmutation of a reference (local ID).
 *
 * @package Ealab\Deployer
 */

namespace Ealab\Deployer\Tracking\ItemValueHandler\ValueProcessor\TransmutationHandler;

use Dakwamine\Component\RootDependencyDefinition;
use Ealab\Deployer\Exception\IgnoredTransmutationException;
use Ealab\Deployer\Tracking\ItemValueHandler\ItemValueHandlerBase;
use Ealab\Deployer\Tracking\TrackingManager;
use Symfony\Component\Yaml\Yaml;

/**
 * Handles the transmutation of a reference (local ID).
 */
class ReferenceTransmutationHandler extends TransmutationHandlerBase {
	/**
	 * Tracking manager.
	 *
	 * @var TrackingManager|null
	 */
	private ?TrackingManager $tracking_manager = null;

	/**
	 * {@inheritDoc}
	 */
	public function getDependencyDefinitions(): array {
		$definitions = parent::getDependencyDefinitions();

		$definitions[] = new RootDependencyDefinition( TrackingManager::class, $this->tracking_manager );

		return $definitions;
	}

	/**
	 * {@inheritDoc}
	 */
	public static function getFormBuild(): ?array {
		$raw_build = <<<YAML
		label:
		  load: field_label
		  content: Reference to...
		field:
		  type: SelectField
		  dataLocation: ~.transmutation_handler_options.tracker
		  dynamicOptions: ~~.enabledTrackers
		YAML;

		$build = Yaml::parse( $raw_build );

		$build['label']['content'] = __( 'Reference to...', 'deploy-sync-content' );

		return $build;
	}

	/**
	 * {@inheritDoc}
	 */
	public static function getId(): string {
		return 'reference';
	}

	/**
	 * {@inheritDoc}
	 */
	static public function getPublicName(): string {
		return _dpgettext( 'deployer-core', 'Transmutation handler public name', 'References' );
	}

	/**
	 * {@inheritDoc}
	 */
	public function toExport( $value, ?array $options = array() ) {
		if ( ! is_scalar( $value ) ) {
			throw new IgnoredTransmutationException();
		}

		$tracker_id = $options['tracker'] ?? null;

		if ( ! $tracker_id ) {
			throw new IgnoredTransmutationException();
		}

		// Transmute this attribute value.
		$tracker = $this->tracking_manager->getItemTrackerByTrackerId( $tracker_id );

		if ( ! $tracker ) {
			// This tracker does not exist. Leave this attribute value as is.
			throw new IgnoredTransmutationException();
		}

		$known_uuid = $tracker->getItemUuidByLocalId( $value, true );

		if ( ! $known_uuid ) {
			// This local ID is not known in the tracker. Leave this attribute value as is.
			throw new IgnoredTransmutationException();
		}

		$trackable_item = $tracker->loadTrackableItem( $known_uuid );

		if ( ! $trackable_item ) {
			// This known UUID is not linked to a valid item. Leave this attribute value as is.
			throw new IgnoredTransmutationException();
		}

		// Add the dependency to the TrackableItem holder.
		$calling_item_value_handler = $options['_item_value_handler'] ?? null;

		if ( $calling_item_value_handler instanceof ItemValueHandlerBase ) {
			$calling_item_value_handler->addItemDependency( $trackable_item );
		}

		// Finally encapsulate the attribute value in a placeholder.
		return static::getProcessorPlaceholder(
			json_encode(
				array(
					'original_is_string' => is_string( $value ),
					'uuid'               => $known_uuid,
				)
			)
		);
	}

	/**
	 * {@inheritdoc}
	 */
	public function toImportFinalize( $value, ?array $options = array() ) {
		// This variable will be set in the placeholder callback and used in the final callback.
		// This is needed because the original value can be a string or an integer.
		$original_is_string = null;

		return static::applyOnPlaceholders(
			$value,
			function ( array $regex_match ) use ( &$original_is_string ) {
				$to_decode = $regex_match[3];

				$decoded = json_decode( $to_decode, true );

				$original_is_string = $decoded['original_is_string'] ?? false;

				$item_full_uuid = $decoded['uuid'] ?? '';

				// Get the local ID from the full UUID.
				$tracker = $this->tracking_manager->getItemTrackerByItemFullUuid( $item_full_uuid );

				if ( ! $tracker ) {
					// This tracker does not exist. Replace by the 0 reference as an attempt to not break things.
					return '0';
				}

				$local_id = $tracker->getLocalIdFromUuid( $item_full_uuid );

				if ( ! $local_id ) {
					// This UUID is not known in the tracker. This should not happen.
					// Replace by the 0 reference as an attempt to not break things.
					return '0';
				}

				return $local_id;
			},
			function ( $value ) use ( &$original_is_string ) {
				// If the original value was a string, return a string.
				return $original_is_string ? (string) $value : (int) $value;
			}
		);
	}

	/**
	 * {@inheritdoc}
	 */
	public function toImportPresave( $value, ?array $options = array() ) {
		// This variable will be set in the placeholder callback and used in the final callback.
		$original_is_string = null;

		return static::applyOnPlaceholders(
			$value,
			function ( array $regex_match ) use ( &$original_is_string ) {
				$to_decode = $regex_match[3];

				$decoded = json_decode( $to_decode, true );

				$original_is_string = $decoded['original_is_string'] ?? false;

				// Return a 0 reference.
				return $original_is_string ? '0' : 0;
			},
			function ( $value ) use ( &$original_is_string ) {
				// If the original value was a string, return a string.
				return $original_is_string ? (string) $value : (int) $value;
			}
		);
	}
}
