<?php

namespace Fotogenic_AI;

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

/**
 * Server-owned exact target and immutable run-item identity contract.
 */
final class Workflow_Item_Identity {
	public const SCHEMA_VERSION = 1;
	private const SOURCE_TYPES = [ 'product', 'media', 'post', 'page' ];
	private const TARGET_TYPES = [ 'product', 'variation', 'media', 'post', 'page' ];
	private const TARGET_FIELDS = [
		'schemaVersion',
		'sourceType',
		'targetType',
		'targetId',
		'parentProductId',
		'variationId',
		'sku',
		'label',
		'eligible',
		'ineligibilityReason',
		'destinationCapabilities',
	];
	private const CAPABILITY_FIELDS = [
		'setFeatured',
		'addGallery',
		'setFeaturedAndGallery',
		'applyFeatured',
	];

	private function __construct() {}

	/**
	 * Resolve an exact, authorized WordPress/WooCommerce target snapshot.
	 *
	 * @return array|\WP_Error
	 */
	public static function resolve_target( string $source_type, int $target_id ) {
		$source_type = sanitize_key( $source_type );
		if ( ! in_array( $source_type, self::SOURCE_TYPES, true ) || $target_id <= 0 ) {
			return self::invalid_target();
		}

		if ( 'product' === $source_type ) {
			return self::resolve_product_target( $target_id );
		}

		if ( ! current_user_can( 'edit_post', $target_id ) ) {
			return Messages::error( 'forbidden', 403 );
		}
		$post = get_post( $target_id );
		$expected_post_type = 'media' === $source_type ? 'attachment' : $source_type;
		if ( ! ( $post instanceof \WP_Post ) || $expected_post_type !== (string) $post->post_type ) {
			return self::invalid_target();
		}

		return self::normalize_target_snapshot(
			[
				'schemaVersion'          => self::SCHEMA_VERSION,
				'sourceType'             => $source_type,
				'targetType'             => $source_type,
				'targetId'               => $target_id,
				'parentProductId'        => 0,
				'variationId'            => 0,
				'sku'                    => '',
				'label'                  => (string) $post->post_title,
				'eligible'               => true,
				'ineligibilityReason'    => '',
				'destinationCapabilities'=> [
					'setFeatured'          => false,
					'addGallery'           => false,
					'setFeaturedAndGallery'=> false,
					'applyFeatured'        => in_array( $source_type, [ 'post', 'page' ], true ),
				],
			]
		);
	}

	/**
	 * Normalize a persisted exact-target snapshot without resolving mutable data.
	 *
	 * @param mixed $raw Raw value.
	 * @return array|\WP_Error
	 */
	public static function normalize_target_snapshot( $raw ) {
		if ( $raw instanceof \stdClass ) {
			$raw = get_object_vars( $raw );
		}
		if ( ! is_array( $raw ) || fotogenic_ai_array_is_list( $raw ) || self::TARGET_FIELDS !== array_keys( $raw ) ) {
			return self::invalid_target();
		}

		$schema_version = $raw['schemaVersion'] ?? null;
		$source_type = is_string( $raw['sourceType'] ?? null ) ? sanitize_key( $raw['sourceType'] ) : '';
		$target_type = is_string( $raw['targetType'] ?? null ) ? sanitize_key( $raw['targetType'] ) : '';
		$target_id = $raw['targetId'] ?? null;
		$parent_id = $raw['parentProductId'] ?? null;
		$variation_id = $raw['variationId'] ?? null;
		$eligible = $raw['eligible'] ?? null;
		$capabilities = $raw['destinationCapabilities'] ?? null;
		if ( $capabilities instanceof \stdClass ) {
			$capabilities = get_object_vars( $capabilities );
		}
		if (
			self::SCHEMA_VERSION !== $schema_version
			|| ! in_array( $source_type, self::SOURCE_TYPES, true )
			|| ! in_array( $target_type, self::TARGET_TYPES, true )
			|| ! is_int( $target_id )
			|| $target_id <= 0
			|| ! is_int( $parent_id )
			|| $parent_id < 0
			|| ! is_int( $variation_id )
			|| $variation_id < 0
			|| ! is_string( $raw['sku'] ?? null )
			|| ! is_string( $raw['label'] ?? null )
			|| ! is_bool( $eligible )
			|| ! is_string( $raw['ineligibilityReason'] ?? null )
			|| ! is_array( $capabilities )
			|| self::CAPABILITY_FIELDS !== array_keys( $capabilities )
			|| count( array_filter( $capabilities, 'is_bool' ) ) !== count( self::CAPABILITY_FIELDS )
		) {
			return self::invalid_target();
		}

		if (
			( 'variation' === $target_type && ( 'product' !== $source_type || $variation_id !== $target_id || $parent_id <= 0 ) )
			|| ( 'product' === $target_type && ( 'product' !== $source_type || 0 !== $variation_id || 0 !== $parent_id ) )
			|| ( in_array( $target_type, [ 'media', 'post', 'page' ], true ) && ( $target_type !== $source_type || 0 !== $variation_id || 0 !== $parent_id ) )
		) {
			return self::invalid_target();
		}

		return [
			'schemaVersion'          => self::SCHEMA_VERSION,
			'sourceType'             => $source_type,
			'targetType'             => $target_type,
			'targetId'               => $target_id,
			'parentProductId'        => $parent_id,
			'variationId'            => $variation_id,
			'sku'                    => self::bounded_text( $raw['sku'], 100 ),
			'label'                  => self::bounded_text( $raw['label'], 255 ),
			'eligible'               => $eligible,
			'ineligibilityReason'    => self::bounded_key( $raw['ineligibilityReason'], 64 ),
			'destinationCapabilities'=> [
				'setFeatured'          => $capabilities['setFeatured'],
				'addGallery'           => $capabilities['addGallery'],
				'setFeaturedAndGallery'=> $capabilities['setFeaturedAndGallery'],
				'applyFeatured'        => $capabilities['applyFeatured'],
			],
		];
	}

