<?php

namespace CelerSearch\CLI;

use CelerSearch\Factories\IndexFactory;
use CelerSearch\Repositories\IndexRepository;
use CelerSearch\Utilities\PortableConfig;
use WP_CLI;
use WP_CLI\Formatter;

/**
 * Manage CelerSearch indices via WP-CLI.
 */
class IndexCommand {

	/**
	 * List all CelerSearch indices.
	 *
	 * ## OPTIONS
	 *
	 * [--format=<format>]
	 * : Output format.
	 * ---
	 * default: table
	 * options:
	 *   - table
	 *   - csv
	 *   - json
	 *   - yaml
	 * ---
	 *
	 * ## EXAMPLES
	 *
	 *     # List all indices
	 *     wp celersearch index list
	 *
	 *     # Output as JSON
	 *     wp celersearch index list --format=json
	 *
	 * @when after_wp_load
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Associative arguments.
	 */
	public function list( $args, $assoc_args ) {
		$repo    = new IndexRepository();
		$indices = $repo->get( [], 1, 100 );

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

		$items = [];
		foreach ( $indices as $index_config ) {
			$local_count  = '-';
			$remote_count = '-';

			try {
				$index        = IndexFactory::create( $index_config->getId() );
				$local_count  = $index->get_candidate_count();
				$count_result = $index->count_items();
				if ( ! $count_result->is_error_response() ) {
					$remote_count = $count_result->get_value();
				}
			} catch ( \Exception $e ) {
				// Keep defaults
			}

			$items[] = [
				'ID'     => $index_config->getId(),
				'Name'   => $index_config->getName(),
				'Slug'   => $index_config->getSlug(),
				'Type'   => $index_config->getType(),
				'Local'  => $local_count,
				'Remote' => $remote_count,
				'Status' => $index_config->getStatus() ?: 'pending',
			];
		}

		$format    = WP_CLI\Utils\get_flag_value( $assoc_args, 'format', 'table' );
		$formatter = new Formatter( $assoc_args, [ 'ID', 'Name', 'Slug', 'Type', 'Local', 'Remote', 'Status' ] );
		$formatter->display_items( $items );
	}

	/**
	 * Show detailed information about an index.
	 *
	 * ## OPTIONS
	 *
	 * <index>
	 * : Index ID or slug.
	 *
	 * [--format=<format>]
	 * : Output format.
	 * ---
	 * default: table
	 * options:
	 *   - table
	 *   - json
	 *   - yaml
	 * ---
	 *
	 * ## EXAMPLES
	 *
	 *     # Show info for index ID 1
	 *     wp celersearch index info 1
	 *
	 *     # Show info for index by slug
	 *     wp celersearch index info posts-news
	 *
	 * @when after_wp_load
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Associative arguments.
	 */
	public function info( $args, $assoc_args ) {
		$identifier   = $args[0];
		$index_config = $this->resolve_index( $identifier );

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

		$info = [
			'ID'         => $index_config->getId(),
			'Name'       => $index_config->getName(),
			'Slug'       => $index_config->getSlug(),
			'Type'       => $index_config->getType(),
			'Service ID' => $index_config->getServiceId(),
			'Status'     => $index_config->getStatus() ?: 'pending',
			'Synced At'  => $index_config->getSyncedAt() ?: '-',
			'Checked At' => $index_config->getCheckedAt() ?: '-',
		];

		try {
			$index              = IndexFactory::create( $index_config->getId() );
			$info['Local Items']  = $index->get_candidate_count();
			$info['Batch Size']   = $index->get_candidate_batch_size();
			$info['Total Batches'] = $index->get_candidate_batch_pages();

			$count_result = $index->count_items();
			if ( ! $count_result->is_error_response() ) {
				$info['Remote Items'] = $count_result->get_value();
			} else {
				$info['Remote Items'] = 'Error: ' . $count_result->get_error_message();
			}
		} catch ( \Exception $e ) {
			$info['Error'] = $e->getMessage();
		}

		$format = WP_CLI\Utils\get_flag_value( $assoc_args, 'format', 'table' );

		if ( $format === 'json' ) {
			WP_CLI::log( json_encode( $info, JSON_PRETTY_PRINT ) );
		} elseif ( $format === 'yaml' ) {
			WP_CLI::log( \Spyc::YAMLDump( $info ) );
		} else {
			foreach ( $info as $key => $value ) {
				WP_CLI::log( sprintf( '%-15s %s', $key . ':', $value ) );
			}
		}
	}

