<?php
/**
 * `wp blaminhor smtp ...` — SMTP module commands.
 *
 * @package BlaminhorEssentials
 */

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

if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) {
	return;
}

/**
 * Configure the SMTP mailer module from the command line.
 *
 * Mirrors the admin UI: general settings (from/name/logging), relay CRUD
 * (add/list/delete), and a real wp_mail() test that exercises the same
 * pipeline as a production email.
 */
class Blaminhor_Essentials_CLI_SMTP extends Blaminhor_Essentials_CLI_Module {

	/**
	 * Module ID matches the slug in register_modules().
	 *
	 * @var string
	 */
	protected $module_id = 'smtp';

	/**
	 * Show the current SMTP settings (with relay passwords masked).
	 *
	 * ## OPTIONS
	 *
	 * [--format=<format>]
	 * : Render format (table, json, csv, yaml). Default: table.
	 *
	 * [--show-relays]
	 * : Include the relay list in the output.
	 *
	 * ## EXAMPLES
	 *
	 *     $ wp blaminhor smtp get
	 *     $ wp blaminhor smtp get --show-relays --format=json
	 *
	 * @when after_wp_load
	 *
	 * @param array $args       Positional args (unused).
	 * @param array $assoc_args Associative args.
	 */
	public function get( $args, $assoc_args ) {
		$module   = $this->get_module_instance();
		$settings = $module->get_effective_settings();

		$rows = array(
			array( 'key' => 'from_email',         'value' => (string) $settings['from_email'] ),
			array( 'key' => 'from_name',          'value' => (string) $settings['from_name'] ),
			array( 'key' => 'force_from_email',   'value' => $settings['force_from_email'] ? 'true' : 'false' ),
			array( 'key' => 'force_from_name',    'value' => $settings['force_from_name'] ? 'true' : 'false' ),
			array( 'key' => 'return_path',        'value' => $settings['return_path'] ? 'true' : 'false' ),
			array( 'key' => 'enable_logging',     'value' => $settings['enable_logging'] ? 'true' : 'false' ),
			array( 'key' => 'log_retention_days', 'value' => (string) (int) $settings['log_retention_days'] ),
			array( 'key' => 'disable_ssl_verify', 'value' => $settings['disable_ssl_verify'] ? 'true' : 'false' ),
			array( 'key' => 'relays_count',       'value' => (string) count( $settings['relays'] ) ),
		);

		if ( isset( $assoc_args['show-relays'] ) && ! empty( $settings['relays'] ) ) {
			foreach ( $settings['relays'] as $i => $relay ) {
				$rows[] = array(
					'key'   => sprintf( 'relay[%d]', $i ),
					'value' => sprintf(
						'%s — %s:%d (%s) — from=%s — %s',
						isset( $relay['name'] ) ? $relay['name'] : '?',
						isset( $relay['host'] ) ? $relay['host'] : '?',
						isset( $relay['port'] ) ? (int) $relay['port'] : 0,
						isset( $relay['encryption'] ) ? $relay['encryption'] : 'none',
						isset( $relay['from_email'] ) ? $relay['from_email'] : '?',
						! empty( $relay['enabled'] ) ? 'enabled' : 'disabled'
					),
				);
			}
		}

		$this->format_for_output( $rows, $assoc_args, array( 'key', 'value' ) );
	}

