<?php

namespace Fotogenic_AI;

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

/**
 * Final data-minimization guard for normalized workflow payloads.
 *
 * This guard does not validate a full recipe and cannot dispatch a request. It
 * is the final invariant check that a later canonical validator/request builder
 * must run before producing upstream fields.
 */
final class Workflow_Contract_Guard {
	private const FORBIDDEN_KEYS = [ 'selectionOverrides', 'selectionOverrideKeys' ];

	private function __construct() {}

	/**
	 * Reject internal override metadata and omit empty/automatic aspect ratios.
	 *
	 * @return array|\WP_Error
	 */
	public static function finalize( array $payload ) {
		$valid = self::reject_forbidden( $payload );
		if ( is_wp_error( $valid ) ) {
			return $valid;
		}

		return self::omit_automatic_aspect_ratios( $payload );
	}

	/**
	 * Reject internal override metadata before any type coercion or normalization.
	 *
	 * @param mixed $payload Raw decoded JSON value.
	 * @return bool|\WP_Error
	 */
	public static function reject_forbidden( $payload ) {
		$forbidden = [];
		self::collect_forbidden_keys( $payload, $forbidden );
		if ( empty( $forbidden ) ) {
			return true;
		}

		return new \WP_Error(
			'fotogenic_ai_forbidden_workflow_field',
			__( 'The workflow request contains internal-only fields.', 'fotogenic-ai' ),
			[
				'status' => 400,
				'fields' => array_values( array_unique( $forbidden ) ),
			]
		);
	}

	/**
	 * @param mixed    $value     Nested payload value.
	 * @param string[] $forbidden Located forbidden key names.
	 */
	private static function collect_forbidden_keys( $value, array &$forbidden ): void {
		if ( is_object( $value ) ) {
			$value = get_object_vars( $value );
		}
		if ( ! is_array( $value ) ) {
			return;
		}
		foreach ( $value as $key => $item ) {
			if ( is_string( $key ) && in_array( $key, self::FORBIDDEN_KEYS, true ) ) {
				$forbidden[] = $key;
			}
			self::collect_forbidden_keys( $item, $forbidden );
		}
	}

	private static function omit_automatic_aspect_ratios( array $value ): array {
		$normalized = [];
		foreach ( $value as $key => $item ) {
			if ( 'aspectRatio' === $key && self::aspect_ratio_is_automatic( $item ) ) {
				continue;
			}
			$normalized[ $key ] = is_array( $item )
				? self::omit_automatic_aspect_ratios( $item )
				: $item;
		}

		return $normalized;
	}

	/**
	 * @param mixed $value Aspect ratio value or selection record.
	 */
	private static function aspect_ratio_is_automatic( $value ): bool {
		if ( null === $value || '' === $value || [] === $value ) {
			return true;
		}
		if ( is_string( $value ) ) {
			$value = trim( $value );
			return '' === $value || 'auto' === strtolower( $value );
		}
		if ( is_array( $value ) && array_key_exists( 'id', $value ) ) {
			return self::aspect_ratio_is_automatic( $value['id'] );
		}

		return false;
	}
}
