<?php
/**
 * Recording workspace adapter router.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Recording;

use ScheduledChanges\Domain\Recording\RecordingSession;
use ScheduledChanges\Domain\Shared\Uuid;

/**
 * Routes recording lifecycle calls by neutral source type.
 */
final class RecordingWorkspaceRouter implements RecordingWorkspaceAdapter {
	/**
	 * @var array<string,RecordingWorkspaceAdapter>
	 */
	private array $routes;

	/**
	 * @param array<string,RecordingWorkspaceAdapter> $routes Adapters keyed by post/source type.
	 */
	public function __construct( array $routes ) {
		$this->routes = $routes;
	}

	/**
	 * Returns a new router with additional source-type routes.
	 *
	 * @param string[] $source_types Source types owned by the adapter.
	 */
	public function withAdapter( array $source_types, RecordingWorkspaceAdapter $adapter ): self {
		$routes = $this->routes;

		foreach ( $source_types as $source_type ) {
			$routes[ $source_type ] = $adapter;
		}

		return new self( $routes );
	}

	public function sourceType(): string {
		return 'routed';
	}

	public function workspaceType(): string {
		return 'routed_workspace';
	}

	public function assertCanStart( int $source_id ): void {
		$this->forSource( $source_id )->assertCanStart( $source_id );
	}

	public function assertCanUse( RecordingSession $session ): void {
		$this->forSession( $session )->assertCanUse( $session );
	}

	public function captureSourceBaseline( int $source_id ): array {
		return $this->forSource( $source_id )->captureSourceBaseline( $source_id );
	}

	public function create( RecordingWorkspaceRequest $request ): RecordingWorkspaceResult {
		return $this->forSource( $request->sourceId() )->create( $request );
	}

	public function captureWorkspace( RecordingSession $session ): array {
		return $this->forSession( $session )->captureWorkspace( $session );
	}

	public function captureSource( RecordingSession $session ): array {
		return $this->forSession( $session )->captureSource( $session );
	}

	public function buildDiff( RecordingSession $session, array $workspace_snapshot ): array {
		return $this->forSession( $session )->buildDiff( $session, $workspace_snapshot );
	}

	public function cleanup( RecordingSession $session, string $reason ): void {
		$this->forSession( $session )->cleanup( $session, $reason );
	}

	public function cleanupCreated( RecordingWorkspaceRequest $request, RecordingWorkspaceResult $workspace, string $reason ): void {
		$this->forSource( $request->sourceId() )->cleanupCreated( $request, $workspace, $reason );
	}

	public function lockTarget( int $source_id ): string {
		return $this->forSource( $source_id )->lockTarget( $source_id );
	}

	public function defaultLaunchName( RecordingSession $session ): string {
		return $this->forSession( $session )->defaultLaunchName( $session );
	}

	public function context( Uuid $uuid, int $source_id, ?int $workspace_id, int $owner_id, string $phase ): array {
		$source_type = get_post_type( $source_id );

		if ( is_string( $source_type ) && isset( $this->routes[ $source_type ] ) ) {
			return $this->routes[ $source_type ]->context( $uuid, $source_id, $workspace_id, $owner_id, $phase );
		}

		$workspace_type = null === $workspace_id ? false : get_post_type( $workspace_id );

		if ( is_string( $workspace_type ) && isset( $this->routes[ $workspace_type ] ) ) {
			return $this->routes[ $workspace_type ]->context( $uuid, $source_id, $workspace_id, $owner_id, $phase );
		}

		throw new \RuntimeException( 'No recording workspace adapter supports this recording context.' );
	}

	private function forSource( int $source_id ): RecordingWorkspaceAdapter {
		$source_type = get_post_type( $source_id );

		if ( ! is_string( $source_type ) || ! isset( $this->routes[ $source_type ] ) ) {
			throw new \RuntimeException( 'No recording workspace adapter supports this source type.' );
		}

		return $this->routes[ $source_type ];
	}

	private function forSession( RecordingSession $session ): RecordingWorkspaceAdapter {
		$source_type = $session->sourceType();

		if ( ! isset( $this->routes[ $source_type ] ) ) {
			throw new \RuntimeException( 'The recording workspace adapter for this session is unavailable.' );
		}

		return $this->routes[ $source_type ];
	}
}
