<?php

namespace Limb_Chatbot\Includes\Data_Objects;

use Exception;
use Limb_Chatbot\Includes\Repositories\Vector_Index_Repository;
use Limb_Chatbot\Includes\Services\Data_Object_Collection;

/**
 * Class Vector_Index
 *
 * Represents a vector index definition in the system, linking to configuration and metadata.
 *
 * @since 1.0.0
 */
class Vector_Index extends WPDB_Data_Object {

	/**
	 * List of fillable columns for database operations.
	 *
	 * @since 1.0.0
	 */
	const FILLABLE = array( 'name', 'description', 'vector_db_id', 'config_id', 'created_at', 'updated_at' );

	/**
	 * Table name used in the database.
	 *
	 * @since 1.0.0
	 */
	const TABLE_NAME = 'lbaic_vector_indexes';

	/**
	 * @var string Index name.
	 * @since 1.0.0
	 */
	public string $name;

	/**
	 * @var string|null Description for the vector index.
	 * @since 1.0.0
	 */
	public ?string $description = null;

	/**
	 * @var string|null Vector database ID (foreign reference).
	 * @since 1.0.0
	 */
	public ?string $vector_db_id = null;

	/**
	 * @var int|null Configuration ID this index is associated with.
	 * @since 1.0.0
	 */
	public ?int $config_id = null;

	/**
	 * @var string Creation timestamp (Y-m-d H:i:s).
	 * @since 1.0.0
	 */
	public string $created_at;

	/**
	 * @var string Update timestamp (Y-m-d H:i:s).
	 * @since 1.0.0
	 */
	public string $updated_at;

	/**
	 * @var array Meta properties excluded from serialization.
	 * @since 1.0.0
	 */
	protected array $meta_properties = array( 'metas', '_links', 'chatbot' );

	/**
	 * @var array Properties to exclude from JSON serialization.
	 * @json_excluded
	 * @since 1.0.0
	 */
	public array $included = [];

	/**
	 * Create a new Vector_Index instance.
	 *
	 * Automatically sets `vector_db_id` based on the provided `config_id`.
	 *
	 * @param  array  $data  Key-value data for creation.
	 *
	 * @return static
	 * @throws Exception
	 * @since 1.0.0
	 */
	public static function create( $data ) {
		if ( ! empty( $data['config_id'] ) ) {
			$config               = Config::find( $data['config_id'] );
			$data['vector_db_id'] = $config ? $config->get_related_to_instance()->get_id() : null;
		}

		return static::make( static::get_db_strategy()->create( $data, static::TABLE_NAME, static::FILLABLE ) );
	}

	/**
	 * Get the ID of the index.
	 *
	 * @return int
	 * @since 1.0.0
	 */
	public function get_id(): int {
		return $this->id;
	}

	/**
	 * Set the ID of the index.
	 *
	 * @param int $id Index ID.
	 * @return void
	 * @since 1.0.0
	 */
	public function set_id( $id ): void {
		$this->id = $id;
	}

	/**
	 * Get the name of the index.
	 *
	 * @return string
	 * @since 1.0.0
	 */
	public function get_name(): string {
		return $this->name;
	}

	/**
	 * Set the name of the index.
	 *
	 * @param string $name Index name.
	 * @return void
	 * @since 1.0.0
	 */
	public function set_name( string $name ): void {
		$this->name = $name;
	}

	/**
	 * Get the index description.
	 *
	 * @return string|null
	 * @since 1.0.0
	 */
	public function get_description(): ?string {
		return $this->description;
	}

	/**
	 * Set the index description.
	 *
	 * @param string|null $description Optional description.
	 * @return void
	 * @since 1.0.0
	 */
	public function set_description( ?string $description ): void {
		$this->description = $description;
	}

	/**
	 * Get the associated vector DB ID.
	 *
	 * @return string|null
	 * @since 1.0.0
	 */
	public function get_vector_db_id(): ?string {
		return $this->vector_db_id;
	}

