<?php
/**
 * Phone and two-factor fields on the user profile.
 *
 * @package TextTango
 */

declare( strict_types=1 );

namespace TextTango\Otp;

use TextTango\Admin\Menu;
use TextTango\Api\PhoneNumber;
use WP_User;

defined( 'ABSPATH' ) || exit;

/**
 * Lets a user see and manage their sign-in phone number.
 *
 * The number can only be *cleared* here, never changed to a different one:
 * setting it by hand would attach an unverified number to an account, and
 * anyone who could edit the profile could then receive that account's codes.
 * A new number is attached only by verifying it (spec §5).
 */
final class ProfileFields {

	/**
	 * Nonce action for profile updates.
	 */
	private const NONCE = 'texttango_profile';

	/**
	 * Register hooks.
	 */
	public function register(): void {
		add_action( 'show_user_profile', array( $this, 'render' ) );
		add_action( 'edit_user_profile', array( $this, 'render' ) );
		add_action( 'personal_options_update', array( $this, 'save' ) );
		add_action( 'edit_user_profile_update', array( $this, 'save' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
	}

	/**
	 * Load the plugin's admin bundle on the profile screens.
	 *
	 * {@see Menu::enqueue_assets()} only covers the plugin's own pages, and the
	 * attach widget lives on a core screen.
	 *
	 * @param string $hook_suffix Current admin screen.
	 */
	public function enqueue_assets( string $hook_suffix ): void {
		if ( ! in_array( $hook_suffix, array( 'profile.php', 'user-edit.php' ), true ) ) {
			return;
		}

		Menu::enqueue_plugin_assets( PhoneVerification::NONCE );
	}

	/**
	 * Output the fields.
	 *
	 * @param WP_User $user User being edited.
	 */
	public function render( WP_User $user ): void {
		$phone    = UserRepository::phone_for( $user->ID );
		$verified = (string) get_user_meta( $user->ID, UserRepository::META_VERIFIED_AT, true );
		$enabled  = '1' === (string) get_user_meta( $user->ID, UserRepository::META_2FA, true );

		// A number is attached by proving possession of the handset, so only the
		// account's own owner can do it — never an administrator on their behalf.
		$is_own_profile = get_current_user_id() === $user->ID;
		?>
		<h2><?php esc_html_e( 'TextTango SMS', 'texttango' ); ?></h2>
		<table class="form-table" role="presentation">
			<tr>
				<th scope="row"><?php esc_html_e( 'Sign-in phone number', 'texttango' ); ?></th>
				<td>
					<?php if ( '' !== $phone ) : ?>
						<p>
							<code><?php echo esc_html( PhoneNumber::mask( $phone ) ); ?></code>
							<?php if ( '' !== $verified ) : ?>
								<span class="description">
									<?php
									printf(
										/* translators: %s: date the number was last verified. */
										esc_html__( 'verified %s', 'texttango' ),
										esc_html( $verified )
									);
									?>
								</span>
							<?php endif; ?>
						</p>
						<p>
							<label>
								<input type="checkbox" name="texttango_remove_phone" value="1" />
								<?php esc_html_e( 'Remove this number', 'texttango' ); ?>
							</label>
						</p>
						<p class="description">
							<?php esc_html_e( 'A number is attached only by verifying a code sent to it, so it cannot be edited here.', 'texttango' ); ?>
						</p>
					<?php elseif ( ! $is_own_profile ) : ?>
						<p class="description">
							<?php esc_html_e( 'No verified number. Only this account\'s owner can add one, by verifying a code sent to the handset.', 'texttango' ); ?>
						</p>
					<?php endif; ?>

					<?php if ( $is_own_profile ) : ?>
						<div class="texttango-ui" data-texttango-attach>
							<div class="tt-mt-2 tt-max-w-md">
								<div data-texttango-attach-step="phone">
									<div class="tt-flex tt-flex-wrap tt-items-end tt-gap-2">
										<div class="tt-grow">
											<label class="tt-label" for="texttango-attach-phone">
												<?php
												echo '' !== $phone
													? esc_html__( 'Replace with a different number', 'texttango' )
													: esc_html__( 'Add your phone number', 'texttango' );
												?>
											</label>
											<input
												class="tt-input"
												id="texttango-attach-phone"
												type="tel"
												autocomplete="tel"
												placeholder="+233…"
												data-texttango-attach-phone />
										</div>
										<button type="button" class="tt-btn tt-btn-secondary" data-texttango-action="attach-request">
											<?php esc_html_e( 'Send code', 'texttango' ); ?>
										</button>
									</div>
									<p class="tt-hint">
										<?php esc_html_e( 'We text you a code to confirm the number is yours. This sends a real SMS and uses credits from your TextTango balance.', 'texttango' ); ?>
									</p>
								</div>

								<div data-texttango-attach-step="code" hidden>
									<div class="tt-flex tt-flex-wrap tt-items-end tt-gap-2">
										<div class="tt-grow">
											<label class="tt-label" for="texttango-attach-code">
												<?php esc_html_e( 'Verification code', 'texttango' ); ?>
											</label>
											<input
												class="tt-input"
												id="texttango-attach-code"
												type="text"
												inputmode="numeric"
												pattern="[0-9]*"
												autocomplete="one-time-code"
												maxlength="<?php echo esc_attr( (string) OtpService::CODE_LENGTH ); ?>"
												data-texttango-attach-code />
										</div>
										<button type="button" class="tt-btn tt-btn-primary" data-texttango-action="attach-verify">
											<?php esc_html_e( 'Verify', 'texttango' ); ?>
										</button>
										<button type="button" class="tt-btn tt-btn-secondary" data-texttango-action="attach-back">
											<?php esc_html_e( 'Change number', 'texttango' ); ?>
										</button>
									</div>
								</div>

								<p class="tt-text-sm tt-mt-2" data-texttango-attach-result aria-live="polite"></p>
							</div>
						</div>
					<?php endif; ?>
				</td>
			</tr>
			<?php if ( TwoFactor::is_enabled() ) : ?>
				<tr>
					<th scope="row"><?php esc_html_e( 'Two-factor authentication', 'texttango' ); ?></th>
					<td>
						<label>
							<input type="checkbox" name="texttango_2fa_enabled" value="1" <?php checked( $enabled ); ?>
								<?php disabled( '' === $phone ); ?> />
							<?php esc_html_e( 'Ask for an SMS code after my password', 'texttango' ); ?>
						</label>
						<?php if ( '' === $phone ) : ?>
							<p class="description">
								<?php esc_html_e( 'Attach a verified phone number first.', 'texttango' ); ?>
							</p>
						<?php endif; ?>
					</td>
				</tr>
			<?php endif; ?>
		</table>
		<?php
		wp_nonce_field( self::NONCE, 'texttango_profile_nonce' );
	}

	/**
	 * Persist the fields.
	 *
	 * @param int $user_id User being saved.
	 */
	public function save( int $user_id ): void {
		if ( ! current_user_can( 'edit_user', $user_id ) ) {
			return;
		}

		$nonce = isset( $_POST['texttango_profile_nonce'] )
			? sanitize_text_field( wp_unslash( $_POST['texttango_profile_nonce'] ) )
			: '';

		if ( ! wp_verify_nonce( $nonce, self::NONCE ) ) {
			return;
		}

		if ( isset( $_POST['texttango_remove_phone'] ) ) {
			delete_user_meta( $user_id, UserRepository::META_PHONE );
			delete_user_meta( $user_id, UserRepository::META_VERIFIED_AT );

			// A second factor with no number to send it to would lock the user out.
			delete_user_meta( $user_id, UserRepository::META_2FA );

			return;
		}

		if ( '' === UserRepository::phone_for( $user_id ) ) {
			return;
		}

		update_user_meta(
			$user_id,
			UserRepository::META_2FA,
			isset( $_POST['texttango_2fa_enabled'] ) ? '1' : '0'
		);
	}
}
