<?php

namespace CelerSearch\CLI;

use CelerSearch\Factories\ServiceFactory;
use CelerSearch\Repositories\ServiceRepository;
use WP_CLI;
use WP_CLI\Formatter;

/**
 * Manage CelerSearch services via WP-CLI.
 */
class ServiceCommand {

	/**
	 * List all CelerSearch services.
	 *
	 * ## OPTIONS
	 *
	 * [--format=<format>]
	 * : Output format.
	 * ---
	 * default: table
	 * options:
	 *   - table
	 *   - csv
	 *   - json
	 *   - yaml
	 * ---
	 *
	 * ## EXAMPLES
	 *
	 *     # List all services
	 *     wp celersearch service list
	 *
	 *     # Output as JSON
	 *     wp celersearch service 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 ServiceRepository();
		$services = $repo->get( [], 1, 100 );

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

		$items = [];
		foreach ( $services as $service_config ) {
			$config = $service_config->getConfig();
			$url    = isset( $config->url ) ? $config->url : '-';

			// Mask API key for security.
			$api_key = isset( $config->api_key ) ? substr( $config->api_key, 0, 8 ) . '...' : '-';

			$items[] = [
				'ID'         => $service_config->getId(),
				'Name'       => $service_config->getName(),
				'Provider'   => $service_config->getProvider(),
				'URL'        => $url,
				'Status'     => $service_config->getStatus() ?: 'pending',
				'Checked At' => $service_config->getCheckedAt() ?: '-',
			];
		}

		$format    = WP_CLI\Utils\get_flag_value( $assoc_args, 'format', 'table' );
		$formatter = new Formatter( $assoc_args, [ 'ID', 'Name', 'Provider', 'URL', 'Status', 'Checked At' ] );
		$formatter->display_items( $items );
	}

	/**
	 * Test connection to a service.
	 *
	 * ## OPTIONS
	 *
	 * <service>
	 * : Service ID.
	 *
	 * [--verbose]
	 * : Show detailed response information.
	 *
	 * ## EXAMPLES
	 *
	 *     # Test service connection
	 *     wp celersearch service test 1
	 *
	 *     # Test with verbose output
	 *     wp celersearch service test 1 --verbose
	 *
	 * @when after_wp_load
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Associative arguments.
	 */
	public function test( $args, $assoc_args ) {
		$service_id = (int) $args[0];
		$verbose    = WP_CLI\Utils\get_flag_value( $assoc_args, 'verbose', false );

		$repo           = new ServiceRepository();
		$service_config = $repo->find( $service_id );

		if ( ! $service_config ) {
			WP_CLI::error( "Service not found: {$service_id}" );
		}

		$service_name = $service_config->getName();
		$provider     = $service_config->getProvider();
		$config       = $service_config->getConfig();

		WP_CLI::log( sprintf( 'Testing connection to "%s" (%s)...', $service_name, $provider ) );

		if ( $verbose ) {
			$url = isset( $config->url ) ? $config->url : 'Not configured';
			WP_CLI::log( sprintf( '  URL: %s', $url ) );
		}

		$start_time = microtime( true );

		try {
			$service = ServiceFactory::create( $service_id );

			// Test by getting health/version info based on provider.
			$success = $this->test_service_connection( $service, $provider, $verbose );
			$duration = round( ( microtime( true ) - $start_time ) * 1000, 2 );

			if ( $success ) {
				// Update service status to connected.
				$repo->update( $service_id, [
					'status'     => ServiceRepository::STATUS_CONNECTED,
					'checked_at' => gmdate( 'Y-m-d H:i:s' ),
				] );

				WP_CLI::success( sprintf( 'Connection successful! (%sms)', $duration ) );
			} else {
				WP_CLI::error( 'Connection test failed.' );
			}
		} catch ( \Exception $e ) {
			$duration = round( ( microtime( true ) - $start_time ) * 1000, 2 );

			// Update service status to failed.
			$repo->update( $service_id, [
				'status'     => ServiceRepository::STATUS_FAILING,
				'checked_at' => gmdate( 'Y-m-d H:i:s' ),
			] );

			WP_CLI::error( sprintf( 'Connection failed (%sms): %s', $duration, $e->getMessage() ) );
		}
	}

	/**
	 * Test service connection based on provider type.
	 *
	 * @param object $service  The service instance.
	 * @param string $provider The provider type.
	 * @param bool   $verbose  Whether to show verbose output.
	 *
	 * @return bool
	 */
	private function test_service_connection( $service, $provider, $verbose ) {
		switch ( $provider ) {
			case 'meilisearch':
				return $this->test_meilisearch_connection( $service, $verbose );

			default:
				WP_CLI::warning( "Unknown provider: {$provider}. Attempting generic test..." );
				return $this->test_generic_connection( $service, $verbose );
		}
	}

	/**
	 * Test MeiliSearch connection.
	 *
	 * @param object $service The MeiliSearch service instance.
	 * @param bool   $verbose Whether to show verbose output.
	 *
	 * @return bool
	 */
	private function test_meilisearch_connection( $service, $verbose ) {
		// Use reflection to access the protected client.
		$reflection = new \ReflectionClass( $service );
		$property   = $reflection->getProperty( 'client' );
		$property->setAccessible( true );
		$client = $property->getValue( $service );

		// Get health status.
		$health = $client->health();

		if ( $verbose ) {
			WP_CLI::log( sprintf( '  Health Status: %s', $health['status'] ?? 'unknown' ) );
		}

		// Get version info.
		$version = $client->version();

		if ( $verbose ) {
			WP_CLI::log( sprintf( '  MeiliSearch Version: %s', $version['pkgVersion'] ?? 'unknown' ) );
			WP_CLI::log( sprintf( '  Commit SHA: %s', substr( $version['commitSha'] ?? 'unknown', 0, 8 ) ) );
			WP_CLI::log( sprintf( '  Commit Date: %s', $version['commitDate'] ?? 'unknown' ) );
		}

		return isset( $health['status'] ) && $health['status'] === 'available';
	}

	/**
	 * Test generic connection (fallback).
	 *
	 * @param object $service The service instance.
	 * @param bool   $verbose Whether to show verbose output.
	 *
	 * @return bool
	 */
	private function test_generic_connection( $service, $verbose ) {
		// For unknown providers, just check if the service was created successfully.
		// The constructor should throw if connection fails.
		return true;
	}
}
