<?php
/**
 * WP-CLI Commands for ZipWP MCP.
 *
 * @package zip-ai
 * @since 1.0.0
 */

namespace ZipAI\MCP\Classes\Cli;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use ZipAI\MCP\Classes\Core\Utils;
use ZipAI\MCP\Classes\Core\Helper;
use WP_CLI;

/**
 * ZipWP MCP CLI Commands.
 *
 * @since 1.0.0
 */
class CLI_Commands {

	/**
	 * Setup the authentication token for ZipWP MCP.
	 *
	 * Encrypts the token using local Sodium key and saves to zip_mcp_settings.
	 *
	 * ## OPTIONS
	 *
	 * <token>
	 * : The plain text auth token to encrypt and store.
	 *
	 * [--email=<email>]
	 * : The user email to associate with the settings.
	 *
	 * ## EXAMPLES
	 *
	 *     # Binds the WP Application Password to the token on the SaaS too, so MCP
	 *     # tools work. App Passwords are SSL-gated + per-user — pass --url=https
	 *     # and a user context (--user, or --email which is resolved to a user).
	 *     wp zip-ai setup-token "your-auth-token" --user=admin --url="https://your-site"
	 *     wp zip-ai setup-token "your-auth-token" --email="user@example.com" --url="https://your-site"
	 *
	 * @subcommand setup-token
	 * @when after_wp_load
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Associative arguments.
	 * @return void
	 */
	public function setup_token( $args, $assoc_args ) {
		$token = $args[0] ?? '';
		$email = $assoc_args['email'] ?? '';

		if ( empty( $token ) ) {
			WP_CLI::error( 'Token is required.' );
			return;
		}

		// Encrypt the token using local Sodium key.
		$encrypted_token = Utils::encrypt( $token );

		if ( empty( $encrypted_token ) ) {
			WP_CLI::error( 'Failed to encrypt token. Sodium may not be available.' );
			return;
		}

		// Get existing settings or create new.
		$mcp_settings = get_option( 'zip_mcp_settings', array() );

		// Update with encrypted token.
		$mcp_settings['auth_token']        = $encrypted_token;
		$mcp_settings['auth_token_server'] = defined( 'ZIPAI_MCP_CREDIT_SERVER_API' ) ? untrailingslashit( ZIPAI_MCP_CREDIT_SERVER_API ) : '';
		$mcp_settings['enabled']           = true;

		if ( ! empty( $email ) ) {
			$mcp_settings['user_email'] = $email;
		}

		// Save settings.
		$result  = update_option( 'zip_mcp_settings', $mcp_settings );
		$current = get_option( 'zip_mcp_settings', array() );
		$stored  = $result || ( isset( $current['auth_token'] ) && $current['auth_token'] === $encrypted_token );

		if ( ! $stored ) {
			WP_CLI::error( 'Failed to save auth token.' );
			return;
		}

		WP_CLI::success( $result ? 'Auth token has been encrypted and saved.' : 'Auth token is already set (unchanged).' );

		// Bind the WP Application Password to THIS token on the SaaS. Without it
		// the token is MCP-blind: SaaS->WP tool discovery 401s and zero MCP
		// tools are packed into each turn. The OAuth "Connect" flow already
		// provisions on connect; the CLI/dev path historically skipped this,
		// which is how hand-set/dev tokens ended up unbound.
		$this->provision_app_password( $email );
	}

	/**
	 * Provision the WP Application Password and bind it to the just-stored
	 * Sanctum token on the SaaS, reporting an HONEST landed/not-landed result
	 * (the underlying push is soft-fail, so re-push to read its status).
	 *
	 * @since 1.0.0
	 *
	 * @param string $email Optional user email to resolve a user context under WP-CLI.
	 * @return void
	 */
	private function provision_app_password( $email ) {
		// Application Passwords are per-user, so provisioning needs a user
		// context. Under WP-CLI that comes from --user; fall back to --email.
		if ( get_current_user_id() <= 0 ) {
			$user = '' !== $email ? get_user_by( 'email', $email ) : null;
			if ( $user instanceof \WP_User ) {
				wp_set_current_user( $user->ID );
			} else {
				WP_CLI::warning( 'Token stored, but no user context — App Password NOT bound. Re-run with --user=<login> --url="https://your-site" (App Passwords are SSL-gated), or connect via the admin UI.' );
				return;
			}
		}

		$provision = Helper::ensure_app_password_provisioned();
		if ( empty( $provision['success'] ) ) {
			WP_CLI::warning( sprintf( 'Token stored, but App Password not provisioned (%s): %s', $provision['code'] ?? 'unknown', $provision['message'] ?? '' ) );
			if ( 'app_passwords_disabled' === ( $provision['code'] ?? '' ) ) {
				WP_CLI::warning( 'App Passwords are SSL-gated under WP-CLI — re-run with --url="https://your-site".' );
			}
			return;
		}

		// ensure_app_password_provisioned() pushes the bind soft-fail (it
		// swallows the HTTP result). Re-push explicitly — the SaaS endpoint is
		// idempotent — so we can report whether the bind actually landed.
		$header = Helper::get_decrypted_app_password_authorization();
		if ( '' !== $header && Helper::push_app_password_to_saas( $header ) ) {
			WP_CLI::success( 'WP Application Password bound to this token on the SaaS — MCP tools are available.' );
		} else {
			WP_CLI::warning( 'App Password provisioned locally but the SaaS bind did NOT land — MCP tools will 401 until it succeeds. Check connectivity to the credit server.' );
		}
	}

	/**
	 * Revoke/remove the authentication token.
	 *
	 * ## EXAMPLES
	 *
	 *     wp zip-ai revoke-token
	 *
	 * @subcommand revoke-token
	 * @when after_wp_load
	 *
	 * @return void
	 */
	public function revoke_token() {
		$mcp_settings = get_option( 'zip_mcp_settings', array() );

		unset( $mcp_settings['auth_token'] );
		$mcp_settings['enabled'] = false;

		update_option( 'zip_mcp_settings', $mcp_settings );

		WP_CLI::success( 'Auth token has been revoked.' );
	}

}
