<?php

namespace Fotogenic_AI;

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

/**
 * Materialize a query-backed Product selection into immutable queue items.
 */
final class Store_Queue_Snapshot_Builder {
	private Store_Queue_Repo $repo;
	private ProductQueryAdapter $products;
	private Service_Bulk $bulk;
	private Workflow_Draft_Normalizer $normalizer;

	public function __construct(
		?Store_Queue_Repo $repo = null,
		?ProductQueryAdapter $products = null,
		?Service_Bulk $bulk = null,
		?Workflow_Draft_Normalizer $normalizer = null
	) {
		$this->repo       = $repo ?? Store_Queue_Repo::instance();
		$this->products   = $products ?? Plugin::instance()->compat()->get_product_query_adapter();
		$this->bulk       = $bulk ?? new Service_Bulk();
		$this->normalizer = $normalizer ?? new Workflow_Draft_Normalizer();
	}

	/**
	 * Prepare at most one bounded page. No method here starts an upstream job.
	 *
	 * @return array|\WP_Error
	 */
	public function prepare_tick( string $queue_uuid, int $user_id ) {
		$queue = $this->repo->get_queue_by_uuid( $queue_uuid, $user_id );
		if ( ! is_array( $queue ) ) {
			return new \WP_Error( 'fotogenic_ai_store_queue_not_found', __( 'The Product queue could not be found.', 'fotogenic-ai' ), [ 'status' => 404 ] );
		}
		if ( ! in_array( (string) $queue['status'], [ 'preparing', 'reviewing' ], true ) ) {
			return $queue;
		}

		$selection = $this->decode_snapshot( $queue['selection_snapshot'] ?? '' );
		$draft     = $this->decode_snapshot( $queue['draft_snapshot'] ?? '' );
		if ( empty( $selection ) || empty( $draft ) ) {
			return $this->pause_invalid_queue( $queue, $user_id, 'invalid_snapshot' );
		}
		$strategy = sanitize_key( (string) ( $selection['strategy'] ?? '' ) );
		if ( ! in_array( $strategy, [ 'all', 'query', 'categories', 'explicit' ], true ) ) {
			return $this->pause_invalid_queue( $queue, $user_id, 'invalid_selection' );
		}

		$after_id     = (int) $queue['scan_after_id'];
		$high_water_id = (int) $queue['scan_high_water_id'];
		if ( 'explicit' === $strategy ) {
			$page = $this->explicit_page( $selection, $after_id );
		} else {
			$page = $this->products->scan_products(
				[
					'search'         => 'all' === $strategy ? '' : (string) ( $selection['search'] ?? '' ),
					'category'       => 'categories' === $strategy || 'query' === $strategy ? (array) ( $selection['categories'] ?? [] ) : [],
					'stock_status'   => 'query' === $strategy ? (array) ( $selection['stock'] ?? [] ) : [],
					'after_id'       => $after_id,
					'high_water_id'  => $high_water_id,
					'limit'          => 100,
				]
			);
		}
		if ( false === (bool) ( $page['available'] ?? false ) ) {
			return $this->pause_invalid_queue( $queue, $user_id, 'product_source_unavailable' );
		}

		$excluded = array_values( array_unique( array_map( 'absint', (array) ( $selection['excludedIds'] ?? [] ) ) ) );
		$prepared = [];
		foreach ( (array) ( $page['items'] ?? [] ) as $summary ) {
			$subject_id = absint( $summary['id'] ?? 0 );
			if ( $subject_id <= 0 || in_array( $subject_id, $excluded, true ) ) {
				continue;
			}
			$prepared[] = $this->prepare_item( $draft, $summary, $subject_id );
		}

		$next_after_id = max( $after_id, (int) ( $page['nextAfterId'] ?? $after_id ) );
		$high_water_id = max( $high_water_id, (int) ( $page['highWaterId'] ?? $high_water_id ) );
		$done          = true === ( $page['done'] ?? false );
		$appended      = $this->repo->append_scan_page(
			(int) $queue['id'],
			$user_id,
			$after_id,
			$prepared,
			$next_after_id,
			$high_water_id,
			$done
		);
		if ( true !== ( $appended['ok'] ?? false ) ) {
			return new \WP_Error(
				'fotogenic_ai_store_queue_prepare_failed',
				__( 'The Product queue could not save its latest preparation page.', 'fotogenic-ai' ),
				[ 'status' => 503, 'reason' => sanitize_key( (string) ( $appended['reason'] ?? '' ) ) ]
			);
		}

		return $appended['queue'];
	}