	/**
	 * Update SMTP general settings.
	 *
	 * Only the flags you pass are modified; the rest is preserved. Boolean
	 * flags accept 1/0, true/false, yes/no, on/off. Pass `false` to
	 * explicitly turn a checkbox off.
	 *
	 * ## OPTIONS
	 *
	 * [--from-email=<email>]
	 * : Default From email address used for outgoing mail.
	 *
	 * [--from-name=<name>]
	 * : Default From name.
	 *
	 * [--force-from-email=<bool>]
	 * : Override every plugin's From email with the value above.
	 *
	 * [--force-from-name=<bool>]
	 * : Override every plugin's From name with the value above.
	 *
	 * [--return-path=<bool>]
	 * : Set Return-Path to match From email.
	 *
	 * [--enable-logging=<bool>]
	 * : Log every email sent through the module.
	 *
	 * [--log-retention-days=<days>]
	 * : Number of days to keep email logs (integer >= 0).
	 *
	 * [--disable-ssl-verify=<bool>]
	 * : Skip SSL certificate verification (debug only — not for production).
	 *
	 * ## EXAMPLES
	 *
	 *     $ wp blaminhor smtp set --from-email=hello@example.com --from-name="Hello"
	 *     $ wp blaminhor smtp set --enable-logging=true --log-retention-days=60
	 *
	 * @when after_wp_load
	 *
	 * @param array $args       Positional args (unused).
	 * @param array $assoc_args Associative args.
	 */
	public function set( $args, $assoc_args ) {
		$this->ensure_capabilities();
		$module   = $this->get_module_instance();
		$current  = $module->get_effective_settings();

		// Start from the current values so save_general re-applies them all.
		$data = array(
			'save_general'       => '1',
			'from_email'         => (string) $current['from_email'],
			'from_name'          => (string) $current['from_name'],
			'log_retention_days' => (string) (int) $current['log_retention_days'],
		);

		// Booleans: include the key (any value) to enable, omit to disable.
		$bool_settings = array(
			'force-from-email'    => 'force_from_email',
			'force-from-name'     => 'force_from_name',
			'return-path'         => 'return_path',
			'enable-logging'      => 'enable_logging',
			'disable-ssl-verify'  => 'disable_ssl_verify',
		);

		foreach ( $bool_settings as $cli_flag => $setting_key ) {
			if ( array_key_exists( $cli_flag, $assoc_args ) ) {
				if ( $this->parse_bool( $assoc_args[ $cli_flag ] ) ) {
					$data[ $setting_key ] = '1';
				}
				// else: omit to disable, no key needed.
			} elseif ( ! empty( $current[ $setting_key ] ) ) {
				// Preserve existing "on" state when flag not passed.
				$data[ $setting_key ] = '1';
			}
		}

		// String overrides.
		if ( isset( $assoc_args['from-email'] ) ) {
			$data['from_email'] = (string) $assoc_args['from-email'];
		}
		if ( isset( $assoc_args['from-name'] ) ) {
			$data['from_name'] = (string) $assoc_args['from-name'];
		}
		if ( isset( $assoc_args['log-retention-days'] ) ) {
			$data['log_retention_days'] = (string) (int) $assoc_args['log-retention-days'];
		}

		$result = $module->apply_settings( $data );
		if ( is_wp_error( $result ) ) {
			WP_CLI::error( $result->get_error_message() );
		}

		blaminhor_essentials_log( 'SMTP general settings updated via WP-CLI.', 'info', 'cli', array( 'event' => 'smtp_set' ) );
		WP_CLI::success( __( 'SMTP settings updated.', 'blaminhor-essentials' ) );
	}

