<?php
/**
 * WordPress-native scheduling permissions.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Capabilities;

use ScheduledChanges\Domain\Launch\LaunchVersion;
use ScheduledChanges\Domain\Operation\TargetDefinition;
use ScheduledChanges\Field\FieldDefinition;

/**
 * Combines scheduler capabilities with WordPress object and field permissions.
 */
final class WordPressPermissionService {
	private const NATIVE_PROVIDER = 'wordpress-core';

	/**
	 * Checks a capability in the context of an existing target.
	 */
	public function actorCan( int $actor_id, string $capability, TargetDefinition $target ): bool {
		if ( ! $this->validActor( $actor_id ) ) {
			return false;
		}

		$object_id = $target->targetId();

		if ( null !== $object_id && in_array( $capability, array( 'edit_post', 'delete_post' ), true ) ) {
			return user_can( $actor_id, $capability, $object_id );
		}

		if ( ! user_can( $actor_id, $capability ) ) {
			return false;
		}

		return null === $object_id || user_can( $actor_id, 'edit_post', $object_id );
	}

	/**
	 * Checks whether an actor may inspect one target without leaking private content.
	 */
	public function canInspectTarget( int $actor_id, TargetDefinition $target ): bool {
		if ( self::NATIVE_PROVIDER !== $target->providerId() ) {
			return false;
		}

		return null !== $target->targetId() && $this->canInspectPost( $actor_id, $target->targetId() );
	}

	/**
	 * Checks whether every target in an immutable version is visible to an actor.
	 */
	public function canInspectVersion( int $actor_id, LaunchVersion $version ): bool {
		foreach ( $version->plan()->graph()->targets() as $target ) {
			if ( ! $this->canInspectTarget( $actor_id, $target ) ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Checks native post visibility using WordPress meta capabilities.
	 */
	public function canInspectPost( int $actor_id, int $post_id ): bool {
		return $post_id > 0
			&& $this->validActor( $actor_id )
			&& get_post( $post_id ) instanceof \WP_Post
			&& user_can( $actor_id, 'read_post', $post_id );
	}

	/**
	 * Returns a stable provider failure when an actor cannot mutate a field.
	 *
	 * @param mixed $value Canonical value to apply or restore.
	 * @return array{code:string,message:string}|null
	 */
	public function fieldPermissionFailure( int $actor_id, \WP_Post $post, FieldDefinition $field, $value ): ?array {
		if ( ! $this->validActor( $actor_id ) ) {
			return array(
				'code'    => 'actor_unavailable',
				'message' => 'A valid WordPress actor is required.',
			);
		}

		if (
			! user_can( $actor_id, 'edit_post', $post->ID )
			|| ( 'edit_post' !== $field->capability() && ! user_can( $actor_id, $field->capability() ) )
		) {
			return array(
				'code'    => 'edit_permission_denied',
				'message' => 'The actor cannot edit this WordPress field.',
			);
		}

		$key              = (string) ( $field->validationRules()['storage_key'] ?? '' );
		$post_type_object = get_post_type_object( $post->post_type );

		if ( in_array( $key, array( 'post_status', 'post_author' ), true ) && ! $post_type_object ) {
			return array(
				'code'    => 'post_type_capabilities_unavailable',
				'message' => 'The target post type capability mapping is unavailable.',
			);
		}

		if (
			'post_status' === $key
			&& in_array( (string) $value, array( 'publish', 'future', 'private' ), true )
			&& $post_type_object
			&& ! user_can( $actor_id, $post_type_object->cap->publish_posts )
		) {
			return array(
				'code'    => 'publish_permission_denied',
				'message' => 'The actor cannot publish this target.',
			);
		}

		if (
			'post_author' === $key
			&& (int) $value !== (int) $post->post_author
			&& (int) $value !== $actor_id
			&& $post_type_object
			&& ! user_can( $actor_id, $post_type_object->cap->edit_others_posts )
		) {
			return array(
				'code'    => 'author_permission_denied',
				'message' => 'The actor cannot assign another author.',
			);
		}

		if ( 'post_parent' === $key && (int) $value > 0 && ! user_can( $actor_id, 'edit_post', (int) $value ) ) {
			return array(
				'code'    => 'parent_permission_denied',
				'message' => 'The actor cannot assign this parent.',
			);
		}

		if (
			'thumbnail_id' === $key
			&& (int) $value > 0
			&& ( ! user_can( $actor_id, 'upload_files' ) || ! user_can( $actor_id, 'edit_post', (int) $value ) )
		) {
			return array(
				'code'    => 'media_permission_denied',
				'message' => 'The actor cannot assign this media item.',
			);
		}

		if (
			'post_meta' === $key
			&& ! user_can( $actor_id, 'edit_post_meta', $post->ID, (string) ( $field->validationRules()['meta_key'] ?? '' ) )
		) {
			return array(
				'code'    => 'metadata_permission_denied',
				'message' => 'The actor cannot edit this registered metadata.',
			);
		}

		return null;
	}

	/**
	 * Requires a real, non-deleted WordPress user.
	 */
	private function validActor( int $actor_id ): bool {
		return $actor_id > 0 && get_userdata( $actor_id ) instanceof \WP_User;
	}
}
