<?php
/**
 * OTP (One-Time Password) via email — Fase 2FA semplificata.
 *
 * @package SPIDy_WP
 */

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

class SPIDy_WP_OTP {

	const OTP_TRANSIENT_PREFIX      = 'spidy_wp_otp_';
	const ATTEMPTS_TRANSIENT_PREFIX = 'spidy_wp_attempts_';
	const OTP_TTL                   = 10 * MINUTE_IN_SECONDS;
	const MAX_ATTEMPTS              = 5;

	/**
	 * Genera un codice a 6 cifre e lo salva (hashato) in un transient
	 * legato all'utente. Non lo invia — la spedizione (email/SMS) è
	 * gestita separatamente per poter scegliere/alternare canale.
	 */
	public static function generate( int $user_id ): string {
		$code = (string) random_int( 100000, 999999 );

		// Storage diretto invece dei transient nativi: stessa scelta già
		// fatta altrove nel plugin, per lo stesso motivo (affidabilità su
		// questo hosting quando il valore deve sopravvivere qualche minuto).
		SPIDy_WP_Login::store_value_direct( self::OTP_TRANSIENT_PREFIX . $user_id, wp_hash_password( $code ), self::OTP_TTL );
		SPIDy_WP_Login::store_value_direct( self::ATTEMPTS_TRANSIENT_PREFIX . $user_id, 0, self::OTP_TTL );

		return $code;
	}

	/**
	 * Invia un codice già generato via email.
	 */
	public static function send_email( int $user_id, string $code ): bool {
		$user = get_userdata( $user_id );
		if ( ! $user ) {
			return false;
		}

		$subject = sprintf(
			/* translators: %s: nome del sito */
			__( '[%s] Il tuo codice di accesso', 'spidy-2fa-login' ),
			get_bloginfo( 'name' )
		);

		$message = sprintf(
			/* translators: 1: nome utente, 2: codice OTP, 3: minuti di validità */
			__( "Ciao %1\$s,\n\nIl tuo codice di accesso è: %2\$s\n\nÈ valido per %3\$d minuti. Se non hai richiesto l'accesso, ignora questa email.", 'spidy-2fa-login' ),
			$user->display_name,
			$code,
			self::OTP_TTL / MINUTE_IN_SECONDS
		);

		return wp_mail( $user->user_email, $subject, $message );
	}

	/**
	 * Invia un codice già generato via SMS (Twilio/Clickatell). Ritorna true/WP_Error.
	 */
	public static function send_sms( string $phone_number, string $code ) {
		$message = sprintf(
			/* translators: 1: codice OTP, 2: minuti di validità */
			__( 'Codice di accesso: %1$s (valido %2$d minuti)', 'spidy-2fa-login' ),
			$code,
			self::OTP_TTL / MINUTE_IN_SECONDS
		);

		return SPIDy_WP_SMS::send( $phone_number, $message );
	}

	/**
	 * Compatibilità: genera e invia via email in un solo passaggio
	 * (usato quando il metodo scelto è "solo email").
	 */
	public static function generate_and_send( int $user_id ): bool {
		$code = self::generate( $user_id );
		return self::send_email( $user_id, $code );
	}

	/**
	 * Verifica il codice inserito dall'utente. Limita i tentativi per
	 * evitare brute force sul codice a 6 cifre.
	 *
	 * @return true|WP_Error
	 */
	public static function verify( int $user_id, string $submitted_code ) {
		$stored_hash = SPIDy_WP_Login::retrieve_value_direct( self::OTP_TRANSIENT_PREFIX . $user_id );

		if ( false === $stored_hash ) {
			return new WP_Error( 'spidy_wp_expired', __( 'Il codice è scaduto. Richiedine uno nuovo.', 'spidy-2fa-login' ) );
		}

		$attempts = (int) SPIDy_WP_Login::retrieve_value_direct( self::ATTEMPTS_TRANSIENT_PREFIX . $user_id );

		if ( $attempts >= self::MAX_ATTEMPTS ) {
			self::clear( $user_id );
			return new WP_Error( 'spidy_wp_locked', __( 'Troppi tentativi errati. Richiedi un nuovo codice.', 'spidy-2fa-login' ) );
		}

		if ( ! wp_check_password( $submitted_code, $stored_hash ) ) {
			SPIDy_WP_Login::store_value_direct( self::ATTEMPTS_TRANSIENT_PREFIX . $user_id, $attempts + 1, self::OTP_TTL );
			return new WP_Error( 'spidy_wp_invalid', __( 'Codice non corretto.', 'spidy-2fa-login' ) );
		}

		self::clear( $user_id );
		return true;
	}

	public static function clear( int $user_id ) {
		SPIDy_WP_Login::delete_value_direct( self::OTP_TRANSIENT_PREFIX . $user_id );
		SPIDy_WP_Login::delete_value_direct( self::ATTEMPTS_TRANSIENT_PREFIX . $user_id );
	}
}
