<?php

namespace Fotogenic_AI;

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

/**
 * Builds the final Generate Images multipart boundary from normalized data.
 */
final class Generate_Images_Multipart_Builder {
	private const MIN_OUTPUTS = 1;
	private const MAX_OUTPUTS = 6;
	private const MAX_PRODUCT_IMAGES = 10;

	private const ALLOWED_INPUTS = [
		'quality',
		'count',
		'style',
		'selections',
		'custom_instructions',
		'include_face',
		'include_body',
		'look_at_camera',
		'product_images',
		'subject_image',
		'environment_image',
		'inspiration_image',
	];

	/**
	 * @param array $command Already-normalized service data and resolved file descriptors.
	 * @return array{fields: array, files: array}|\WP_Error
	 */
	public function build( array $command ) {
		$command = Workflow_Contract_Guard::finalize( $command );
		if ( is_wp_error( $command ) ) {
			return $command;
		}

		$unknown = array_values( array_diff( array_keys( $command ), self::ALLOWED_INPUTS ) );
		if ( ! empty( $unknown ) ) {
			return $this->invalid_payload( $unknown );
		}

		$quality = $command['quality'] ?? null;
		$count   = $command['count'] ?? null;
		if ( ! is_string( $quality ) || ! Workflow_Quality_Catalog::supports( 'product_images', $quality ) || ! is_int( $count ) || $count < self::MIN_OUTPUTS || $count > self::MAX_OUTPUTS ) {
			return $this->invalid_payload();
		}

		$selections = $command['selections'] ?? [];
		if ( ! is_array( $selections ) || ( ! empty( $selections ) && fotogenic_ai_array_is_list( $selections ) ) ) {
			return $this->invalid_payload();
		}

		$instructions = $command['custom_instructions'] ?? '';
		if ( ! is_string( $instructions ) ) {
			return $this->invalid_payload();
		}
		$instructions = trim( $instructions );
		$style = $command['style'] ?? '';
		if ( ! is_string( $style ) || strlen( $style ) > 120 ) {
			return $this->invalid_payload( [ 'style' ] );
		}
		$style = trim( $style );

		$flags = [];
		foreach ( [ 'include_face', 'include_body', 'look_at_camera' ] as $key ) {
			$value = $command[ $key ] ?? null;
			if ( null !== $value && ! is_bool( $value ) ) {
				return $this->invalid_payload( [ $key ] );
			}
			$flags[ $key ] = $value;
		}

		$product_images = $command['product_images'] ?? null;
		if ( ! $this->is_file_list( $product_images, 1, self::MAX_PRODUCT_IMAGES ) ) {
			return $this->invalid_inputs();
		}

		$subject_image = $this->optional_file( $command['subject_image'] ?? null );
		if ( is_wp_error( $subject_image ) ) {
			return $subject_image;
		}
		$environment_image = $this->optional_file( $command['environment_image'] ?? null );
		if ( is_wp_error( $environment_image ) ) {
			return $environment_image;
		}
		$inspiration_image = $this->optional_file( $command['inspiration_image'] ?? null );
		if ( is_wp_error( $inspiration_image ) ) {
			return $inspiration_image;
		}

		$fields = [
			'quality' => $quality,
			'count'   => (string) $count,
		];
		if ( ! empty( $selections ) ) {
			$encoded_selections = wp_json_encode( $selections );
			if ( ! is_string( $encoded_selections ) ) {
				return $this->invalid_payload( [ 'selections' ] );
			}
			$fields['selections'] = $encoded_selections;
		}
		if ( '' !== $instructions ) {
			$fields['customInstructions'] = $instructions;
		}
		if ( '' !== $style ) {
			$fields['style'] = $style;
		}

		$files = [ 'productImages' => $product_images ];
		if ( null !== $subject_image ) {
			$files['subjectImage'] = $subject_image;
			if ( null !== $flags['include_face'] ) {
				$fields['includeFace'] = $flags['include_face'] ? 'true' : 'false';
			}
			if ( null !== $flags['include_body'] ) {
				$fields['includeBody'] = $flags['include_body'] ? 'true' : 'false';
			}
			if ( null !== $flags['look_at_camera'] ) {
				$fields['lookAtCamera'] = $flags['look_at_camera'] ? 'true' : 'false';
			}
		}
		if ( null !== $environment_image ) {
			$files['environmentImage'] = $environment_image;
		}
		if ( null !== $inspiration_image ) {
			$files['inspirationImage'] = $inspiration_image;
		}

		return [
			'fields' => $fields,
			'files'  => $files,
		];
	}

	/**
	 * @param mixed $value Candidate file list.
	 */
	private function is_file_list( $value, int $minimum, int $maximum ): bool {
		if ( ! is_array( $value ) || ! fotogenic_ai_array_is_list( $value ) ) {
			return false;
		}
		$count = count( $value );
		if ( $count < $minimum || $count > $maximum ) {
			return false;
		}

		foreach ( $value as $descriptor ) {
			if ( ! $this->is_file_descriptor( $descriptor ) ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * @param mixed $value Optional single file descriptor.
	 * @return array|null|\WP_Error
	 */
	private function optional_file( $value ) {
		if ( null === $value ) {
			return null;
		}
		if ( ! $this->is_file_descriptor( $value ) ) {
			return $this->invalid_inputs();
		}

		return $value;
	}

	/**
	 * @param mixed $value Resolved file descriptor.
	 */
	private function is_file_descriptor( $value ): bool {
		return is_array( $value )
			&& isset( $value['path'], $value['filename'], $value['mime'], $value['bytes'] );
	}

	/**
	 * @param string[] $fields Invalid internal field names.
	 */
	private function invalid_payload( array $fields = [] ): \WP_Error {
		return new \WP_Error(
			'fotogenic_ai_invalid_generate_multipart',
			__( 'The Generate Images request could not be prepared safely.', 'fotogenic-ai' ),
			[
				'status' => 400,
				'fields' => $fields,
			]
		);
	}

	private function invalid_inputs(): \WP_Error {
		return new \WP_Error(
			'fotogenic_ai_invalid_generate_inputs',
			__( 'Generate Images requires one to ten resolved product images.', 'fotogenic-ai' ),
			[ 'status' => 400 ]
		);
	}
}
