<?php
/**
 * Recording clone strategy result.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Recording;

/**
 * Structured warnings, blocking failures, and metadata from clone strategies.
 */
final class RecordingCloneStrategyResult {
	/**
	 * @var array<int,array<string,mixed>>
	 */
	private array $warnings;
	/**
	 * @var array<int,array<string,mixed>>
	 */
	private array $failures;
	/**
	 * @var array<string,mixed>
	 */
	private array $metadata;

	/**
	 * @param array<int,array<string,mixed>> $warnings Recoverable warnings.
	 * @param array<int,array<string,mixed>> $failures Blocking failures.
	 * @param array<string,mixed>            $metadata Strategy metadata.
	 */
	public function __construct( array $warnings = array(), array $failures = array(), array $metadata = array() ) {
		$this->warnings = array_values( $warnings );
		$this->failures = array_values( $failures );
		$this->metadata = $metadata;
	}

	/**
	 * @param array<string,mixed> $metadata Strategy metadata.
	 */
	public static function success( array $metadata = array() ): self {
		return new self( array(), array(), $metadata );
	}

	/**
	 * @param array<string,mixed> $context Optional structured context.
	 */
	public static function warning( string $code, string $message, array $context = array() ): self {
		return new self(
			array(
				self::entry( $code, $message, $context ),
			)
		);
	}

	/**
	 * @param array<string,mixed> $context Optional structured context.
	 */
	public static function failure( string $code, string $message, array $context = array() ): self {
		return new self(
			array(),
			array(
				self::entry( $code, $message, $context ),
			)
		);
	}

	public function merge( self $result ): self {
		return new self(
			array_merge( $this->warnings, $result->warnings() ),
			array_merge( $this->failures, $result->failures() ),
			array_merge( $this->metadata, $result->metadata() )
		);
	}

	public function withProviderId( string $provider_id ): self {
		return new self(
			$this->tagEntries( $this->warnings, $provider_id ),
			$this->tagEntries( $this->failures, $provider_id ),
			$this->metadata
		);
	}

	/**
	 * @return array<int,array<string,mixed>>
	 */
	public function warnings(): array {
		return $this->warnings;
	}

	/**
	 * @return array<int,array<string,mixed>>
	 */
	public function failures(): array {
		return $this->failures;
	}

	/**
	 * @return array<string,mixed>
	 */
	public function metadata(): array {
		return $this->metadata;
	}

	public function hasFailures(): bool {
		return array() !== $this->failures;
	}

	/**
	 * @return array<string,mixed>
	 */
	public function toArray(): array {
		return array(
			'warnings' => $this->warnings,
			'failures' => $this->failures,
			'metadata' => $this->metadata,
		);
	}

	public function failureSummary(): string {
		$messages = array();

		foreach ( $this->failures as $failure ) {
			$message = trim( (string) ( $failure['message'] ?? '' ) );
			$code    = trim( (string) ( $failure['code'] ?? '' ) );

			if ( '' === $message ) {
				continue;
			}

			$messages[] = '' === $code ? $message : '[' . $code . '] ' . $message;
		}

		return implode( ' ', array_values( array_unique( $messages ) ) );
	}

	/**
	 * @param array<string,mixed> $context Optional structured context.
	 * @return array<string,mixed>
	 */
	private static function entry( string $code, string $message, array $context ): array {
		return array(
			'code'    => sanitize_key( $code ),
			'message' => $message,
			'context' => $context,
		);
	}

	/**
	 * @param array<int,array<string,mixed>> $entries Entries to tag.
	 * @return array<int,array<string,mixed>>
	 */
	private function tagEntries( array $entries, string $provider_id ): array {
		foreach ( $entries as $index => $entry ) {
			$entries[ $index ]['provider_id'] = $provider_id;
		}

		return $entries;
	}
}
