<?php

namespace CelerSearch\Services;

use Exception;
use CelerSearch\Abstracts\IndexResponse;
use CelerSearch\DataTransfer\IndexResponses\ErrorResponse;
use CelerSearch\DataTransfer\IndexResponses\SearchResponse;
use CelerSearch\DataTransfer\ServiceConfig;
use CelerSearch\Indices\BaseIndex;
use CelerSearch\Interfaces\IIndexableObject;

abstract class BaseService {

	/**
	 * The configuration object
	 * @var ServiceConfig
	 */
	protected ServiceConfig $details;

	/**
	 * Constructor
	 *
	 * @param ServiceConfig $details
	 *
	 * @throws Exception
	 */
	public function __construct( ServiceConfig $details ) {
		$this->details = $details;
		$this->init();
	}

	/**
	 * A good place to create API connection, etc.
	 * @return void
	 * @throws Exception
	 */
	abstract public function init(): void;

	/**
	 * Perform a search on the index
	 *
	 * @param BaseIndex $index
	 * @param string $query
	 * @param array $params
	 *
	 * @return IndexResponse
	 */
	abstract public function search( BaseIndex $index, string $query, array $params = [] ): IndexResponse;

	/**
	 * Format the search parameters
	 * @param array $params
	 *
	 * @return array
	 */
	abstract protected function format_search_params(array $params): array;

	/**
	 * Syncs item to the index
	 *
	 * @param BaseIndex $index
	 * @param IIndexableObject $item
	 *
	 * @return IndexResponse
	 */
	abstract public function sync_item( BaseIndex $index, IIndexableObject $item ): IndexResponse;

	/**
	 * Deletes item from the index
	 *
	 * @param BaseIndex $index
	 * @param IIndexableObject $item
	 *
	 * @return IndexResponse
	 */
	abstract public function delete_item( BaseIndex $index, IIndexableObject $item ): IndexResponse;

	/**
	 * Counts index indexed data
	 *
	 * @param BaseIndex $index
	 *
	 * @return IndexResponse
	 */
	abstract public function count_items( BaseIndex $index ): IndexResponse;

	/**
	 * Check whether the index exists
	 *
	 * @param BaseIndex $index
	 *
	 * @return IndexResponse
	 */
	abstract public function check_index( BaseIndex $index ): IndexResponse;

	/**
	 * Create the index if it doesn't exist
	 *
	 * @param BaseIndex $index
	 *
	 * @return IndexResponse
	 */
	abstract public function touch_index( BaseIndex $index ): IndexResponse;

	/**
	 * Clear the index
	 *
	 * @param BaseIndex $index
	 *
	 * @return IndexResponse
	 */
	abstract public function clear_index( BaseIndex $index ): IndexResponse;

	/**
	 * Delete the index
	 *
	 * @param BaseIndex $index
	 *
	 * @return IndexResponse
	 */
	abstract public function delete_index( BaseIndex $index ): IndexResponse;

	/**
	 * Push index settings to remote
	 *
	 * @param BaseIndex $index
	 *
	 * @return IndexResponse
	 */
	abstract public function push_settings( BaseIndex $index ): IndexResponse;

	/**
	 * Build the settings
	 *
	 * @param BaseIndex $index
	 *
	 * @return IndexResponse
	 */
	abstract public function build_settings( BaseIndex $index ): array;

	/**
	 * Returns the service settings
	 *
	 * @param BaseIndex $index
	 *
	 * @return mixed
	 */
	public function get_settings( BaseIndex $index ): array {
		$settings = apply_filters( 'celersearch_service_settings', $this->build_settings( $index ), $this );
		return apply_filters( 'celersearch_service_' . $this->details->getProvider() . '_settings', $settings, $this->details, $this );
	}

	/**
	 * Lightweight health check for the service
	 *
	 * Checks if the service is reachable and healthy without creating
	 * or modifying any indices or documents.
	 *
	 * @return bool True if the service is healthy, false otherwise
	 */
	abstract public function health_check(): bool;

	/**
	 * Wait for an async task to complete
	 *
	 * Providers with asynchronous operations (like MeiliSearch) should implement
	 * this method to block until the task completes. Providers with synchronous
	 * operations can simply return true.
	 *
	 * @param IndexResponse $response Response containing task info
	 * @param int           $timeout_ms Maximum wait time in milliseconds
	 *
	 * @return bool True if task completed successfully, false otherwise
	 */
	abstract public function wait_for_completion( IndexResponse $response, int $timeout_ms = 5000 ): bool;

}