<?php
declare( strict_types=1 );

namespace FiboFilters\WpCli;

use FiboFilters\Config\Index;
use FiboFilters\DI\ContainerWrapper;
use FiboFilters\Indexer\BuildInfo;
use FiboFilters\Indexer\Config;
use WP_CLI;
use WP_CLI\Utils;

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

/**
 * Builds the FiboFilters index.
 */
class FibofiltersIndexCommand {
	/** @var BuildInfo */
	private $build_info;

	/** @var Config */
	private $config;

	/** @var Index */
	private $index;

	public function __construct() {
		$this->build_info = ContainerWrapper::get_instance()->get( BuildInfo::class );
		$this->config     = ContainerWrapper::get_instance()->get( Config::class );
		$this->index      = ContainerWrapper::get_instance()->get( Index::class );
	}

	/**
	 * Build index.
	 *
	 * ## OPTIONS
	 *
	 * [--show-logs]
	 * : Show indexer logs.
	 *
	 * [--disable-parallel]
	 * : Disable parallel index build.
	 */
	public function build( array $args, array $assoc_args ): void {
		$show_logs        = (bool) Utils\get_flag_value( $assoc_args, 'show-logs', false );
		$disable_pallarel = (bool) Utils\get_flag_value( $assoc_args, 'disable-parallel', false );

		if ( $disable_pallarel && ! defined( 'FIBOFILTERS_PARALLEL_INDEX_BUILD' ) ) {
			define( 'FIBOFILTERS_PARALLEL_INDEX_BUILD', false );
		}

		$this->index->build( 'direct' );

		$status = $this->build_info->get_info( 'status', $this->config->get_index_role() );

		if ( $status === 'completed' ) {
			if ( $show_logs ) {
				$this->show_logs( $this->get_index_role() );
			}
			WP_CLI::success( 'Indexing completed.' );
		} elseif ( $status === 'error' ) {
			if ( $show_logs ) {
				$this->show_logs( $this->config->is_parallel_building_enabled() ? 'tmp' : $this->get_index_role() );
			}
			WP_CLI::error( 'Indexing failed due to an error.' );
		} else {
			if ( $show_logs ) {
				$this->show_logs( $this->config->is_parallel_building_enabled() ? 'tmp' : $this->get_index_role() );
			}
			WP_CLI::error( sprintf( 'Unknown error. Indexing status: %s', $status ) );
		}
	}

	/**
	 * Update the indicated products in the index.
	 *
	 * ## OPTIONS
	 *
	 * <id>
	 * : One or more IDs of products to update in index (comma-separated).
	 */
	public function update( $args, $assoc_args ) {
		$this->action_in_index( 'update', $args, $assoc_args );
	}

	/**
	 * Delete the indicated products in the index.
	 *
	 * ## OPTIONS
	 *
	 * <id>
	 * : One or more IDs of products to delete in index (comma-separated).
	 */
	public function delete( $args, $assoc_args ) {
		$this->action_in_index( 'delete', $args, $assoc_args );
	}

	/**
	 * Delete whole main index.
	 *
	 * @subcommand destroy
	 */
	public function remove_index() {
		$this->index->remove_index();
	}

	/**
	 * Get indexer info value or values.
	 *
	 * ## OPTIONS
	 *
	 * [<key>]
	 * : Indexer info key.
	 * ---
	 * options:
	 *   - build_id
	 *   - status
	 *   - plugin_version
	 *   - start_ts
	 *   - end_ts
	 *   - total_products_for_indexing
	 *   - simple_products_count
	 *   - variable_products_count
	 *   - variations_count
	 * ---
	 *
	 * [--index-role=<role>]
	 * : Index role: 'main' or 'tmp' (if parallel index building enabled). Default: 'main'.
	 *
	 * @subcommand get-info
	 */
	public function get_info( $args, $assoc_args ) {
		$key = '';
		if ( ! empty( $args ) ) {
			list( $key ) = $args;
		}

		if ( ! empty( $key ) ) {
			$value = $this->build_info->get_info( $key, $this->get_index_role( $assoc_args ) );
			WP_CLI::print_value( $value );
		} else {
			$info_keys = [
				'build_id',
				'status',
				'plugin_version',
				'start_ts',
				'end_ts',
				'total_products_for_indexing',
				'simple_products_count',
				'variable_products_count',
				'variations_count',
			];
			$items     = [];
			foreach ( $info_keys as $info_key ) {
				$items[] = [
					'key'   => $info_key,
					'value' => $this->build_info->get_info( $info_key, $this->get_index_role( $assoc_args ) ),
				];
			}
			Utils\format_items( 'table', $items, [ 'key', 'value' ] );
		}
	}

	/**
	 * Show indexer logs.
	 *
	 * ## OPTIONS
	 *
	 * [--index-role=<role>]
	 * : Index role: 'main' or 'tmp' (if parallel index building enabled). Default: 'main'.
	 *
	 * @subcommand show-logs
	 */
	public function show_indexer_logs( $args, $assoc_args ) {
		$this->show_logs( $this->get_index_role( $assoc_args ) );
	}

	private function show_logs( $index_role = 'main' ) {
		$logs = $this->build_info->get_info( 'logs', $index_role );
		if ( ! empty( $logs ) ) {
			foreach ( $logs as $log ) {
				if ( $log['error'] ) {
					WP_CLI::error( $log['message'], false );
				} else {
					WP_CLI::log( $log['message'] );
				}
			}
		}
	}

	private function get_index_role( array $assoc_args = [] ): string {
		$index_role = 'main';

		if ( $this->config->is_parallel_building_enabled() ) {
			$assoc_args = wp_parse_args(
				$assoc_args,
				[
					'index-role' => 'main',
				]
			);
			if ( in_array( $assoc_args['index-role'], [ 'main', 'tmp' ], true ) ) {
				$index_role = $assoc_args['index-role'];
			}
		}

		return $index_role;
	}

	/**
	 * Update or delete the indicated posts in the index.
	 */
	private function action_in_index( $action, $args ) {
		if ( empty( $args[0] ) ) {
			WP_CLI::error( 'The ID list is empty.' );
		}

		$ids       = preg_replace( '/\s+/', '', $args[0] );
		$ids       = explode( ',', $ids );
		$posts_ids = array_filter( $ids, 'ctype_digit' );
		$posts_ids = array_map( 'intval', $posts_ids );

		if ( $this->build_info->get_info( 'status' ) !== 'completed' ) {
			WP_CLI::error( 'The index must be properly built to be able to update it.' );
		}

		foreach ( $posts_ids as $post_id ) {
			if ( $action === 'update' ) {
				$this->index->update_single( $post_id );
			} elseif ( $action === 'delete' ) {
				$this->index->delete_single( $post_id );
			}
			$label = $action === 'update' ? 'Indexed' : 'Deleted';
			WP_CLI::log( sprintf( $label . ' product %d.', $post_id ) );
		}
	}
}
