<?php

namespace Limb_Chatbot\Includes\AI_Providers\Gemini\Utilities;

use Limb_Chatbot\Includes\Data_Objects\Vector;
use Limb_Chatbot\Includes\AI_Providers\Gemini\Endpoints\Embed_Content_Endpoint;
use Limb_Chatbot\Includes\AI_Providers\Gemini\Gemini;
use Limb_Chatbot\Includes\Exceptions\Exception;
use Limb_Chatbot\Includes\Utilities\Embedding_Utility as Global_Embedding_Utility;
use Limb_Chatbot\Includes\Data_Objects\Setting;
use ReflectionClass;

/**
 * Class Embedding_Utility
 *
 * Utility class for handling embedding-related operations for Gemini AI provider.
 *
 * @package Limb_Chatbot\Includes\AI_Providers\Gemini\Utilities
 * @since 1.0.0
 */
class Embedding_Utility {

	/**
	 * Global embedding utility instance.
	 *
	 * @var Global_Embedding_Utility
	 */
	public Global_Embedding_Utility $global_utility;

	/**
	 * Configuration ID.
	 *
	 * @var int|null
	 * @since 1.0.0
	 */
	public ?int $config_id = null;

	/**
	 * AI model ID.
	 *
	 * @var int|null
	 * @since 1.0.0
	 */
	public ?int $ai_model_id = null;

	/**
	 * Timeout in seconds.
	 *
	 * @var int|null
	 * @since 1.0.0
	 */
	public ?int $timeout = 60;

	/**
	 * API version string.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	public string $version = 'v1beta';

	/**
	 * Constructor.
	 *
	 * @param  Global_Embedding_Utility  $global_utility  Global embedding utility instance.
	 *
	 * @since 1.0.0
	 */
	public function __construct( Global_Embedding_Utility $global_utility ) {
		$this->global_utility = $global_utility;
		$this->populate_properties( 'ai_providers.' . Gemini::$id . '.utilities.embedding.' );
	}

	/**
	 * Populate properties from settings using a prefix.
	 *
	 * @param  string  $setting_prefix  Prefix for settings keys.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function populate_properties( string $setting_prefix ) {
		$reflection = new ReflectionClass( $this );
		$properties = $reflection->getProperties();
		foreach ( $properties as $property ) {
			$value = Setting::find( $setting_prefix . $property->getName() )->get_value();
			// Assign value if type is only built in to avoid assignment of non built in properties
			if ( $property->getType() && $property->getType()->isBuiltin() && ! empty( $value ) ) {
				$property->setValue( $this, $value );
			}
		}
	}

	/**
	 * Generate embedding vector via the Embed_Content_Endpoint.
	 *
	 * @return Vector|null The generated vector or null.
	 * @throws Exception
	 * @since 1.0.0
	 */
	public function generate(): ?Vector {
		return ( new Embed_Content_Endpoint( $this ) )->generate( $this->global_utility->get_vector() );
	}

	/**
	 * Get AI model ID.
	 *
	 * @return int|null
	 * @since 1.0.0
	 */
	public function get_ai_model_id(): ?int {
		return $this->ai_model_id;
	}

	/**
	 * Get timeout value.
	 *
	 * @return int|null
	 * @since 1.0.0
	 */
	public function get_timeout(): ?int {
		return $this->timeout;
	}

	/**
	 * Get config ID.
	 *
	 * @return int|null
	 * @since 1.0.0
	 */
	public function get_config_id(): ?int {
		return $this->config_id;
	}

	/**
	 * Get API version.
	 *
	 * @return string
	 * @since 1.0.0
	 */
	public function get_version(): string {
		return $this->version;
	}
}