<?php

namespace Fotogenic_AI;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Own durable, idempotent destination delivery after attachment finalization.
 */
final class Result_Delivery_Coordinator {
	public const STATE_VERSION = 1;

	private const ACTIONS = [ 'apply_featured', 'apply_gallery', 'apply_both', 'add_selected_to_gallery', 'add_all_to_gallery' ];
	private const ATTACHMENT_ACTIONS = [ 'apply_featured', 'apply_gallery', 'apply_both' ];
	private const AUTOMATIC_WORKFLOWS = [ 'product_images', 'campaign_set' ];
	public const HOOK_RETRY = 'fotogenic_ai_result_delivery_retry';
	private const LOCK_PREFIX = 'fotogenic_ai_result_delivery_';
	private const TARGET_LOCK_PREFIX = 'fotogenic_ai_result_delivery_target_';
	private const LOCK_TTL = 300;
	private const RETRY_DELAY = MINUTE_IN_SECONDS;

	private ?ProductMediaWriteAdapter $media_write_adapter;

	public function __construct( ?ProductMediaWriteAdapter $media_write_adapter ) {
		$this->media_write_adapter = $media_write_adapter;
	}

	/**
	 * Apply a trusted automatic policy after a terminal run has saved outputs.
	 *
	 * @return array<string, mixed>|\WP_Error
	 */
	public function apply_automatic( string $run_id, string $final_status ) {
		$run_id       = trim( $run_id );
		$final_status = sanitize_key( $final_status );
		$row          = Logger::instance()->get_run_row( $run_id );
		if (
			'' === $run_id
			|| empty( $row )
			|| ! in_array( $final_status, [ 'completed', 'partial' ], true )
			|| $final_status !== sanitize_key( (string) ( $row['final_status'] ?? '' ) )
		) {
			return $this->invalid_delivery();
		}

		$workflow   = sanitize_key( (string) ( $row['workflow'] ?? '' ) );
		$run_target = sanitize_key( (string) ( $row['run_target'] ?? '' ) );
		$destination = $this->decode_object( $row['destination_snapshot'] ?? null );
		if (
			'product' !== $run_target
			|| ! in_array( $workflow, self::AUTOMATIC_WORKFLOWS, true )
			|| 'gallery_append' !== sanitize_key( (string) ( $destination['assignmentMode'] ?? '' ) )
		) {
			return [
				'changed'        => false,
				'reviewRequired' => true,
			];
		}

		$target = $this->target_from_row( $row );
		if ( is_wp_error( $target ) ) {
			return $target;
		}
		if ( 'variation' === (string) $target['targetType'] ) {
			return [
				'changed'        => false,
				'reviewRequired' => true,
			];
		}
		if ( 'product' !== (string) $target['targetType'] ) {
			return $this->invalid_delivery();
		}

		$attachment_ids = $this->owned_attachment_ids( $row );
		if ( empty( $attachment_ids ) ) {
			$attachment_ids = $this->admitted_attachment_ids( $row );
			if ( empty( $attachment_ids ) ) {
				return $this->invalid_delivery();
			}
		}

		return $this->deliver( $row, $attachment_ids, 'add_all_to_gallery', true );
	}

	/**
	 * Apply one already-authorized manual destination action.
	 *
	 * @return array{status:string,action:string,changed:bool,editUrl:string}|\WP_Error
	 */
	public function apply_manual_action( array $row, array $attachment_ids, string $action ) {
		return $this->deliver( $row, $attachment_ids, $action, false );
	}