	/**
	 * Send a test email through the configured SMTP pipeline.
	 *
	 * Uses wp_mail() so the same hooks fire as in production: PHPMailer
	 * configuration by the module, From/Return-Path overrides, logging.
	 *
	 * ## OPTIONS
	 *
	 * [--to=<email>]
	 * : Destination address. Defaults to the current site's admin email.
	 *
	 * ## EXAMPLES
	 *
	 *     $ wp blaminhor smtp test
	 *     $ wp blaminhor smtp test --to=qa@example.com
	 *
	 * @when after_wp_load
	 *
	 * @param array $args       Positional args (unused).
	 * @param array $assoc_args Associative args.
	 */
	public function test( $args, $assoc_args ) {
		$this->ensure_capabilities();
		$module = $this->get_module_instance();

		$to = isset( $assoc_args['to'] ) ? sanitize_email( (string) $assoc_args['to'] ) : sanitize_email( (string) get_option( 'admin_email' ) );
		if ( ! is_email( $to ) ) {
			WP_CLI::error( __( 'Invalid recipient email. Pass --to=<valid-email>.', 'blaminhor-essentials' ) );
		}

		// Same configuration sanity check as the admin AJAX handler so the
		// CLI fails fast instead of dispatching an undeliverable message.
		$global_from = $module->get_setting( 'from_email' );
		$has_relay   = false;
		foreach ( (array) $module->get_setting( 'relays', array() ) as $relay ) {
			if ( ! empty( $relay['enabled'] ) && ! empty( $relay['from_email'] ) ) {
				$has_relay = true;
				break;
			}
		}
		if ( empty( $global_from ) && ! $has_relay ) {
			WP_CLI::error( __( 'No "From Email" configured. Set a From email in general settings or add an enabled relay first.', 'blaminhor-essentials' ) );
		}

		$subject = sprintf(
			/* translators: %s: site name */
			__( '[%s] Test Email from Blaminhor Essentials SMTP (CLI)', 'blaminhor-essentials' ),
			get_bloginfo( 'name' )
		);
		$body = '<p>' . esc_html__( 'This test email was triggered via wp blaminhor smtp test.', 'blaminhor-essentials' ) . '</p>'
			. '<p><strong>Site:</strong> ' . esc_html( home_url() ) . '<br>'
			. '<strong>Time:</strong> ' . esc_html( current_time( 'mysql' ) ) . '</p>';

		$result = wp_mail( $to, $subject, $body, array( 'Content-Type: text/html; charset=UTF-8' ) );

		if ( $result ) {
			blaminhor_essentials_log( sprintf( 'SMTP test email dispatched to %s via WP-CLI.', $to ), 'info', 'cli', array( 'event' => 'smtp_test' ) );
			WP_CLI::success( sprintf(
				/* translators: %s: recipient email */
				__( 'Test email dispatched to %s.', 'blaminhor-essentials' ),
				$to
			) );
			return;
		}

		global $phpmailer;
		$err = ( isset( $phpmailer ) && isset( $phpmailer->ErrorInfo ) ) ? $phpmailer->ErrorInfo : __( 'Unknown error', 'blaminhor-essentials' );
		WP_CLI::error( sprintf(
			/* translators: %s: PHPMailer error message */
			__( 'wp_mail() returned false. PHPMailer error: %s', 'blaminhor-essentials' ),
			$err
		) );
	}