	/**
	 * Validate a browser hint against a freshly resolved target.
	 * @param mixed $hint Target identity hint.
	 */
	public static function hint_matches( array $target, $hint ): bool {
		if ( $hint instanceof \stdClass ) {
			$hint = get_object_vars( $hint );
		}
		if (
			! is_array( $hint )
			|| [ 'targetType', 'targetId', 'parentProductId', 'variationId' ] !== array_keys( $hint )
			|| ! is_string( $hint['targetType'] ?? null )
			|| ! is_int( $hint['targetId'] ?? null )
			|| ! is_int( $hint['parentProductId'] ?? null )
			|| ! is_int( $hint['variationId'] ?? null )
		) {
			return false;
		}

		return (string) ( $target['targetType'] ?? '' ) === sanitize_key( $hint['targetType'] )
			&& (int) ( $target['targetId'] ?? 0 ) === $hint['targetId']
			&& (int) ( $target['parentProductId'] ?? 0 ) === $hint['parentProductId']
			&& (int) ( $target['variationId'] ?? 0 ) === $hint['variationId'];
	}

	/**
	 * Return the small exact-identity hint allowed in the browser start envelope.
	 */
	public static function target_hint( array $target ): array {
		return [
			'targetType'      => (string) ( $target['targetType'] ?? '' ),
			'targetId'        => (int) ( $target['targetId'] ?? 0 ),
			'parentProductId' => (int) ( $target['parentProductId'] ?? 0 ),
			'variationId'     => (int) ( $target['variationId'] ?? 0 ),
		];
	}

	/**
	 * Create a stable RFC 4122 version-5-shaped UUID from non-secret identifiers.
	 */
	public static function deterministic_uuid( string $namespace, string ...$parts ): string {
		$hex = substr( hash( 'sha256', $namespace . "\0" . implode( "\0", $parts ) ), 0, 32 );
		$hex[12] = '5';
		$hex[16] = dechex( ( hexdec( $hex[16] ) & 0x3 ) | 0x8 );

		return substr( $hex, 0, 8 ) . '-' . substr( $hex, 8, 4 ) . '-' . substr( $hex, 12, 4 ) . '-' . substr( $hex, 16, 4 ) . '-' . substr( $hex, 20, 12 );
	}

	/** @return array|\WP_Error */
	private static function resolve_product_target( int $target_id ) {
		if (
			! function_exists( __NAMESPACE__ . '\\fotogenic_ai_woocommerce_available' )
			|| ! fotogenic_ai_woocommerce_available()
		) {
			return Messages::error( 'woocommerce_not_active', 503 );
		}
		// phpcs:ignore WordPress.WP.Capabilities.Unknown -- WooCommerce maps this meta capability for exact Product and variation posts.
		if ( ! current_user_can( 'edit_product', $target_id ) ) {
			return Messages::error( 'forbidden_product', 403 );
		}

		$product = wc_get_product( $target_id );
		if ( ! ( $product instanceof \WC_Product ) ) {
			return Messages::error( 'missing_product', 404 );
		}
		$is_variation = $product instanceof \WC_Product_Variation || $product->is_type( 'variation' );
		$parent_id = $is_variation && method_exists( $product, 'get_parent_id' ) ? (int) $product->get_parent_id() : 0;
		if ( $is_variation && $parent_id <= 0 ) {
			return self::invalid_target();
		}
		$featured_id = (int) $product->get_image_id( 'edit' );
		$gallery_ids = $is_variation
			? []
			: array_values( array_unique( array_filter( array_map( 'absint', (array) $product->get_gallery_image_ids( 'edit' ) ) ) ) );
		$attachment_ids = array_values( array_unique( array_filter( array_merge( [ $featured_id ], $gallery_ids ) ) ) );
		$eligible             = ! empty( $attachment_ids );
		$ineligibility_reason = $eligible ? '' : 'missing_image';
		if ( $is_variation && 'outofstock' === (string) $product->get_stock_status() ) {
			$eligible              = false;
			$ineligibility_reason = 'out_of_stock';
		}

		return self::normalize_target_snapshot(
			[
				'schemaVersion'          => self::SCHEMA_VERSION,
				'sourceType'             => 'product',
				'targetType'             => $is_variation ? 'variation' : 'product',
				'targetId'               => $target_id,
				'parentProductId'        => $parent_id,
				'variationId'            => $is_variation ? $target_id : 0,
				'sku'                    => (string) $product->get_sku( 'edit' ),
				'label'                  => (string) $product->get_name( 'edit' ),
				'eligible'               => $eligible,
				'ineligibilityReason'    => $ineligibility_reason,
				'destinationCapabilities'=> [
					'setFeatured'          => true,
					'addGallery'           => ! $is_variation,
					'setFeaturedAndGallery'=> ! $is_variation,
					'applyFeatured'        => false,
				],
			]
		);
	}

	private static function bounded_text( string $value, int $maximum ): string {
		$value = sanitize_text_field( $value );
		return function_exists( 'mb_substr' ) ? mb_substr( $value, 0, $maximum ) : substr( $value, 0, $maximum );
	}

	private static function bounded_key( string $value, int $maximum ): string {
		return substr( sanitize_key( $value ), 0, $maximum );
	}

	private static function invalid_target(): \WP_Error {
		return new \WP_Error(
			'fotogenic_ai_invalid_target_identity',
			__( 'The selected target identity could not be verified.', 'fotogenic-ai' ),
			[ 'status' => 409 ]
		);
	}
}