	/** @return array{items: array, total: int, nextAfterId: int, done: bool, highWaterId: int, available: bool} */
	private function explicit_page( array $selection, int $offset ): array {
		$ids   = array_values( array_unique( array_filter( array_map( 'absint', (array) ( $selection['selectedIds'] ?? [] ) ) ) ) );
		sort( $ids, SORT_NUMERIC );
		$batch = array_slice( $ids, $offset, 100 );
		$items = [];
		foreach ( $batch as $subject_id ) {
			$summary = $this->products->get_product_summary( $subject_id );
			if ( is_array( $summary ) ) {
				$items[] = $summary;
			}
		}
		$next = $offset + count( $batch );
		return [
			'items'       => $items,
			'total'       => count( $ids ),
			'nextAfterId' => $next,
			'done'        => $next >= count( $ids ),
			'highWaterId' => count( $ids ),
			'available'   => true,
		];
	}

	private function prepare_item( array $draft, array $summary, int $subject_id ): array {
		$exact                      = $draft;
		$exact['selection']         = [ 'strategy' => 'explicit', 'selectedIds' => [ $subject_id ], 'excludedIds' => [], 'query' => [] ];
		$exact_object               = $this->normalizer->rehydrate_normalized( $exact );
		$exact                      = is_wp_error( $exact_object ) ? $exact_object : $this->normalizer->normalize( $exact_object );
		$sources                    = is_wp_error( $exact ) ? $exact : $this->bulk->build_exact_source_snapshot( $exact );
		$label                      = fotogenic_ai_plain_text_label( (string) ( $summary['label'] ?? '' ) );
		$base = [
			'subject_type'     => 'product',
			'subject_id'       => $subject_id,
			'search_label'     => $label,
			'sku'              => sanitize_text_field( (string) ( $summary['sku'] ?? '' ) ),
			'summary_snapshot' => $summary,
		];
		if ( is_wp_error( $sources ) || 1 !== count( (array) $sources ) ) {
			$error_code = is_wp_error( $sources ) ? sanitize_key( $sources->get_error_code() ) : 'invalid_source_snapshot';
			return array_merge(
				$base,
				[
					'status'             => 'ineligible',
					'status_reason'      => $error_code,
					'target_snapshot'    => (array) ( $summary['target'] ?? [] ),
					'source_snapshot'    => [],
					'source_fingerprint' => '',
				]
			);
		}

		$source      = array_values( $sources )[0];
		$fingerprint = $this->normalizer->fingerprint(
			[
				'schemaVersion' => 1,
				'subjectType'   => 'product',
				'subjectId'     => $subject_id,
				'target'        => $source['target'] ?? [],
				'attachmentIds' => $source['attachmentIds'] ?? [],
				'sourceRoles'   => $source['sourceRoles'] ?? [],
			]
		);
		if ( is_wp_error( $fingerprint ) ) {
			return array_merge( $base, [ 'status' => 'ineligible', 'status_reason' => 'source_fingerprint_failed', 'target_snapshot' => [], 'source_snapshot' => [], 'source_fingerprint' => '' ] );
		}

		return array_merge(
			$base,
			[
				'status'             => 'ready',
				'status_reason'      => '',
				'target_snapshot'    => (array) ( $source['target'] ?? [] ),
				'source_snapshot'    => $source,
				'source_fingerprint' => $fingerprint,
			]
		);
	}

	/**
	 * Re-resolve one queued Product before it crosses a billable boundary.
	 *
	 * @return array|\WP_Error
	 */
	public function refresh_item( array $draft, int $subject_id ) {
		$summary = $this->products->get_product_summary( $subject_id );
		if ( is_wp_error( $summary ) ) {
			return $summary;
		}
		if ( ! is_array( $summary ) ) {
			return new \WP_Error( 'fotogenic_ai_store_queue_product_missing', __( 'A queued Product is no longer available.', 'fotogenic-ai' ), [ 'status' => 409 ] );
		}
		return $this->prepare_item( $draft, $summary, $subject_id );
	}

	/** @return array<string, mixed> */
	private function decode_snapshot( $value ): array {
		$decoded = is_string( $value ) ? json_decode( $value, true ) : null;
		return is_array( $decoded ) ? $decoded : [];
	}

	/** @return array|\WP_Error */
	private function pause_invalid_queue( array $queue, int $user_id, string $reason ) {
		$this->repo->update_queue_cas(
			(int) $queue['id'],
			$user_id,
			(int) $queue['revision'],
			[ 'status' => 'paused', 'status_reason' => $reason, 'last_error' => __( 'Product queue preparation requires attention.', 'fotogenic-ai' ) ]
		);
		return new \WP_Error( 'fotogenic_ai_store_queue_prepare_paused', __( 'Product queue preparation requires attention.', 'fotogenic-ai' ), [ 'status' => 409, 'reason' => $reason ] );
	}
}