	/**
	 * Convert a valid durable state into browser-safe presentation facts.
	 *
	 * @return array<string, mixed>
	 */
	public static function presentation( array $row, array $current_attachment_ids ): array {
		$state = self::normalize_state( $row['delivery_state'] ?? null );
		$current_attachment_ids = self::normalize_attachment_ids( $current_attachment_ids );
		if ( empty( $state ) || empty( $current_attachment_ids ) ) {
			return [];
		}

		$state_attachment_ids = (array) $state['attachmentIds'];
		$action               = (string) $state['action'];
		$status               = (string) $state['status'];
		$is_attachment_action = in_array( $action, self::ATTACHMENT_ACTIONS, true );
		$is_selected_action   = 'add_selected_to_gallery' === $action;
		$applied_attachment_id = absint( $state['appliedAttachmentId'] ?? 0 );
		if ( $is_attachment_action ) {
			if ( $applied_attachment_id <= 0 || ! in_array( $applied_attachment_id, $state_attachment_ids, true ) ) {
				return [];
			}
			if ( 'applied' === $status && ! in_array( $applied_attachment_id, $current_attachment_ids, true ) ) {
				return [];
			}
		} elseif (
			'applied' === $status
			&& (
				( $is_selected_action && ! empty( array_diff( $state_attachment_ids, $current_attachment_ids ) ) )
				|| ( ! $is_selected_action && $state_attachment_ids !== $current_attachment_ids )
			)
		) {
			return [];
		} elseif ( empty( array_intersect( $state_attachment_ids, $current_attachment_ids ) ) ) {
			return [];
		}

		if ( 'applied' === $status && ! self::applied_state_matches_destination( $row, $state ) ) {
			return [];
		}

		if ( 'applied' === $status ) {
			$delivery_label = __( 'Applied to destination', 'fotogenic-ai' );
			if ( 'add_all_to_gallery' === $action ) {
				$delivery_label = __( 'Appended to product gallery', 'fotogenic-ai' );
			} elseif ( $is_selected_action ) {
				$delivery_label = sprintf(
					/* translators: 1: number of selected campaign images, 2: total campaign images */
					__( '%1$d of %2$d campaign images appended to product gallery', 'fotogenic-ai' ),
					count( $state_attachment_ids ),
					count( $current_attachment_ids )
				);
			}
			return [
				'appliedAction'        => $action,
				'appliedAttachmentId'  => $is_attachment_action ? $applied_attachment_id : 0,
				'appliedAttachmentIds' => $is_selected_action ? $state_attachment_ids : [],
				'deliveryLabel'        => $delivery_label,
			];
		}
		if ( 'failed' === $status ) {
			return [
				'deliveryFailed' => true,
				'retryAction'    => $action,
				'deliveryLabel'  => __( 'Delivery failed; attachments saved', 'fotogenic-ai' ),
				'message'        => __( 'Generated attachments were saved, but the destination could not be updated.', 'fotogenic-ai' ),
			];
		}
		if ( 'applying' === $status ) {
			return [
				'deliveryPending' => true,
				'deliveryLabel'   => __( 'Applying saved results', 'fotogenic-ai' ),
			];
		}

		return [];
	}

	/**
	 * Finalized-run hook. Delivery is non-billable and uses only saved identity.
	 */
	public static function handle_finalized_run( string $run_id, string $final_status ): void {
		if ( ! Background_Runtime_State::is_enabled() ) {
			return;
		}
		try {
			$coordinator = self::runtime();
			if ( null === $coordinator ) {
				return;
			}
			$coordinator->apply_automatic( $run_id, $final_status );
		} catch ( \Throwable $exception ) {
			try {
				Logger::instance()->log_finalize_worker(
					'result_delivery_deferred',
					[
						'run_id' => trim( $run_id ),
						'reason' => sanitize_key( fotogenic_ai_get_debug_type( $exception ) ),
					]
				);
			} catch ( \Throwable $logging_exception ) {
				// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Last-resort bounded evidence must not abort core finalizer cleanup.
				error_log( 'Fotogenic AI: optional result delivery was deferred after an unexpected worker error.' );
			}
		}
	}

	/**
	 * Recover a process that stopped after persisting an applying state.
	 */
	public static function handle_scheduled_retry( string $run_id, string $mode ): void {
		if ( ! Background_Runtime_State::is_enabled() ) {
			return;
		}
		$row   = Logger::instance()->get_run_row( trim( $run_id ) );
		$state = self::normalize_state( $row['delivery_state'] ?? null );
		$mode  = sanitize_key( $mode );
		if ( empty( $row ) || ! in_array( $mode, [ 'automatic', 'manual' ], true ) ) {
			return;
		}

		$coordinator = self::runtime();
		if ( null === $coordinator ) {
			return;
		}
		if ( ! empty( $state ) ) {
			if ( 'applying' !== (string) $state['status'] ) {
				return;
			}
			$state_mode = ! empty( $state['automatic'] ) ? 'automatic' : 'manual';
			if ( $state_mode !== $mode ) {
				return;
			}
			if ( 'manual' === $mode ) {
				$coordinator->deliver(
					$row,
					(array) $state['attachmentIds'],
					(string) $state['action'],
					false
				);
				return;
			}
		}
		if ( 'manual' === $mode ) {
			return;
		}

		$coordinator->apply_automatic( $run_id, sanitize_key( (string) ( $row['final_status'] ?? '' ) ) );
	}