	/**
	 * Add a new SMTP relay.
	 *
	 * Sensitive: the password is encrypted via blaminhor_essentials_encrypt()
	 * before being stored, matching the admin form behaviour. Prefer passing
	 * --password through an env var (BLAMINHOR_SMTP_PASSWORD) to keep it out
	 * of shell history.
	 *
	 * ## OPTIONS
	 *
	 * --name=<name>
	 * : Human-readable label for the relay.
	 *
	 * --host=<host>
	 * : SMTP server host (e.g. smtp.gmail.com).
	 *
	 * --port=<port>
	 * : SMTP port (e.g. 587, 465, 25).
	 *
	 * --from-email=<email>
	 * : From email address used by this relay (required).
	 *
	 * [--provider=<provider>]
	 * : Provider slug (sendgrid, mailgun, smtpcom, brevo, sparkpost, other). Default: other.
	 *
	 * [--encryption=<encryption>]
	 * : Encryption: none, tls, ssl. Default: tls.
	 *
	 * [--username=<username>]
	 * : SMTP username.
	 *
	 * [--password=<password>]
	 * : SMTP password. If omitted, read from BLAMINHOR_SMTP_PASSWORD env var.
	 *
	 * [--from-name=<name>]
	 * : From name used by this relay.
	 *
	 * [--enabled=<bool>]
	 * : Whether the relay is enabled. Default: true.
	 *
	 * ## EXAMPLES
	 *
	 *     $ wp blaminhor smtp add-relay --name=Gmail --host=smtp.gmail.com --port=587 \
	 *           --encryption=tls --username=foo@example.com --from-email=hello@example.com
	 *     $ BLAMINHOR_SMTP_PASSWORD='s3cret' wp blaminhor smtp add-relay --name=... --password=
	 *
	 * @subcommand add-relay
	 * @when after_wp_load
	 *
	 * @param array $args       Positional args (unused).
	 * @param array $assoc_args Associative args.
	 */
	public function add_relay( $args, $assoc_args ) {
		$this->ensure_capabilities();
		$module = $this->get_module_instance();

		$required = array( 'name', 'host', 'port', 'from-email' );
		foreach ( $required as $flag ) {
			if ( ! isset( $assoc_args[ $flag ] ) || '' === (string) $assoc_args[ $flag ] ) {
				WP_CLI::error( sprintf(
					/* translators: %s: flag name */
					__( 'Missing required --%s.', 'blaminhor-essentials' ),
					$flag
				) );
			}
		}

		$from_email = sanitize_email( (string) $assoc_args['from-email'] );
		if ( ! is_email( $from_email ) ) {
			WP_CLI::error( __( '--from-email must be a valid email address.', 'blaminhor-essentials' ) );
		}

		$password = isset( $assoc_args['password'] ) ? (string) $assoc_args['password'] : '';
		if ( '' === $password ) {
			$env = getenv( 'BLAMINHOR_SMTP_PASSWORD' );
			if ( false !== $env && '' !== $env ) {
				$password = (string) $env;
			}
		}

		$data = array(
			'add_relay'        => '1',
			'relay_name'       => (string) $assoc_args['name'],
			'relay_provider'   => isset( $assoc_args['provider'] ) ? (string) $assoc_args['provider'] : 'other',
			'relay_host'       => (string) $assoc_args['host'],
			'relay_port'       => (string) (int) $assoc_args['port'],
			'relay_encryption' => isset( $assoc_args['encryption'] ) ? (string) $assoc_args['encryption'] : 'tls',
			'relay_username'   => isset( $assoc_args['username'] ) ? (string) $assoc_args['username'] : '',
			'relay_password'   => $password,
			'relay_from_email' => $from_email,
			'relay_from_name'  => isset( $assoc_args['from-name'] ) ? (string) $assoc_args['from-name'] : '',
		);

		$enabled = isset( $assoc_args['enabled'] ) ? $this->parse_bool( $assoc_args['enabled'] ) : true;
		if ( $enabled ) {
			$data['relay_enabled'] = '1';
		}

		$result = $module->apply_settings( $data );
		if ( is_wp_error( $result ) ) {
			WP_CLI::error( $result->get_error_message() );
		}

		$relays = (array) $module->get_setting( 'relays', array() );
		$index  = count( $relays ) - 1;

		blaminhor_essentials_log( sprintf( 'SMTP relay "%s" added via WP-CLI (index %d).', $data['relay_name'], $index ), 'info', 'cli', array( 'event' => 'smtp_add_relay' ) );
		WP_CLI::success( sprintf(
			/* translators: 1: relay name, 2: zero-based index */
			__( 'Relay "%1$s" added at index %2$d.', 'blaminhor-essentials' ),
			$data['relay_name'],
			$index
		) );
	}

