<?php

namespace Limb_Chatbot\Includes\Services;

use Exception;
use Limb_Chatbot\Includes\Data_Objects\Dataset;
use Limb_Chatbot\Includes\Data_Objects\Dataset_Entry;
use Limb_Chatbot\Includes\Data_Objects\Vector;
use Limb_Chatbot\Includes\Repositories\Dataset_Entry_Repository;
use Limb_Chatbot\Includes\Repositories\Vector_Repository;

/**
 * Service class for managing dataset-entry related operations
 *
 * @since 1.0.0
 */
class Dataset_Entry_Service {

	/**
	 * Dataset repository instance.
	 *
	 * @since 1.0.0
	 * @var Dataset_Entry_Repository
	 */
	protected Dataset_Entry_Repository $repository;

	/**
	 * Vector Service instance.
	 *
	 * @var Vector_Service
	 * @since 1.0.0
	 */
	protected Vector_Service $vector_service;

	/**
	 * Constructor.
	 *
	 * @param  Dataset_Entry_Repository|null  $repository  Optional repository instance. A default one will be created if not passed.
	 *
	 * @since 1.0.0
	 *
	 */
	public function __construct( ?Dataset_Entry_Repository $repository = null ) {
		$this->repository     = $repository ?? new Dataset_Entry_Repository();
		$this->vector_service = new Vector_Service( new Vector_Repository() );
	}

	/**
	 * Creates or updates a batch of dataset entries.
	 *
	 * If an entry contains an 'id', it will be updated; otherwise, a new record is created.
	 *
	 * @param  array  $items  List of dataset entry arrays.
	 *
	 * @return Collection Collection of created or updated Dataset_Entry instances.
	 * @throws Exception
	 * @since 1.0.0
	 *
	 */
	public function batch( $items ): Collection {
		$collection = new Collection();
		foreach ( $items as $item ) {
			$res = ! empty( $item['id'] ) ? Dataset_Entry::update( [ 'id' => $item['id'] ],
				$item ) : Dataset_Entry::create( $item );
			$collection->push_item( $res );
		}

		return $collection;
	}

	/**
	 * Update entry
	 *
	 * @param  int  $id
	 * @param  array  $data
	 *
	 * @return Dataset_Entry|null
	 * @throws Exception
	 */
	public function update( int $id, array $data ): ?Dataset_Entry {
		if ( $dataset_entry = Dataset_Entry::update( [ 'id' => $id ], $data ) ) {
			$vectors = Vector::where( [ 'dataset_entry_id' => $id ] );
			$vectors->each( function ( $vector ) {
				$this->vector_service->sync( $vector );
			} );
		}

		return $dataset_entry;
	}

	/**
	 * Create entry
	 *
	 * @param  array  $data
	 *
	 * @return Dataset_Entry|null
	 * @throws Exception
	 */
	public function create( array $data ): ?Dataset_Entry {
		return Dataset_Entry::create( $data );
	}

	/**
	 * Entries batch delete.
	 * Runs vector delete job, if there are vectors associated with entries.
	 *
	 * @param $data
	 *
	 * @return bool
	 * @throws \Limb_Chatbot\Includes\Exceptions\Exception
	 */
	public function batch_delete( $data ) {
		$dataset_entries = Dataset_Entry::where( $data );
		if ( ! $dataset_entries->is_empty() ) {
			$dataset = $dataset_entries->first()->dataset();
			if ( $dataset instanceof Dataset && $dataset->get_type() == Dataset::TYPE_INFORMATIONAL_KNOWLEDGE ) {
				$this->vector_service->batch_delete( [ 'dataset_entry_id' => $dataset_entries->pluck( 'id' ) ] );
			}

			return Dataset_Entry::delete( $data );
		}

		return true;
	}

	/**
	 * Delete entry by id
	 *
	 * @param  int  $id
	 *
	 * @return bool|null
	 * @throws \Limb_Chatbot\Includes\Exceptions\Exception
	 */
	public function delete( int $id ): ?bool {
		if ( $vectors = Vector::where( [ 'dataset_entry_id' => $id ] ) ) {
			$vector_deleted = null;
			$vectors->each( function ( $vector ) use ( &$vector_deleted ) {
				$vector_deleted = $this->vector_service->delete( $vector );
			} );
		}
		if ( ! isset( $vector_deleted ) || $vector_deleted ) {
			return Dataset_Entry::delete( [ 'id' => $id ] );
		}

		return false;
	}
}