<?php

namespace Limb_Chatbot\Includes\Vector_Dbs\Pinecone\Endpoints;

use Limb_Chatbot\Includes\Data_Objects\Vector;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Exceptions\Exception;
use Limb_Chatbot\Includes\Services\Helper;

/**
 * Class Vectors_Endpoint
 *
 * Handles upsert and delete operations for vectors in Pinecone vector database.
 *
 * @package Limb_Chatbot\Includes\Vector_Dbs\Pinecone\Endpoints
 *
 * @since 1.0.0
 */
class Vectors_Endpoint extends Pinecone_Endpoint {
	/**
	 * Special role allowing universal access.
	 */
	private const ROLE_EVERYONE = 'everyone';

	/**
	 * Host address for Pinecone API requests.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	protected string $host;

	/**
	 * Upsert a single vector into the Pinecone vector index.
	 *
	 * @param Vector $vector Vector object to upsert.
	 * @return bool True on successful upsert.
	 *
	 * @throws Exception Throws if Pinecone returns an error during upsert.
	 *
	 * @since 1.0.0
	 * @since 1.0.15 Added dimensions support
	 */
	// TODO namespace implementation
	public function upsert( Vector $vector ): bool {
		if ( ! empty( $vector->dimension ) ) {
			$this->utility->set_dimension( $vector->dimension );
		}
		$vector_index = $this->utility->get_global_utility()->get_vector_index();
		$http_client  = $this->http_client_factory();
		$this->define_host( $vector_index );

		$url = 'https://' . $this->host . '/vectors/upsert';
		$payload = [
			'vectors' => [
				[
					'id'       => $vector->get_uuid(),
					'values'   => maybe_unserialize( $vector->get_values() ),
					'metadata' => [
						'internal_id'     => $vector->get_id(),
						'access_roles'    => $vector->get_access_roles(),
						'access_everyone' => in_array( self::ROLE_EVERYONE, (array) $vector->get_access_roles(), true ),
					],
				]
			]
		];

		$response         = $http_client->post( $url, array(
			'headers' => $this->get_header(),
			'timeout' => $this->utility->get_timeout(),
			'body'    => json_encode( $payload )
		) );

		$response_handler = new Vectors\Response_Handler( $response, $http_client, $this );
		if ( ! $response_handler->status_starts_with_2() ) {
			Helper::log($response_handler->get_body());
			throw new Exception( Error_Codes::PINECONE_ERROR, __( 'Unable to upsert the vector', 'limb-chatbot' ), $response_handler->get_body() );
		}

		return true;
	}

	/**
	 * Upsert multiple vectors into the Pinecone vector index in batch.
	 *
	 * @param Vector[] $vectors Array of Vector objects to upsert.
	 * @return bool True on successful batch upsert.
	 *
	 * @throws Exception Throws if Pinecone returns an error during upsert.
	 *
	 * @since 1.0.0
	 * @since 1.0.15 Added dimensions support
	 */
	public function upsert_batch( array $vectors ): bool {
		if ( ! empty( $vectors ) && ! empty( $vectors[0]->dimension ) ) {
			$this->utility->set_dimension( $vectors[0]->dimension );
		}
		$vector_index = $this->utility->get_global_utility()->get_vector_index();
		$http_client  = $this->http_client_factory();
		foreach ( $vectors as $vector ) {
			$body['vectors'][] = [
				'id'       => $vector->get_uuid(),
				'values'   => maybe_unserialize( $vector->get_values() ),
				'metadata' => [
					'internal_id'     => $vector->get_id(),
					'access_roles'    => $vector->get_access_roles(),
					'access_everyone' => in_array( self::ROLE_EVERYONE, (array) $vector->get_access_roles(), true ),
				],
			];
		}
		$this->define_host( $vector_index );

		$url = 'https://' . $this->host . '/vectors/upsert';

		$response = $http_client->post( $url, array(
			'headers' => $this->get_header(),
			'timeout' => $this->utility->get_timeout(),
			'body'    => json_encode( $body ),
		) );

		$response_handler = new Vectors\Response_Handler( $response, $http_client, $this );
		if ( ! $response_handler->status_starts_with_2() ) {
			throw new Exception( Error_Codes::PINECONE_ERROR, __( 'Unable to upsert the vectors', 'limb-chatbot' ), $response_handler->get_body() );
		}

		return true;
	}

	/**
	 * Delete one or more vectors from the Pinecone vector index.
	 *
	 * @param Vector|Vector[] $vectors Single Vector or array of Vector objects to delete.
	 * @return bool True on successful deletion.
	 *
	 * @throws Exception Throws if invalid argument type or Pinecone returns an error during delete.
	 *
	 * @since 1.0.0
	 */
	public function delete( $vectors ): bool {
		$vector_index = $this->utility->get_global_utility()->get_vector_index();
		$http_client  = $this->http_client_factory();
		if ( is_a( $vectors, Vector::class ) ) {
			$vectors = [ $vectors ];
		} elseif ( ! is_array( $vectors ) ) {
			throw new Exception( Error_Codes::DATA_INVALID_VECTOR_ARGUMENT, __( 'Invalid vector argument', 'limb-chatbot' ) );
		}
		foreach ( $vectors as $vector ) {
			$body['ids'][] = $vector->get_uuid();
		}
		$this->define_host( $vector_index );
		$response = $http_client->post( 'https://' . $this->host . '/vectors/delete', array(
			'headers' => $this->get_header(),
			'timeout' => $this->utility->get_timeout(),
			'body'    => json_encode( $body ),
		) );

		$response_handler = new Vectors\Response_Handler( $response, $http_client, $this );
		if ( ! $response_handler->status_code_is_ok() ) {
			throw new Exception( Error_Codes::PINECONE_ERROR, __( 'Unable to delete the vectors', 'limb-chatbot' ), $response_handler->get_body() );
		}

		return true;
	}

}