<?php
/**
 * WordPress validation environment.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Validation;

use ScheduledChanges\Capabilities\WordPressPermissionService;
use ScheduledChanges\Database\TableHealth;
use ScheduledChanges\Domain\Operation\Operation;
use ScheduledChanges\Domain\Operation\TargetDefinition;
use ScheduledChanges\Extension\PublicHooks;

/**
 * Performs current-state checks through public WordPress and provider APIs.
 */
final class WordPressValidationEnvironment implements FieldPermissionValidationEnvironment {
	private WordPressPermissionService $permissions;

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

	public function targetExists( TargetDefinition $target ): bool {
		if ( $target->future() ) {
			return true;
		}

		if ( null === $target->targetId() ) {
			return false;
		}

		$exists = get_post_type( $target->targetId() ) === $target->targetType();

		return PublicHooks::validationTargetExists( $exists, $target );
	}

	public function actualSubtype( TargetDefinition $target ): string {
		if ( $target->future() ) {
			return $target->subtype();
		}

		return PublicHooks::validationTargetSubtype( $target->subtype(), $target );
	}

	public function actorCan( int $actor_id, string $capability, TargetDefinition $target ): bool {
		return $this->permissions->actorCan( $actor_id, $capability, $target );
	}

	/**
	 * Applies value-aware native WordPress field permissions at validation time.
	 *
	 * @param mixed $value Planned canonical field value.
	 * @return array{code:string,message:string}|null
	 */
	public function fieldPermissionFailure( int $actor_id, TargetDefinition $target, \ScheduledChanges\Field\FieldDefinition $field, $value ): ?array {
		if ( 'wordpress-core' !== $target->providerId() || null === $target->targetId() ) {
			return null;
		}

		$post = get_post( $target->targetId() );

		return $post instanceof \WP_Post
			? $this->permissions->fieldPermissionFailure( $actor_id, $post, $field, $value )
			: null;
	}

	public function targetFingerprint( TargetDefinition $target ): ?string {
		if ( null === $target->targetId() ) {
			return null;
		}

		$post = get_post( $target->targetId() );

		if ( ! $post instanceof \WP_Post ) {
			return null;
		}

		return hash(
			'sha256',
			implode(
				'|',
				array(
					(string) $post->ID,
					$post->post_type,
					$post->post_status,
					$post->post_modified_gmt,
					$this->actualSubtype( $target ),
				)
			)
		);
	}

	public function attachmentExists( int $attachmentId ): bool {
		return 'attachment' === get_post_type( $attachmentId );
	}

	public function termExists( int $termId, string $taxonomy ): bool {
		$exists = term_exists( $termId, $taxonomy );

		return null !== $exists && false !== $exists;
	}

	public function snapshotStorageAvailable(): bool {
		$health = ( new TableHealth() )->summary();

		return $health['existing'] === $health['expected']
			&& ! in_array( 'snapshots', $health['missing'], true );
	}

	public function actionSchedulerHealthy(): bool {
		return function_exists( 'as_schedule_single_action' )
			&& function_exists( 'as_enqueue_async_action' )
			&& function_exists( 'as_get_scheduled_actions' )
			&& class_exists( 'ActionScheduler' )
			&& class_exists( 'ActionScheduler_Store' );
	}

	public function externalHealth( Operation $operation ): ValidationReport {
		/**
		 * Filters external health validation for one operation.
		 *
		 * Return true for healthy, false for a generic blocking failure, a
		 * WP_Error for structured failure, or a ValidationReport.
		 *
		 * @param mixed     $health Current health result.
		 * @param Operation $operation Operation being validated.
		 */
		$health = PublicHooks::externalOperationHealth( $operation );

		if ( $health instanceof ValidationReport ) {
			return $health;
		}

		if ( is_wp_error( $health ) ) {
			return new ValidationReport(
				array(
					new ValidationIssue(
						'external.health_check_failed',
						$health->get_error_message(),
						ValidationLevel::BLOCKING_ERROR,
						'operations.' . $operation->uuid()->value()
					),
				)
			);
		}

		if ( false === $health ) {
			return new ValidationReport(
				array(
					new ValidationIssue(
						'external.health_check_failed',
						'An external operation health check failed.',
						ValidationLevel::BLOCKING_ERROR,
						'operations.' . $operation->uuid()->value()
					),
				)
			);
		}

		if ( true === $health ) {
			return ValidationReport::valid();
		}

		return new ValidationReport(
			array(
				new ValidationIssue(
					'external.invalid_health_response',
					'An external health check returned an unsupported response.',
					ValidationLevel::BLOCKING_ERROR,
					'operations.' . $operation->uuid()->value()
				),
			)
		);
	}
}
