<?php
declare( strict_types=1 );

namespace FiboFilters\Indexer\Filters;

use FiboFilters\Indexer\AbstractDocument;
use FiboFilters\Indexer\AbstractIndexer;
use FiboFilters\Indexer\BuildInfo;
use FiboFilters\Indexer\DocumentFactory;
use FiboFilters\Indexer\Logger;
use FiboFilters\Indexer\SourceQueryFactory;
use FiboFilters\Util\Cache;
use FiboFilters\WP\WPDBSecond;
use FiboFilters\WPDBException;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class Indexer
 */
class Indexer extends AbstractIndexer {

	/** @var BuildInfo */
	protected $build_info;

	/** @var Database */
	private $database;

	/** @var DocumentFactory */
	protected $document_factory;

	/** @var Logger */
	protected $logger;

	/** @var SourceQueryFactory */
	protected $source_query_factory;

	/** @var WPDBSecond */
	protected $wpdb_second;

	public function __construct(
		BuildInfo $build_info,
		Logger $logger,
		Database $database,
		DocumentFactory $document_factory,
		SourceQueryFactory $source_query_factory,
		WPDBSecond $wpdb_second,
		Cache $cache
	) {
		$this->type = 'filters';

		parent::__construct( $build_info, $logger, $wpdb_second, $cache );

		$this->database             = $database;
		$this->document_factory     = $document_factory;
		$this->source_query_factory = $source_query_factory;
	}

	/**
	 * Get Document ready for indexing
	 *
	 * @param array $row Raw document data.
	 */
	protected function get_document( array $row ): AbstractDocument {
		$type   = $this->get_type();
		$row    = apply_filters( "fibofilters/indexer/{$type}/items_row", $row );
		$config = [
			'index_role' => $this->get_index_role(),
		];

		return $this->document_factory->get( $row, $config );
	}

	/**
	 * Fetch data of products that should be indexed
	 *
	 * @param array  $ids Product IDs.
	 * @param string $context From where the update is called.
	 */
	protected function fetch_products_set_data( array $ids = [], string $context = '' ): array {
		$source = $this->source_query_factory->get(
			'filters',
			[
				'post_in' => $ids,
				'context' => $context,
			]
		);

		return $source->get_data();
	}

	/**
	 * Update product
	 *
	 * @param int    $post_id Product ID.
	 * @param bool   $update_descriptor Whether to set a descriptor update date.
	 * @param string $context From where the update is called.
	 */
	public function update( int $post_id, bool $update_descriptor = false, $context = '' ): void {
		$this->delete( $post_id );
		$this->insert( $post_id, $update_descriptor, $context );
	}

	/**
	 * Remove product from the index
	 *
	 * @param int  $post_id Product ID.
	 * @param bool $update_descriptor Mark descriptor as deleted.
	 */
	public function delete( int $post_id, bool $update_descriptor = false ): void {
		try {
			$updated_date = '';
			if ( $update_descriptor ) {
				$updated_date = current_time( 'mysql' );
				$this->build_info->add_info( 'last_updated_date', $updated_date, 'main' );
			}
			$this->process_item_row( [ 'ID' => $post_id ], 'delete', $updated_date );
		} catch ( WPDBException $e ) {
			$this->logger->handle_updater_throwable_error( $e, $this->get_log_prefix() );
		}
	}

	/**
	 * Insert product to the index
	 *
	 * @param int    $post_id Product ID.
	 * @param bool   $update_descriptor Whether to set a descriptor update date.
	 * @param string $context From where the update is called.
	 */
	public function insert( int $post_id, bool $update_descriptor = false, string $context = '' ): void {
		foreach ( $this->fetch_products_set_data( [ $post_id ], $context ) as $row ) {
			try {
				$updated_date = '';
				if ( $update_descriptor ) {
					$updated_date = current_time( 'mysql' );
					$this->build_info->add_info( 'last_updated_date', $updated_date, 'main' );
				}
				$this->process_item_row( $row, 'save', $updated_date );
			} catch ( WPDBException $e ) {
				$this->logger->handle_updater_throwable_error( $e, $this->get_log_prefix() );
			}
		}
	}

	/**
	 * Wipe index
	 *
	 * @param string $index_role Index role.
	 */
	public function wipe( string $index_role ): bool {
		$this->database->remove( $index_role );

		return true;
	}

	public function complete(): void {
		$this->build_info->add_info( 'end_filters_ts', time() );
		$this->logger->log( '[Filters index] Completed' );

		/** Wp-documentor:ignore */
		do_action( 'fibofilters/indexer/maybe_completed' );
	}
}