	/**
	 * Set the associated vector DB ID.
	 *
	 * @param string|null $vector_db_id DB identifier.
	 * @return void
	 * @since 1.0.0
	 */
	public function set_vector_db_id( ?string $vector_db_id ): void {
		$this->vector_db_id = $vector_db_id;
	}

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

	/**
	 * Set the configuration ID.
	 *
	 * @param int|null $config_id Configuration ID.
	 * @return void
	 * @since 1.0.0
	 */
	public function set_config_id( ?int $config_id ): void {
		$this->config_id = $config_id;
	}

	/**
	 * Get the creation datetime.
	 *
	 * @return string
	 * @since 1.0.0
	 */
	public function get_created_at(): string {
		return $this->created_at;
	}

	/**
	 * Set the creation datetime.
	 *
	 * @param string $created_at Datetime string.
	 * @return void
	 * @since 1.0.0
	 */
	public function set_created_at( string $created_at ): void {
		$this->created_at = $created_at;
	}

	/**
	 * Get the last updated datetime.
	 *
	 * @return string
	 * @since 1.0.0
	 */
	public function get_updated_at(): string {
		return $this->updated_at;
	}

	/**
	 * Set the last updated datetime.
	 *
	 * @param string $updated_at Datetime string.
	 * @return void
	 * @since 1.0.0
	 */
	public function set_updated_at( string $updated_at ): void {
		$this->updated_at = $updated_at;
	}

	/**
	 * Find a specific meta entry by key for this vector index.
	 *
	 * @param string $meta_key The meta key to search for.
	 * @return Vector_Index_Meta|null
	 * @since 1.0.0
	 */
	public function find_meta( string $meta_key ): ?Vector_Index_Meta {
		return Vector_Index_Meta::where( [ 'vector_index_id' => $this->id, 'meta_key' => $meta_key ] )->first();
	}

	/**
	 * Get all meta entries for this vector index.
	 *
	 * @return Vector_Index_Meta[]
	 * @since 1.0.0
	 */
	public function metas() {
		return Vector_Index_Meta::where( [ 'vector_index_id' => $this->id ] )->get();
	}

	public function chatbot() {
		$meta = $this->find_meta( 'chatbot_uuid' );
		if ( $meta instanceof Vector_Index_Meta ) {
			if ( $meta->get_meta_value() ) {
				$chatbot = Chatbot::find_by_uuid( $meta->get_meta_value() );
			} else {
				$chatbot = Chatbot::make();
			}
			if ( $chatbot instanceof Chatbot ) {
				$collection = ( new Data_Object_Collection( [ $chatbot ] ) )->with( 'utility' );

				return $collection->first();
			}
		}

		return null;
	}

	public function is_actionable() {
		$meta = $this->find_meta( 'purpose' );

		return $meta instanceof Vector_Index_Meta && $meta->get_meta_value() === 'actionable_knowledge';
	}

	public function get_datasets() {
		$vector_index_vectors = Vector_Index_Vector::where( [ 'vector_index_id' => $this->id ] );
		if ( $vector_index_vectors->is_empty() ) {
			return null;
		}
		$vectors = Vector::where( [ 'id' => $vector_index_vectors->pluck( 'vector_id' ) ] );
		unset( $vector_index_vectors );
		if ( $vectors->is_empty() ) {
			return null;
		}
		$dataset_entries = Dataset_Entry::where( [ 'id' => $vectors->pluck( 'dataset_entry_id' ) ] );
		unset( $vectors );
		if ($dataset_entries->is_empty()){
			return null;
		}

		return Dataset::where([ 'id' => $dataset_entries->pluck('dataset_id')]);
	}

	public function get_meta( $key ) {
		$meta = Vector_Index_Meta::where( [ 'meta_key' => $key, 'vector_index_id' => $this->id ] );

		return ! $meta->is_empty() ? $meta->first() : null;
	}

	public function get_inactive_vectors(){
		return ( new Vector_Index_Repository() )->get_inactive_vectors( $this->id );
	}
}