<?php

namespace Fotogenic_AI;

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

final class Service_Presets {
	/**
	 * List strict universal recipe presets for one route.
	 *
	 * @return array|\WP_Error
	 */
	public function list_universal_presets( string $workflow, string $source, string $scope ) {
		$valid = $this->validate_universal_route( $workflow, $source, $scope );
		if ( is_wp_error( $valid ) ) {
			return $valid;
		}
		$result = Presets::instance()->list_universal_presets( $workflow, $source, $scope );
		if ( is_wp_error( $result ) ) {
			return $result;
		}

		return $result;
	}

	/**
	 * Read one strict universal recipe preset.
	 *
	 * @return array|\WP_Error
	 */
	public function get_universal_preset(
		string $preset_uuid,
		string $workflow,
		string $source,
		string $scope
	) {
		$valid = $this->validate_universal_route( $workflow, $source, $scope );
		if ( is_wp_error( $valid ) ) {
			return $valid;
		}
		$snapshot = Presets::instance()->get_universal_preset_data( $preset_uuid, $workflow, $source, $scope );
		if ( is_wp_error( $snapshot ) ) {
			return $snapshot;
		}

		return [
			'preset' => [
				'uuid'     => sanitize_text_field( $preset_uuid ),
				'snapshot' => $this->present_snapshot( $snapshot ),
			],
		];
	}

	/**
	 * Validate and save one JSON-authentic universal snapshot.
	 *
	 * @param mixed $raw Raw value.
	 * @return array|\WP_Error
	 */
	public function save_universal_preset( $raw ) {
		$members = $this->object_members( $raw );
		if ( is_wp_error( $members ) ) {
			return $members;
		}
		if ( ! $this->has_exact_fields( $members, [ 'presetUuid', 'name', 'snapshot' ] ) ) {
			return $this->invalid_request();
		}
		if ( ! is_string( $members['presetUuid'] ) || ! is_string( $members['name'] ) || ! ( $members['snapshot'] instanceof \stdClass ) ) {
			return $this->invalid_request();
		}

		$saved = Presets::instance()->save_universal_preset(
			$members['presetUuid'],
			$members['name'],
			$members['snapshot'],
			get_current_user_id()
		);
		if ( is_wp_error( $saved ) ) {
			return $saved;
		}
		$snapshot = $saved['snapshot'];
		$list     = Presets::instance()->list_universal_presets(
			$snapshot['workflow'],
			$snapshot['sourceType'],
			$snapshot['scope']
		);
		if ( is_wp_error( $list ) ) {
			return $list;
		}

		return [
			'presets'         => $list['presets'],
			'truncated'       => $list['truncated'],
			'savedPresetUuid' => $saved['uuid'],
			'snapshot'        => $this->present_snapshot( $snapshot ),
		];
	}

	/**
	 * Delete only a preset compatible with the submitted universal route.
	 *
	 * @param mixed $raw Raw value.
	 * @return array|\WP_Error
	 */
	public function delete_universal_preset( $raw ) {
		$members = $this->object_members( $raw );
		if ( is_wp_error( $members ) ) {
			return $members;
		}
		if ( ! $this->has_exact_fields( $members, [ 'presetUuid', 'workflow', 'sourceType', 'scope' ] ) ) {
			return $this->invalid_request();
		}
		foreach ( $members as $value ) {
			if ( ! is_string( $value ) || '' === $value ) {
				return $this->invalid_request();
			}
		}
		$valid = $this->validate_universal_route( $members['workflow'], $members['sourceType'], $members['scope'] );
		if ( is_wp_error( $valid ) ) {
			return $valid;
		}
		$deleted = Presets::instance()->delete_universal_preset(
			$members['presetUuid'],
			$members['workflow'],
			$members['sourceType'],
			$members['scope']
		);
		if ( is_wp_error( $deleted ) ) {
			return $deleted;
		}
		if ( ! $deleted ) {
			return new \WP_Error(
				'fotogenic_ai_preset_delete_failed',
				__( 'The preset could not be removed.', 'fotogenic-ai' ),
				[ 'status' => 500 ]
			);
		}
		$list = Presets::instance()->list_universal_presets(
			$members['workflow'],
			$members['sourceType'],
			$members['scope']
		);
		if ( is_wp_error( $list ) ) {
			return $list;
		}

		return [
			'presets'           => $list['presets'],
			'truncated'         => $list['truncated'],
			'deletedPresetUuid' => $members['presetUuid'],
		];
	}

	/**
	 * @return bool|\WP_Error
	 */
	private function validate_universal_route( string $workflow, string $source, string $scope ) {
		$definition = Workflow_Registry::get( $workflow );
		if ( null === $definition || ! $definition->supports( $source, $scope ) ) {
			return new \WP_Error(
				'fotogenic_ai_incompatible_preset',
				__( 'This preset route is not supported.', 'fotogenic-ai' ),
				[ 'status' => 409 ]
			);
		}

		return true;
	}

	/**
	 * @param mixed $raw Raw value.
	 * @return array|\WP_Error
	 */
	private function object_members( $raw ) {
		return $raw instanceof \stdClass ? get_object_vars( $raw ) : $this->invalid_request();
	}

	/**
	 * @param string[] $fields Exact JSON field names.
	 */
	private function has_exact_fields( array $members, array $fields ): bool {
		return count( $members ) === count( $fields )
			&& empty( array_diff( array_keys( $members ), $fields ) )
			&& empty( array_diff( $fields, array_keys( $members ) ) );
	}

	private function invalid_request(): \WP_Error {
		return new \WP_Error(
			'fotogenic_ai_invalid_preset_request',
			__( 'The preset request is invalid.', 'fotogenic-ai' ),
			[ 'status' => 400 ]
		);
	}

	/**
	 * Preserve an empty recipe as a JSON object at the browser boundary.
	 */
	private function present_snapshot( array $snapshot ): array {
		if ( [] === ( $snapshot['recipe'] ?? null ) ) {
			$snapshot['recipe'] = new \stdClass();
		}

		return $snapshot;
	}
}