	/**
	 * List the configured SMTP relays.
	 *
	 * Passwords are never returned in any format. The relay's zero-based
	 * index is the identifier you pass to other commands (e.g. delete-relay).
	 *
	 * ## OPTIONS
	 *
	 * [--format=<format>]
	 * : Render format (table, json, csv, yaml, count, ids). Default: table.
	 *
	 * [--fields=<fields>]
	 * : Subset of columns to display. Available: index, name, provider, host,
	 *   port, encryption, username, from_email, from_name, enabled.
	 *
	 * ## EXAMPLES
	 *
	 *     $ wp blaminhor smtp list-relays
	 *     $ wp blaminhor smtp list-relays --format=json
	 *
	 * @subcommand list-relays
	 * @when after_wp_load
	 *
	 * @param array $args       Positional args (unused).
	 * @param array $assoc_args Associative args.
	 */
	public function list_relays( $args, $assoc_args ) {
		$module = $this->get_module_instance();
		$relays = (array) $module->get_setting( 'relays', array() );

		$rows = array();
		foreach ( $relays as $i => $relay ) {
			$rows[] = array(
				'index'      => (string) $i,
				'name'       => isset( $relay['name'] ) ? $relay['name'] : '',
				'provider'   => isset( $relay['provider'] ) ? $relay['provider'] : '',
				'host'       => isset( $relay['host'] ) ? $relay['host'] : '',
				'port'       => isset( $relay['port'] ) ? (string) (int) $relay['port'] : '',
				'encryption' => isset( $relay['encryption'] ) ? $relay['encryption'] : '',
				'username'   => isset( $relay['username'] ) ? $relay['username'] : '',
				'from_email' => isset( $relay['from_email'] ) ? $relay['from_email'] : '',
				'from_name'  => isset( $relay['from_name'] ) ? $relay['from_name'] : '',
				'enabled'    => ! empty( $relay['enabled'] ) ? 'true' : 'false',
			);
		}

		$this->format_for_output(
			$rows,
			$assoc_args,
			array( 'index', 'name', 'host', 'port', 'encryption', 'from_email', 'enabled' )
		);
	}

	/**
	 * Delete an SMTP relay by index.
	 *
	 * ## OPTIONS
	 *
	 * <index>
	 * : Zero-based index of the relay (see `list-relays`).
	 *
	 * [--yes]
	 * : Skip the interactive confirmation prompt.
	 *
	 * ## EXAMPLES
	 *
	 *     $ wp blaminhor smtp delete-relay 0
	 *     $ wp blaminhor smtp delete-relay 1 --yes
	 *
	 * @subcommand delete-relay
	 * @when after_wp_load
	 *
	 * @param array $args       Positional args: [<index>].
	 * @param array $assoc_args Associative args.
	 */
	public function delete_relay( $args, $assoc_args ) {
		$this->ensure_capabilities();
		$module = $this->get_module_instance();

		if ( ! isset( $args[0] ) || ! is_numeric( $args[0] ) ) {
			WP_CLI::error( __( 'Missing relay index. Usage: wp blaminhor smtp delete-relay <index>', 'blaminhor-essentials' ) );
		}
		$index  = (int) $args[0];
		$relays = (array) $module->get_setting( 'relays', array() );
		if ( ! isset( $relays[ $index ] ) ) {
			WP_CLI::error( sprintf(
				/* translators: %d: relay index */
				__( 'No relay at index %d.', 'blaminhor-essentials' ),
				$index
			) );
		}

		WP_CLI::confirm(
			sprintf(
				/* translators: 1: relay name, 2: index */
				__( 'Delete relay "%1$s" at index %2$d?', 'blaminhor-essentials' ),
				isset( $relays[ $index ]['name'] ) ? $relays[ $index ]['name'] : '?',
				$index
			),
			$assoc_args
		);

		$result = $module->apply_settings( array(
			'delete_relay' => '1',
			'relay_index'  => (string) $index,
		) );
		if ( is_wp_error( $result ) ) {
			WP_CLI::error( $result->get_error_message() );
		}

		blaminhor_essentials_log( sprintf( 'SMTP relay #%d deleted via WP-CLI.', $index ), 'info', 'cli', array( 'event' => 'smtp_delete_relay' ) );
		WP_CLI::success( sprintf(
			/* translators: %d: relay index */
			__( 'Relay #%d deleted.', 'blaminhor-essentials' ),
			$index
		) );
	}
}