	/**
	 * @return array{status:string,action:string,changed:bool,editUrl:string}|\WP_Error
	 */
	private function deliver( array $row, array $attachment_ids, string $action, bool $automatic ) {
		$run_id = trim( (string) ( $row['run_id'] ?? '' ) );
		$action = sanitize_key( $action );
		$attachment_ids = self::normalize_attachment_ids( $attachment_ids );
		if ( '' === $run_id || empty( $attachment_ids ) || ! in_array( $action, self::ACTIONS, true ) ) {
			return $this->invalid_delivery();
		}

		$lock_key = self::LOCK_PREFIX . substr( hash( 'sha256', $run_id ), 0, 40 );
		$locks    = Background_Lock::instance();
		$token    = $locks->acquire_option( $lock_key, self::LOCK_TTL );
		if ( false === $token ) {
			if ( $automatic ) {
				$this->schedule_retry( $run_id, true );
			}
			return new \WP_Error(
				'fotogenic_ai_result_delivery_busy',
				__( 'This result destination is already being updated.', 'fotogenic-ai' ),
				[ 'status' => 409 ]
			);
		}
		$target_lock_key = '';
		$target_token    = false;

		try {
			$fresh = Logger::instance()->get_run_row( $run_id );
			if ( ! $this->same_run_identity( $row, $fresh ) ) {
				return $this->invalid_delivery();
			}
			$admitted_attachment_ids = $this->admitted_attachment_ids( $fresh );
			$owned_attachment_ids = $this->owned_attachment_ids( $fresh );
			if ( empty( $owned_attachment_ids ) || ! empty( array_diff( $attachment_ids, $owned_attachment_ids ) ) ) {
				if ( ! empty( $admitted_attachment_ids ) && empty( array_diff( $attachment_ids, $admitted_attachment_ids ) ) ) {
					return $this->attachment_validation_failure( $run_id, $action, $attachment_ids, $automatic );
				}
				return $this->invalid_delivery();
			}
			if ( $automatic && $attachment_ids !== $owned_attachment_ids ) {
				return $this->invalid_delivery();
			}

			$target = $this->target_from_row( $fresh );
			if ( is_wp_error( $target ) ) {
				return $target;
			}
			$flags = $this->action_flags( $action, $target, $attachment_ids );
			if ( is_wp_error( $flags ) ) {
				return $flags;
			}

			$owner_id  = absint( $fresh['user_id'] ?? 0 );
			$target_id = absint( $target['targetId'] ?? 0 );
			if ( $owner_id <= 0 || $target_id <= 0 || ! $this->owner_can_edit_target( $owner_id, $target ) ) {
				$forbidden = new \WP_Error(
					'fotogenic_ai_result_delivery_forbidden',
					__( 'The saved run owner can no longer update this destination.', 'fotogenic-ai' ),
					[ 'status' => 403 ]
				);
				$failed = $this->state_document(
					'failed',
					$action,
					$attachment_ids,
					$automatic,
					(string) $forbidden->get_error_code(),
					(string) $forbidden->get_error_message()
				);
				if ( $this->persist_state( $run_id, $failed ) ) {
					$this->clear_retry( $run_id, $automatic );
				}
				return $forbidden;
			}
			$existing = self::normalize_state( $fresh['delivery_state'] ?? null );
			if (
				'applied' === (string) ( $existing['status'] ?? '' )
				&& $action === (string) ( $existing['action'] ?? '' )
				&& $attachment_ids === (array) ( $existing['attachmentIds'] ?? [] )
				&& self::applied_state_matches_destination( $fresh, $existing )
			) {
				return $this->success_response( $fresh, $action, false );
			}

			$applying = $this->state_document( 'applying', $action, $attachment_ids, $automatic );
			if ( ! $this->schedule_retry( $run_id, $automatic ) ) {
				$unavailable = new \WP_Error(
					'fotogenic_ai_result_delivery_unavailable',
					__( 'The result destination retry could not be scheduled.', 'fotogenic-ai' ),
					[ 'status' => 503 ]
				);
				$this->persist_state(
					$run_id,
					$this->state_document(
						'failed',
						$action,
						$attachment_ids,
						$automatic,
						(string) $unavailable->get_error_code(),
						(string) $unavailable->get_error_message()
					)
				);
				return $unavailable;
			}
			if ( ! $this->persist_state( $run_id, $applying ) ) {
				$this->clear_retry( $run_id, $automatic );
				return new \WP_Error(
					'fotogenic_ai_result_delivery_unavailable',
					__( 'The result destination state could not be saved.', 'fotogenic-ai' ),
					[ 'status' => 503 ]
				);
			}

			$run_target      = sanitize_key( (string) ( $fresh['run_target'] ?? '' ) );
			$target_lock_key = self::TARGET_LOCK_PREFIX . substr( hash( 'sha256', get_current_blog_id() . ':' . $run_target . ':' . $target_id ), 0, 40 );
			$target_token    = $locks->acquire_option( $target_lock_key, self::LOCK_TTL );
			if ( false === $target_token ) {
				return new \WP_Error(
					'fotogenic_ai_result_delivery_busy',
					__( 'This destination is already being updated. Delivery will retry automatically.', 'fotogenic-ai' ),
					[ 'status' => 409 ]
				);
			}

			$original_user_id = get_current_user_id();
			if ( $original_user_id !== $owner_id ) {
				wp_set_current_user( $owner_id );
			}
			try {
				$applied = $this->apply_to_target( $fresh, $target, $attachment_ids, $flags );
			} finally {
				if ( get_current_user_id() !== $original_user_id ) {
					wp_set_current_user( $original_user_id );
				}
			}
			if ( is_wp_error( $applied ) ) {
				$failed = $this->state_document(
					'failed',
					$action,
					$attachment_ids,
					$automatic,
					(string) $applied->get_error_code(),
					(string) $applied->get_error_message()
				);
				if ( $this->persist_state( $run_id, $failed ) ) {
					$this->clear_retry( $run_id, $automatic );
				}
				return $applied;
			}

			$changed = ! empty( $applied['changed'] );
			$this->apply_attachment_copy( $fresh, $target, $attachment_ids );
			$applied_state = $this->state_document( 'applied', $action, $attachment_ids, $automatic );
			if ( ! $this->persist_state( $run_id, $applied_state ) ) {
				return new \WP_Error(
					'fotogenic_ai_result_delivery_unavailable',
					__( 'The destination was updated, but the result state still needs reconciliation.', 'fotogenic-ai' ),
					[ 'status' => 503 ]
				);
			}
			$this->clear_retry( $run_id, $automatic );
			$this->emit_applied( $fresh, $action, $attachment_ids, $changed );

			return $this->success_response( $fresh, $action, $changed );
		} finally {
			if ( false !== $target_token && '' !== $target_lock_key ) {
				$locks->release_option( $target_lock_key, $target_token );
			}
			$locks->release_option( $lock_key, $token );
		}
	}

