<?php

namespace CelerSearch\CLI;

defined( 'ABSPATH' ) || exit;

use CelerSearch\Factories\IndexFactory;
use CelerSearch\Repositories\IndexRepository;
use WP_CLI;

/**
 * Reindex CelerSearch indices via WP-CLI.
 */
class ReindexCommand {

	/**
	 * Reindex one or all CelerSearch indices.
	 *
	 * ## OPTIONS
	 *
	 * [<index>]
	 * : Index ID or slug to reindex.
	 *
	 * [--all]
	 * : Reindex all indices.
	 *
	 * [--clear]
	 * : Clear index before reindexing.
	 *
	 * [--batch=<number>]
	 * : Batch size override.
	 *
	 * [--from=<number>]
	 * : Resume from batch number.
	 *
	 * [--dry-run]
	 * : Show what would be indexed without executing.
	 *
	 * ## EXAMPLES
	 *
	 *     # Reindex index with ID 1
	 *     wp celersearch reindex 1
	 *
	 *     # Reindex index by slug
	 *     wp celersearch reindex posts-news
	 *
	 *     # Reindex all indices
	 *     wp celersearch reindex --all
	 *
	 *     # Clear and reindex all indices
	 *     wp celersearch reindex --all --clear
	 *
	 *     # Resume from batch 5
	 *     wp celersearch reindex 1 --from=5
	 *
	 *     # Use custom batch size
	 *     wp celersearch reindex 1 --batch=50
	 *
	 *     # Preview without executing
	 *     wp celersearch reindex --all --dry-run
	 *
	 * @when after_wp_load
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Associative arguments.
	 */
	public function __invoke( $args, $assoc_args ) {
		$index_identifier = $args[0] ?? null;
		$reindex_all      = WP_CLI\Utils\get_flag_value( $assoc_args, 'all', false );

		if ( ! $index_identifier && ! $reindex_all ) {
			WP_CLI::error( 'Please specify an index ID/slug or use --all to reindex all indices.' );
		}

		if ( $index_identifier && $reindex_all ) {
			WP_CLI::error( 'Cannot specify both an index identifier and --all flag.' );
		}

		if ( $reindex_all ) {
			$this->reindex_all( $assoc_args );
		} else {
			$this->reindex_single( $index_identifier, $assoc_args );
		}
	}

	/**
	 * Reindex a single index.
	 *
	 * @param string $identifier Index ID or slug.
	 * @param array  $assoc_args Associative arguments.
	 */
	private function reindex_single( $identifier, $assoc_args ) {
		$index_config = $this->resolve_index( $identifier );

		if ( ! $index_config ) {
			WP_CLI::error( "Index not found: {$identifier}" );
		}

		$this->do_reindex( $index_config, $assoc_args );
	}

	/**
	 * Reindex all indices.
	 *
	 * @param array $assoc_args Associative arguments.
	 */
	private function reindex_all( $assoc_args ) {
		$repo    = new IndexRepository();
		$indices = $repo->get( [], 1, 100 );

		if ( empty( $indices ) ) {
			WP_CLI::warning( 'No indices found.' );
			return;
		}

		WP_CLI::log( sprintf( 'Found %d indices to reindex.', count( $indices ) ) );
		WP_CLI::log( '' );

		foreach ( $indices as $index_config ) {
			$this->do_reindex( $index_config, $assoc_args );
			WP_CLI::log( '' );
		}

		WP_CLI::success( 'All indices reindexed.' );
	}

	/**
	 * Resolve index by ID or slug.
	 *
	 * @param string $identifier Index ID or slug.
	 *
	 * @return \CelerSearch\DataTransfer\IndexConfig|null
	 */
	private function resolve_index( $identifier ) {
		$repo = new IndexRepository();

		// Try as ID first.
		if ( is_numeric( $identifier ) ) {
			$index = $repo->find( (int) $identifier );
			if ( $index ) {
				return $index;
			}
		}

		// Try as slug.
		return $repo->find_by_slug( $identifier );
	}

	/**
	 * Perform the actual reindexing.
	 *
	 * @param \CelerSearch\DataTransfer\IndexConfig $index_config Index configuration.
	 * @param array                                  $assoc_args   Associative arguments.
	 */
	private function do_reindex( $index_config, $assoc_args ) {
		$dry_run    = WP_CLI\Utils\get_flag_value( $assoc_args, 'dry-run', false );
		$clear      = WP_CLI\Utils\get_flag_value( $assoc_args, 'clear', false );
		$from_batch = (int) WP_CLI\Utils\get_flag_value( $assoc_args, 'from', 1 );
		$batch_size = WP_CLI\Utils\get_flag_value( $assoc_args, 'batch', null );

		$index_name = $index_config->getName();
		$index_slug = $index_config->getSlug();
		$index_id   = $index_config->getId();

		WP_CLI::log( sprintf( 'Reindexing: %s (ID: %d, Slug: %s)', $index_name, $index_id, $index_slug ) );

		try {
			$index = IndexFactory::create( $index_id );
		} catch ( \Exception $e ) {
			WP_CLI::error( sprintf( 'Failed to create index instance: %s', $e->getMessage() ) );
			return;
		}

		// Override batch size if provided.
		if ( $batch_size ) {
			add_filter( 'celersearch_indexable_items_batch_size', function() use ( $batch_size ) {
				return (int) $batch_size;
			}, 100 );
		}

		$total_items   = $index->get_candidate_count();
		$current_batch = $index->get_candidate_batch_size();
		$total_batches = $index->get_candidate_batch_pages();

		if ( $total_batches < 1 ) {
			$total_batches = 1;
		}

		WP_CLI::log( sprintf( '  Items: %d | Batch Size: %d | Batches: %d', $total_items, $current_batch, $total_batches ) );

		if ( $from_batch > 1 ) {
			WP_CLI::log( sprintf( '  Resuming from batch: %d', $from_batch ) );
		}

		if ( $dry_run ) {
			WP_CLI::log( '  [DRY RUN] Would index the above items.' );
			return;
		}

		// Clear index if requested and starting from batch 1.
		if ( $clear && $from_batch === 1 ) {
			WP_CLI::log( '  Clearing index...' );
			try {
				$index->clear();
			} catch ( \Exception $e ) {
				WP_CLI::warning( sprintf( 'Failed to clear index: %s', $e->getMessage() ) );
			}
		}

		$progress = \WP_CLI\Utils\make_progress_bar( '  Indexing', $total_batches - $from_batch + 1 );

		for ( $batch = $from_batch; $batch <= $total_batches; $batch++ ) {
			try {
				$index->rebuild_index( $batch );
			} catch ( \Exception $e ) {
				WP_CLI::warning( sprintf( 'Batch %d failed: %s', $batch, $e->getMessage() ) );
			}
			$progress->tick();
		}

		$progress->finish();

		WP_CLI::success( sprintf( 'Indexed %d items in %d batches for "%s".', $total_items, $total_batches - $from_batch + 1, $index_name ) );
	}
}
