<?php
/**
 * Base field provider behavior.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Provider;

use ScheduledChanges\Field\CanonicalNormalizer;
use ScheduledChanges\Field\FieldDefinition;

/**
 * Supplies canonical normalization and snapshot diff behavior.
 */
abstract class AbstractFieldProvider implements FieldProvider {
	private CanonicalNormalizer $normalizer;

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

	/**
	 * @param mixed $value
	 * @return mixed
	 */
	public function normalize( FieldDefinition $field, $value ) {
		return $this->normalizer->normalize(
			$field->dataType(),
			$value,
			$field->normalization()
		);
	}

	/**
	 * @param mixed $value
	 */
	public function validate(
		ObjectContext $context,
		FieldDefinition $field,
		$value
	): ValidationResult {
		unset( $context );

		try {
			$this->normalize( $field, $value );
		} catch ( \Throwable $throwable ) {
			return ValidationResult::invalid(
				'invalid_value',
				$throwable->getMessage(),
				$field->id()
			);
		}

		return ValidationResult::valid();
	}

	public function diff( ProviderSnapshot $before, ProviderSnapshot $after ): ChangeCollection {
		$fieldIds = array_unique(
			array_merge(
				array_keys( $before->values() ),
				array_keys( $after->values() )
			)
		);
		$changes  = array();

		sort( $fieldIds );

		foreach ( $fieldIds as $fieldId ) {
			if ( $before->value( $fieldId ) !== $after->value( $fieldId ) ) {
				$changes[] = new Change(
					$fieldId,
					$before->value( $fieldId ),
					$after->value( $fieldId )
				);
			}
		}

		return new ChangeCollection( $changes );
	}
}