	/** @return array{set_featured:bool,add_gallery:bool}|\WP_Error */
	private function action_flags( string $action, array $target, array $attachment_ids ) {
		$capabilities = (array) ( $target['destinationCapabilities'] ?? [] );
		$target_type  = (string) ( $target['targetType'] ?? '' );
		$single       = 1 === count( $attachment_ids );
		if ( in_array( $target_type, [ 'post', 'page' ], true ) ) {
			if ( 'apply_featured' !== $action || ! $single || empty( $capabilities['applyFeatured'] ) ) {
				return $this->invalid_delivery();
			}
			return [
				'set_featured' => true,
				'add_gallery'  => false,
			];
		}
		if ( ! in_array( $target_type, [ 'product', 'variation' ], true ) ) {
			return $this->invalid_delivery();
		}
		$set_featured = in_array( $action, [ 'apply_featured', 'apply_both' ], true );
		$add_gallery  = in_array( $action, [ 'apply_gallery', 'apply_both', 'add_selected_to_gallery', 'add_all_to_gallery' ], true );
		if (
			( in_array( $action, self::ATTACHMENT_ACTIONS, true ) && ! $single )
			|| ( $set_featured && empty( $capabilities['setFeatured'] ) )
			|| ( $add_gallery && ( 'product' !== $target_type || empty( $capabilities['addGallery'] ) ) )
			|| ( 'apply_both' === $action && empty( $capabilities['setFeaturedAndGallery'] ) )
		) {
			return $this->invalid_delivery();
		}

		return compact( 'set_featured', 'add_gallery' );
	}

	private function target_from_row( array $row ) {
		$run_target = sanitize_key( (string) ( $row['run_target'] ?? '' ) );
		$workflow   = sanitize_key( (string) ( $row['workflow'] ?? '' ) );
		$target = Workflow_Item_Identity::normalize_target_snapshot(
			$this->decode_object( $row['target_snapshot'] ?? null )
		);
		if (
			is_wp_error( $target )
			|| ! in_array( $run_target, [ 'product', 'post', 'page' ], true )
			|| ( 'product' === $run_target && ! in_array( $workflow, [ 'product_images', 'campaign_set' ], true ) )
			|| ( in_array( $run_target, [ 'post', 'page' ], true ) && 'product_images' !== $workflow )
			|| $run_target !== (string) ( $target['sourceType'] ?? '' )
			|| (int) ( $target['targetId'] ?? 0 ) !== (int) ( $row['subject_id'] ?? 0 )
		) {
			return $this->invalid_delivery();
		}
		return $target;
	}

