<?php
/**
 * Recording clone lifecycle dispatcher.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Recording;

use ScheduledChanges\Extension\PublicHooks;

/**
 * Dispatches public recording clone strategies registered by modules.
 */
final class RecordingCloneLifecycle {
	public function afterClone( RecordingCloneContext $context ): RecordingCloneStrategyResult {
		return $this->dispatch( $context, 'afterClone', null );
	}

	public function cleanup( RecordingCloneContext $context, string $reason ): RecordingCloneStrategyResult {
		return $this->dispatch( $context, 'cleanup', $reason );
	}

	/**
	 * @param string|null $reason Cleanup reason.
	 */
	private function dispatch(
		RecordingCloneContext $context,
		string $method,
		?string $reason
	): RecordingCloneStrategyResult {
		$aggregate = RecordingCloneStrategyResult::success();
		$metadata  = array();

		foreach ( PublicHooks::recordingCloneStrategies( array() ) as $strategy ) {
			if ( ! $strategy instanceof RecordingCloneStrategy ) {
				$aggregate = $aggregate->merge(
					RecordingCloneStrategyResult::warning(
						'invalid_recording_clone_strategy',
						'An entry registered with scheduled_changes_recording_clone_strategies does not implement RecordingCloneStrategy.'
					)
				);
				continue;
			}

			$provider_id = $this->providerId( $strategy );

			try {
				$result = 'cleanup' === $method
					? $strategy->cleanup( $context, (string) $reason )
					: $strategy->afterClone( $context );
			} catch ( \Throwable $throwable ) {
				$result = RecordingCloneStrategyResult::failure(
					'recording_clone_strategy_exception',
					$throwable->getMessage(),
					array( 'provider_id' => $provider_id )
				);
			}

			$metadata[ $provider_id ] = $result->metadata();
			$tagged                   = $result->withProviderId( $provider_id );
			$aggregate                = $aggregate->merge(
				new RecordingCloneStrategyResult(
					$tagged->warnings(),
					$tagged->failures()
				)
			);
		}

		return $aggregate->merge( RecordingCloneStrategyResult::success( $metadata ) );
	}

	private function providerId( RecordingCloneStrategy $strategy ): string {
		$provider_id = sanitize_key( $strategy->providerId() );

		return '' === $provider_id ? 'unknown' : $provider_id;
	}
}
