<?php

namespace Limb_Chatbot\Includes\Services;

use Exception;
use Limb_Chatbot\Includes\Data_Objects\Chatbot;
use Limb_Chatbot\Includes\Data_Objects\Vector_Index;
use Limb_Chatbot\Includes\Data_Objects\Vector_Index_Meta;
use Limb_Chatbot\Includes\Data_Objects\Vector_Index_Vector;
use Limb_Chatbot\Includes\Repositories\Vector_Index_Repository;
use Limb_Chatbot\Includes\Repositories\Vector_Repository;
use Limb_Chatbot\Includes\Utilities\Vector_Db_Utility;

/**
 * Class Vector_Index_Service
 *
 * Service for managing vector indexes, including creation and deletion.
 *
 * @since 1.0.0
 */
class Vector_Index_Service {

	/**
	 * Repository for vector index data operations.
	 *
	 * @var Vector_Index_Repository
	 * @since 1.0.0
	 */
	public Vector_Index_Repository $repository;

	/**
	 * Vector Service for vector related operations
	 *
	 * @var Vector_Service
	 * @since 1.0.0
	 */
	public Vector_Service $vector_service;

	/**
	 * Vector_Index_Service constructor.
	 *
	 * @param  Vector_Index_Repository  $repository
	 *
	 * @since 1.0.0
	 */
	public function __construct( Vector_Index_Repository $repository ) {
		$this->repository     = $repository;
		$this->vector_service = new Vector_Service( new Vector_Repository() );
	}

	/**
	 * Creates a new vector index with associated metadata and external index.
	 *
	 * @param  array  $data  Vector index data including 'metas'.
	 *
	 * @return Vector_Index Created vector index instance.
	 * @throws Exception
	 * @since 1.0.0
	 */
	public function create( array $data ) {
		$vector_index = $this->repository->make( $data );
		foreach ( $data['metas'] as $meta_data ) {
			$metas[] = Vector_Index_Meta::make( $meta_data );
		}
		if ( $vector_index->get_config_id() ) {
			$utility        = ( new Vector_Db_Utility( $vector_index, $metas ?? [] ) );
			$external_metas = $utility->create_index();
			$vector_index->set_vector_db_id( $utility->get_id() );
			$metas = array_merge( $metas ?? [], $external_metas->items );
		}
		$vector_index->save();
		foreach ( $metas ?? [] as $meta ) {
			$meta->set_vector_index_id( $vector_index->get_id() );
			$meta->save();
		}
		if ( ! empty( $metas ) ) {
			$vector_index->included['metas'] = $metas;
		}

		return $vector_index;
	}

	/**
	 * Deletes a vector index along with its associated vectors and metadata.
	 * If related vectors exist, then runs the job to remove the vectors, otherwise removes only index.
	 *
	 * @param  int  $vector_index_id  ID of the vector index to delete.
	 *
	 * @return bool True on successful deletion, false otherwise.
	 * @throws \Limb_Chatbot\Includes\Exceptions\Exception
	 * @since 1.0.0
	 */
	public function delete( int $vector_index_id ) {
		if ( ! $vector_index = Vector_Index::find( $vector_index_id ) ) {
			return true;
		}
		if ( $vector_index->get_config_id() ) {
			$removed_externally = ( new Vector_Db_Utility( $vector_index ) )->delete_index();
		}
		if ( ! isset( $removed_externally ) || $removed_externally ) {
			Vector_Index_Meta::delete( [ 'vector_index_id' => $vector_index->get_id() ] );

			return $this->repository->delete( $vector_index_id );
		}

		return false;
	}

	public function remove( int $vector_index_id ) {
		if ( ! $vector_index = Vector_Index::find( $vector_index_id ) ) {
			return true;
		}
		if ( $vector_index->get_config_id() ) {
			$removed_externally = ( new Vector_Db_Utility( $vector_index ) )->delete_index();
		}
		if ( ! isset( $removed_externally ) || $removed_externally ) {
			$chatbot_uuid = $vector_index->find_meta( 'chatbot_uuid' );
			if ( $chatbot_uuid instanceof Vector_Index_Meta ) {
				$chatbot_uuid = $chatbot_uuid->get_meta_value();
			}
			$chatbot = ! empty( $chatbot_uuid ) ? Chatbot::find_by_uuid( $chatbot_uuid ) : Chatbot::make();
			if ( ! empty( $chatbot ) && $chatbot instanceof Chatbot ) {
				$kb_vector_ids = $chatbot->get_parameter( 'kb_vector_index_ids' );
				foreach ( $kb_vector_ids as $key => $value ) {
					if ( $value == $vector_index->get_id() ) {
						unset( $kb_vector_ids[ $key ] );
					}
				}
				$chatbot->update_parameter( 'kb_vector_index_ids', array_unique( $kb_vector_ids ) );
			}
			Vector_Index_Vector::delete( [ 'vector_index_id' => $vector_index->get_id() ] );
			Vector_Index_Meta::delete( [ 'vector_index_id' => $vector_index->get_id() ] );

			return $this->repository->delete( $vector_index_id );
		}

		return true;
	}
}