	/** @return int[] */
	private function owned_attachment_ids( array $row ): array {
		$job_ids = $this->normalize_job_ids( $row['job_ids'] ?? [] );
		$map = Generate_Result_Authority::validated_attachment_map( $row );
		$ordered = [];
		foreach ( $job_ids as $job_id ) {
			foreach ( Generate_Output_Map::for_job( $map, $job_id ) as $attachment_id ) {
				if ( $attachment_id > 0 && ! in_array( $attachment_id, $ordered, true ) ) {
					$ordered[] = $attachment_id;
				}
			}
		}
		return $ordered;
	}

	/** @return int[] */
	private function admitted_attachment_ids( array $row ): array {
		$job_ids = $this->normalize_job_ids( $row['job_ids'] ?? [] );
		$map     = Generate_Result_Authority::admitted_attachment_map( $row );
		$ordered = [];
		foreach ( $job_ids as $job_id ) {
			foreach ( Generate_Output_Map::for_job( $map, $job_id ) as $attachment_id ) {
				if ( $attachment_id > 0 && ! in_array( $attachment_id, $ordered, true ) ) {
					$ordered[] = $attachment_id;
				}
			}
		}
		return $ordered;
	}

	private function attachment_validation_failure( string $run_id, string $action, array $attachment_ids, bool $automatic ): \WP_Error {
		$error = new \WP_Error(
			'fotogenic_ai_result_delivery_attachment_invalid',
			__( 'One or more saved result attachments can no longer be verified.', 'fotogenic-ai' ),
			[ 'status' => 409 ]
		);
		$failed = $this->state_document(
			'failed',
			$action,
			$attachment_ids,
			$automatic,
			(string) $error->get_error_code(),
			(string) $error->get_error_message()
		);
		if ( $this->persist_state( $run_id, $failed ) ) {
			$this->clear_retry( $run_id, $automatic );
		}
		return $error;
	}

	private function owner_can_edit_target( int $owner_id, array $target ): bool {
		$target_id   = absint( $target['targetId'] ?? 0 );
		$target_type = sanitize_key( (string) ( $target['targetType'] ?? '' ) );
		if ( $target_id <= 0 ) {
			return false;
		}
		if ( in_array( $target_type, [ 'product', 'variation' ], true ) ) {
			// phpcs:ignore WordPress.WP.Capabilities.Unknown -- WooCommerce maps this meta capability for exact Product and variation posts.
			return user_can( $owner_id, 'edit_product', $target_id );
		}
		return in_array( $target_type, [ 'post', 'page' ], true ) && user_can( $owner_id, 'edit_post', $target_id );
	}

	/** @return array{changed:bool}|\WP_Error */
	private function apply_to_target( array $row, array $target, array $attachment_ids, array $flags ) {
		$target_type = sanitize_key( (string) ( $target['targetType'] ?? '' ) );
		if ( in_array( $target_type, [ 'product', 'variation' ], true ) ) {
			if ( ! $this->media_write_adapter instanceof ProductMediaWriteAdapter ) {
				return new \WP_Error(
					'fotogenic_ai_result_delivery_unavailable',
					__( 'The Product destination is not currently available.', 'fotogenic-ai' ),
					[ 'status' => 503 ]
				);
			}
			return $this->media_write_adapter->apply_to_target(
				$target,
				$attachment_ids,
				(bool) ( $flags['set_featured'] ?? false ),
				(bool) ( $flags['add_gallery'] ?? false )
			);
		}

		$target_id  = absint( $target['targetId'] ?? 0 );
		$run_target = sanitize_key( (string) ( $row['run_target'] ?? '' ) );
		$post       = get_post( $target_id );
		if (
			! is_object( $post )
			|| $target_type !== $run_target
			|| $target_type !== sanitize_key( (string) ( $post->post_type ?? '' ) )
			|| true !== (bool) ( $flags['set_featured'] ?? false )
			|| ! empty( $flags['add_gallery'] )
			|| 1 !== count( $attachment_ids )
		) {
			return $this->invalid_delivery();
		}

		$attachment_id = absint( $attachment_ids[0] );
		$changed       = $attachment_id !== (int) get_post_thumbnail_id( $target_id );
		if ( $changed && ! set_post_thumbnail( $target_id, $attachment_id ) ) {
			return new \WP_Error(
				'fotogenic_ai_result_delivery_invalid',
				__( 'The featured image could not be updated.', 'fotogenic-ai' ),
				[ 'status' => 409 ]
			);
		}
		if ( $changed ) {
			clean_post_cache( $target_id );
		}
		return [ 'changed' => $changed ];
	}