	/**
	 * Clear an index (delete all documents without deleting the index).
	 *
	 * ## OPTIONS
	 *
	 * <index>
	 * : Index ID or slug.
	 *
	 * [--yes]
	 * : Skip confirmation prompt.
	 *
	 * ## EXAMPLES
	 *
	 *     # Clear index ID 1
	 *     wp celersearch index clear 1
	 *
	 *     # Clear without confirmation
	 *     wp celersearch index clear 1 --yes
	 *
	 * @when after_wp_load
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Associative arguments.
	 */
	public function clear( $args, $assoc_args ) {
		$identifier   = $args[0];
		$index_config = $this->resolve_index( $identifier );

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

		$index_name = $index_config->getName();

		WP_CLI::confirm( "Are you sure you want to clear all documents from '{$index_name}'?", $assoc_args );

		try {
			$index  = IndexFactory::create( $index_config->getId() );
			$result = $index->clear();

			if ( ! $result->is_error_response() ) {
				WP_CLI::success( "Index '{$index_name}' cleared successfully." );
			} else {
				WP_CLI::error( "Failed to clear index: " . $result->get_error_message() );
			}
		} catch ( \Exception $e ) {
			WP_CLI::error( "Failed to clear index: " . $e->getMessage() );
		}
	}

	/**
	 * Export an index as a portable JSON envelope.
	 *
	 * ## OPTIONS
	 *
	 * <index>
	 * : Index ID or slug.
	 *
	 * [--output=<path>]
	 * : Write JSON to this file instead of stdout.
	 *
	 * ## EXAMPLES
	 *
	 *     # Print envelope to stdout
	 *     wp celersearch index export 1
	 *
	 *     # Write envelope to a file
	 *     wp celersearch index export posts-news --output=/tmp/posts.json
	 *
	 * @when after_wp_load
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Associative arguments.
	 */
	public function export( $args, $assoc_args ) {
		$identifier   = $args[0];
		$index_config = $this->resolve_index( $identifier );

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

		try {
			$envelope = PortableConfig::export_index( $index_config );
		} catch ( \Throwable $e ) {
			WP_CLI::error( 'Export failed: ' . $e->getMessage() );
		}

		$json   = wp_json_encode( $envelope, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
		$output = WP_CLI\Utils\get_flag_value( $assoc_args, 'output', null );
		if ( $output ) {
			$bytes = file_put_contents( $output, $json );
			if ( $bytes === false ) {
				WP_CLI::error( "Could not write to {$output}" );
			}
			WP_CLI::success( sprintf( 'Wrote %d bytes to %s', $bytes, $output ) );
		} else {
			WP_CLI::log( $json );
		}
	}

	/**
	 * Import an index from a portable JSON envelope. Always creates a new
	 * record — slug collisions are resolved by appending `-1`, `-2`, ….
	 *
	 * ## OPTIONS
	 *
	 * <file>
	 * : Path to a CelerSearch index export file.
	 *
	 * ## EXAMPLES
	 *
	 *     wp celersearch index import /tmp/posts.json
	 *
	 * @when after_wp_load
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Associative arguments.
	 */
	public function import( $args, $assoc_args ) {
		$file = $args[0];
		$envelope = $this->read_envelope_file( $file );

		try {
			$result = PortableConfig::import_index( $envelope );
		} catch ( \InvalidArgumentException $e ) {
			WP_CLI::error( $e->getMessage() );
		} catch ( \Throwable $e ) {
			WP_CLI::error( 'Import failed: ' . $e->getMessage() );
		}

		$record = $result['record'];
		WP_CLI::success( sprintf(
			'Imported index #%d (%s) with slug "%s".',
			$record->getId(),
			$record->getName(),
			$record->getSlug()
		) );

		if ( empty( $result['service_ref_resolved'] ) ) {
			WP_CLI::warning( 'The referenced service was not found on this site — assign a service before using the index.' );
		}
	}

	/**
	 * Read and decode a JSON envelope file.
	 *
	 * @param string $path File path.
	 *
	 * @return array
	 */
	private function read_envelope_file( $path ) {
		if ( ! is_readable( $path ) ) {
			WP_CLI::error( "Cannot read file: {$path}" );
		}
		$contents = file_get_contents( $path );
		if ( $contents === false || $contents === '' ) {
			WP_CLI::error( "File is empty: {$path}" );
		}
		$envelope = json_decode( $contents, true );
		if ( ! is_array( $envelope ) ) {
			WP_CLI::error( "File is not valid JSON: {$path}" );
		}
		return $envelope;
	}

	/**
	 * 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 );
	}
}