	/** @return string[] */
	private function normalize_job_ids( $value ): array {
		if ( is_string( $value ) ) {
			$decoded = json_decode( $value, true );
			$value   = is_array( $decoded ) ? $decoded : [];
		}
		if ( ! is_array( $value ) ) {
			return [];
		}
		$normalized = [];
		foreach ( $value as $job_id ) {
			$job_id = Upstream_Identifier::normalize( $job_id );
			if ( '' !== $job_id ) {
				$normalized[] = $job_id;
			}
		}
		return array_values( array_unique( $normalized ) );
	}

	private function same_run_identity( array $expected, array $actual ): bool {
		return ! empty( $actual )
			&& hash_equals( (string) ( $expected['run_id'] ?? '' ), (string) ( $actual['run_id'] ?? '' ) )
			&& (int) ( $expected['user_id'] ?? 0 ) === (int) ( $actual['user_id'] ?? 0 )
			&& hash_equals( (string) ( $expected['request_id'] ?? '' ), (string) ( $actual['request_id'] ?? '' ) )
			&& sanitize_key( (string) ( $expected['workflow'] ?? '' ) ) === sanitize_key( (string) ( $actual['workflow'] ?? '' ) )
			&& sanitize_key( (string) ( $expected['run_target'] ?? '' ) ) === sanitize_key( (string) ( $actual['run_target'] ?? '' ) )
			&& (int) ( $expected['subject_id'] ?? 0 ) === (int) ( $actual['subject_id'] ?? 0 );
	}

	/** @return array<string, mixed> */
	private function state_document(
		string $status,
		string $action,
		array $attachment_ids,
		bool $automatic,
		string $error_code = '',
		string $error_message = ''
	): array {
		return [
			'version'             => self::STATE_VERSION,
			'status'              => $status,
			'action'              => $action,
			'attachmentIds'       => self::normalize_attachment_ids( $attachment_ids ),
			'appliedAttachmentId' => in_array( $action, self::ATTACHMENT_ACTIONS, true ) ? absint( $attachment_ids[0] ?? 0 ) : 0,
			'automatic'           => $automatic,
			'errorCode'           => substr( sanitize_key( $error_code ), 0, 64 ),
			'errorMessage'        => substr( sanitize_text_field( $error_message ), 0, 500 ),
			'updatedAt'           => gmdate( DATE_ATOM ),
		];
	}

	private function persist_state( string $run_id, array $state ): bool {
		$encoded = wp_json_encode( $state, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
		if ( ! is_string( $encoded ) ) {
			return false;
		}
		Logger::instance()->update_run( $run_id, [ 'delivery_state' => $encoded ] );
		$fresh = self::normalize_state( Logger::instance()->get_run_row( $run_id )['delivery_state'] ?? null );
		return ! empty( $fresh )
			&& hash_equals( (string) $state['status'], (string) $fresh['status'] )
			&& hash_equals( (string) $state['action'], (string) $fresh['action'] )
			&& (array) $state['attachmentIds'] === (array) $fresh['attachmentIds'];
	}

	/** @return array<string, mixed> */
	private static function normalize_state( $value ): array {
		if ( is_string( $value ) ) {
			$trimmed = trim( $value );
			$decoded = json_decode( $trimmed, true );
			$value   = is_array( $decoded ) && fotogenic_ai_str_starts_with( $trimmed, '{' ) ? $decoded : null;
		}
		if ( ! is_array( $value ) || self::STATE_VERSION !== (int) ( $value['version'] ?? 0 ) ) {
			return [];
		}
		$status = sanitize_key( (string) ( $value['status'] ?? '' ) );
		$action = sanitize_key( (string) ( $value['action'] ?? '' ) );
		$attachment_ids = self::normalize_attachment_ids( $value['attachmentIds'] ?? [] );
		if ( ! in_array( $status, [ 'applying', 'applied', 'failed' ], true ) || ! in_array( $action, self::ACTIONS, true ) || empty( $attachment_ids ) ) {
			return [];
		}
		$applied_attachment_id = absint( $value['appliedAttachmentId'] ?? 0 );
		if ( in_array( $action, self::ATTACHMENT_ACTIONS, true ) && ! in_array( $applied_attachment_id, $attachment_ids, true ) ) {
			return [];
		}
		if ( ! in_array( $action, self::ATTACHMENT_ACTIONS, true ) ) {
			$applied_attachment_id = 0;
		}
		return [
			'version'             => self::STATE_VERSION,
			'status'              => $status,
			'action'              => $action,
			'attachmentIds'       => $attachment_ids,
			'appliedAttachmentId' => $applied_attachment_id,
			'automatic'           => true === ( $value['automatic'] ?? false ),
			'errorCode'           => substr( sanitize_key( (string) ( $value['errorCode'] ?? '' ) ), 0, 64 ),
			'errorMessage'        => substr( sanitize_text_field( (string) ( $value['errorMessage'] ?? '' ) ), 0, 500 ),
			'updatedAt'           => substr( sanitize_text_field( (string) ( $value['updatedAt'] ?? '' ) ), 0, 64 ),
		];
	}

	/**
	 * Revalidate a durable Applied marker against the exact current destination.
	 */
	private static function applied_state_matches_destination( array $row, array $state ): bool {
		$target = self::presentation_target_from_row( $row );
		if ( empty( $target ) ) {
			return false;
		}

		$action        = sanitize_key( (string) ( $state['action'] ?? '' ) );
		$attachment_ids = self::normalize_attachment_ids( $state['attachmentIds'] ?? [] );
		$attachment_id = absint( $state['appliedAttachmentId'] ?? 0 );
		$target_id     = absint( $target['targetId'] ?? 0 );
		$target_type   = sanitize_key( (string) ( $target['targetType'] ?? '' ) );
		if ( $target_id <= 0 || empty( $attachment_ids ) ) {
			return false;
		}

		if ( in_array( $target_type, [ 'post', 'page' ], true ) ) {
			$post = get_post( $target_id );
			return 'apply_featured' === $action
				&& is_object( $post )
				&& $target_type === sanitize_key( (string) ( $post->post_type ?? '' ) )
				&& $attachment_id > 0
				&& $attachment_id === (int) get_post_thumbnail_id( $target_id );
		}

		if (
			! in_array( $target_type, [ 'product', 'variation' ], true )
			|| ! fotogenic_ai_woocommerce_available()
			|| ! function_exists( 'wc_get_product' )
		) {
			return false;
		}
		$product = wc_get_product( $target_id );
		if ( ! is_object( $product ) || ! method_exists( $product, 'get_image_id' ) || ! method_exists( $product, 'get_gallery_image_ids' ) ) {
			return false;
		}
		$featured_id = (int) $product->get_image_id( 'edit' );
		$gallery_ids = 'variation' === $target_type
			? []
			: self::normalize_attachment_ids( (array) $product->get_gallery_image_ids( 'edit' ) );
		$featured_matches = $attachment_id > 0 && $attachment_id === $featured_id;
		$gallery_matches  = 'product' === $target_type && empty( array_diff( $attachment_ids, $gallery_ids ) );

		if ( 'apply_featured' === $action ) {
			return $featured_matches;
		}
		if ( in_array( $action, [ 'apply_gallery', 'add_selected_to_gallery', 'add_all_to_gallery' ], true ) ) {
			return $gallery_matches;
		}
		return 'apply_both' === $action && $featured_matches && $gallery_matches;
	}

	/** @return array<string, mixed> */
	private static function presentation_target_from_row( array $row ): array {
		$raw = $row['target_snapshot'] ?? null;
		if ( is_string( $raw ) ) {
			$trimmed = trim( $raw );
			$decoded = json_decode( $trimmed, true );
			$raw = is_array( $decoded ) && fotogenic_ai_str_starts_with( $trimmed, '{' ) ? $decoded : [];
		}
		$target = Workflow_Item_Identity::normalize_target_snapshot( $raw );
		if ( is_wp_error( $target ) ) {
			return [];
		}
		$run_target = sanitize_key( (string) ( $row['run_target'] ?? '' ) );
		return $run_target === sanitize_key( (string) ( $target['sourceType'] ?? '' ) )
			&& absint( $row['subject_id'] ?? 0 ) === absint( $target['targetId'] ?? 0 )
			? $target
			: [];
	}

	/** @return int[] */
	private static function normalize_attachment_ids( $value ): array {
		if ( ! is_array( $value ) ) {
			return [];
		}
		return array_values( array_unique( array_filter( array_map( 'absint', $value ) ) ) );
	}

	private function schedule_retry( string $run_id, bool $automatic ): bool {
		if ( ! Background_Runtime_State::is_enabled() ) {
			return false;
		}
		$args = [ $run_id, $automatic ? 'automatic' : 'manual' ];
		if ( false !== wp_next_scheduled( self::HOOK_RETRY, $args ) ) {
			if ( Background_Runtime_State::is_enabled() ) {
				return true;
			}
			$this->clear_retry( $run_id, $automatic );
			return false;
		}
		$scheduled = true === wp_schedule_single_event( time() + self::RETRY_DELAY, self::HOOK_RETRY, $args );
		if ( ! Background_Runtime_State::is_enabled() ) {
			$this->clear_retry( $run_id, $automatic );
			return false;
		}
		return $scheduled;
	}

	private function clear_retry( string $run_id, bool $automatic ): void {
		wp_clear_scheduled_hook( self::HOOK_RETRY, [ $run_id, $automatic ? 'automatic' : 'manual' ] );
	}

	private function apply_attachment_copy( array $row, array $target, array $attachment_ids ): void {
		if ( ! class_exists( Media_Saver::class ) ) {
			return;
		}
		$run_target = sanitize_key( (string) ( $row['run_target'] ?? '' ) );
		$context = [
			'run_target'        => $run_target,
			'subject_id'        => (int) $target['targetId'],
			'product_id'        => 'product' === $run_target ? (int) $target['targetId'] : 0,
			'product_title'     => (string) ( $target['label'] ?? $row['product_title'] ?? '' ),
			'scene_description' => '',
		];
		$copy = Media_Saver::instance()->build_copy_for_context( $context );
		foreach ( $attachment_ids as $attachment_id ) {
			Media_Saver::apply_generated_attachment_meta( (int) $attachment_id, $copy );
		}
		if ( class_exists( Studio_Cache_Store::class ) ) {
			Studio_Cache_Store::flush_all_caches();
		}
	}

	private function emit_applied( array $row, string $action, array $attachment_ids, bool $changed ): void {
		if ( ! $changed || ! function_exists( 'do_action' ) ) {
			return;
		}
		$bulk_run_uuid = strtolower( trim( (string) ( $row['bulk_run_uuid'] ?? '' ) ) );
		$payload = [
			'workflow'      => sanitize_key( (string) ( $row['workflow'] ?? '' ) ),
			'sourceType'    => sanitize_key( (string) ( $row['run_target'] ?? '' ) ),
			'scope'         => '' !== $bulk_run_uuid ? 'bulk' : 'single',
			'subjectId'     => absint( $row['subject_id'] ?? 0 ),
			'action'        => $action,
			'attachmentIds' => $attachment_ids,
			'changed'       => true,
		];
		if ( '' !== $bulk_run_uuid ) {
			$payload['bulkRunUuid'] = $bulk_run_uuid;
			$payload['bulkItemId']  = absint( $row['bulk_item_id'] ?? 0 );
		}
		do_action( 'fotogenic_ai_result_applied', $payload );
	}

	/** @return array{status:string,action:string,changed:bool,editUrl:string} */
	private function success_response( array $row, string $action, bool $changed ): array {
		return [
			'status'  => 'applied',
			'action'  => $action,
			'changed' => $changed,
			'editUrl' => (string) ( get_edit_post_link( (int) ( $row['subject_id'] ?? 0 ), 'raw' ) ?: '' ),
		];
	}

	/** @return array<string, mixed> */
	private function decode_object( $value ): array {
		if ( is_string( $value ) ) {
			$trimmed = trim( $value );
			$decoded = json_decode( $trimmed, true );
			return is_array( $decoded ) && fotogenic_ai_str_starts_with( $trimmed, '{' ) ? $decoded : [];
		}
		return is_array( $value ) && ( empty( $value ) || ! fotogenic_ai_array_is_list( $value ) ) ? $value : [];
	}

	private function invalid_delivery(): \WP_Error {
		return new \WP_Error(
			'fotogenic_ai_result_delivery_invalid',
			__( 'The saved result destination could not be verified.', 'fotogenic-ai' ),
			[ 'status' => 409 ]
		);
	}

	private static function runtime(): ?self {
		if ( ! class_exists( Plugin::class ) ) {
			return null;
		}
		$adapter = Plugin::instance()->compat()->get_product_media_write_adapter();
		return new self( $adapter instanceof ProductMediaWriteAdapter ? $adapter : null );
	}
}

if ( function_exists( 'add_action' ) ) {
	add_action( 'fotogenic_ai_run_finalized', [ Result_Delivery_Coordinator::class, 'handle_finalized_run' ], 35, 2 );
	add_action( Result_Delivery_Coordinator::HOOK_RETRY, [ Result_Delivery_Coordinator::class, 'handle_scheduled_retry' ], 10, 2 );
}
