<?php

namespace LLAR\Core\MfaFlow\Providers\Email;

use LLAR\Core\MfaFlow\MfaApiClient;
use LLAR\Core\MfaFlow\Providers\MfaProviderInterface;
use LLAR\Core\MfaFlow\MfaRestApi;

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

/**
 * Email MFA provider: handshake and verify via API (LLA_MFA_API_BASE_URL + LLA_MFA_API_PATH).
 * Builds send_email_url and send_email_url_fallback from send_email_secret for the handshake payload.
 * External app redirects user here; app calls site send_email_url to deliver OTP.
 */
class LlarMfaProvider implements MfaProviderInterface {

	const PROVIDER_ID = 'llar';
	const AJAX_ACTION = 'llar_mfa_flow_send_code';

	/** Content-ID token for embedded OTP email logo (multipart/related). */
	const OTP_EMAIL_LOGO_CID = 'llar_mfa_otp_logo';

	/**
	 * Absolute path to logo file for one-shot phpmailer_init embed (cleared after wp_mail).
	 *
	 * @var string
	 */
	private static $otp_email_embed_path = '';

	/**
	 * @var string
	 */
	private static $otp_email_embed_cid = '';

	/**
	 * @return string
	 */
	public function get_id() {
		return self::PROVIDER_ID;
	}

	/**
	 * @return string
	 */
	public function get_label() {
		return __( 'External app (LLAR MFA API)', 'limit-login-attempts-reloaded' );
	}

	/**
	 * Build send_email_url (REST) and send_email_url_fallback (AJAX) for handshake API.
	 * No secret in URL; caller must send secret in POST body. send_email_secret stays in payload for the API to use.
	 *
	 * @param string|null $send_email_secret Unused (kept for interface); secret is passed in handshake payload for POST body.
	 * @return array { send_email_url: string, send_email_url_fallback: string }
	 */
	public function build_send_email_urls( $send_email_secret = null ) {
		$send_email_url          = MfaRestApi::get_send_code_rest_url();
		$send_email_url_fallback = add_query_arg(
			array(
				'action'      => self::AJAX_ACTION,
				'_ajax_nonce' => wp_create_nonce( self::AJAX_ACTION ),
			),
			admin_url( 'admin-ajax.php' )
		);
		return array(
			'send_email_url'          => $send_email_url,
			'send_email_url_fallback' => $send_email_url_fallback,
		);
	}

	/**
	 * @param array $payload user_ip, login_url, user_group, is_pre_authenticated.
	 * Secret is generated by MFA app and returned in response; plugin sends only send_email_url(s).
	 * @return array { success: bool, data: array|null, error: string|null }
	 */
	public function handshake( array $payload ) {
		$urls    = $this->build_send_email_urls();
		$payload = array_merge( $payload, $urls );
		$opts    = $this->get_request_options();
		$api     = new MfaApiClient();
		return $api->handshake( $payload, $opts );
	}

	/**
	 * @param string $token  Session token.
	 * @param string $secret Session secret.
	 * @return array { success: bool, data: array|null, error: string|null }
	 */
	public function verify( $token, $secret ) {
		$opts = $this->get_request_options();
		$api  = new MfaApiClient();
		return $api->verify( $token, $secret, $opts );
	}

	/**
	 * Send verification code to the user by email.
	 *
	 * @param \WP_User $user    User to send code to.
	 * @param string   $code    Verification code.
	 * @param array    $context Optional. Keys: ip, browser, location (from email endpoint request).
	 * @return array { success: bool, message: string|null }
	 */
	public function send_code( $user, $code, $context = array() ) {
		if ( ! $user || ! is_a( $user, 'WP_User' ) || empty( $user->user_email ) ) {
			return array(
				'success' => true,
				'message' => null,
			);
		}

		$subject = __( 'Verify your login', 'limit-login-attempts-reloaded' );

		$site_url    = home_url();
		$site_parsed = wp_parse_url( $site_url );
		$site_domain = ( is_array( $site_parsed ) && ! empty( $site_parsed['host'] ) ) ? $site_parsed['host'] : str_replace( array( 'http://', 'https://' ), '', $site_url );

		$ip_from_ctx       = isset( $context['ip'] ) && is_string( $context['ip'] ) ? $context['ip'] : '';
		$browser_from_ctx  = isset( $context['browser'] ) && is_string( $context['browser'] ) ? $context['browser'] : '';
		$location_from_ctx = isset( $context['location'] ) && is_string( $context['location'] ) ? $context['location'] : '';

		$timestamp   = current_time( 'timestamp' );
		$date_format = get_option( 'date_format' );
		$time_format = get_option( 'time_format' );
		$format      = is_string( $date_format ) && is_string( $time_format ) && $date_format !== '' && $time_format !== ''
			? $date_format . ' ' . $time_format
			: 'F j, Y \a\t g:i A';
		$time_label  = date_i18n( $format, $timestamp );

		$ttl_seconds = defined( 'LLA_MFA_FLOW_OTP_TTL' ) ? (int) LLA_MFA_FLOW_OTP_TTL : 180;
		$ttl_seconds = $ttl_seconds > 0 ? $ttl_seconds : 180;
		$code_ttl    = (int) max( 1, ceil( $ttl_seconds / 60 ) );

		$code_safe        = $this->sanitize_mfa_email_code( $code );
		$site_domain_safe = $this->sanitize_mfa_email_field( (string) $site_domain, 253 );
		$ip_safe          = $this->sanitize_mfa_email_ip( $ip_from_ctx );
		$location_safe    = $this->sanitize_mfa_email_field( $location_from_ctx, 500 );
		$browser_safe     = $this->sanitize_mfa_email_field( $browser_from_ctx, 500 );
		$time_safe        = $this->sanitize_mfa_email_field( (string) $time_label, 200 );
		$code_ttl_minutes = $code_ttl;

		$llar_mfa_otp_logo_cid = '';
		$otp_logo_path        = '';
		if ( defined( 'LLA_PLUGIN_DIR' ) ) {
			$otp_logo_path = LLA_PLUGIN_DIR . 'assets/img/llar-logo-email.png';
			if ( file_exists( $otp_logo_path ) && is_readable( $otp_logo_path ) ) {
				$llar_mfa_otp_logo_cid = self::OTP_EMAIL_LOGO_CID;
			}
		}

		ob_start();
		include LLA_PLUGIN_DIR . 'views/emails/mfa-verification.php';
		$body = (string) ob_get_clean();

		$headers  = array( 'Content-Type: text/html; charset=UTF-8' );
		$to_email = $user->user_email;

		if ( $llar_mfa_otp_logo_cid !== '' ) {
			self::$otp_email_embed_path = $otp_logo_path;
			self::$otp_email_embed_cid  = $llar_mfa_otp_logo_cid;
			add_action( 'phpmailer_init', array( __CLASS__, 'phpmailer_embed_otp_logo' ), 10, 1 );
		}

		$sent = false;
		try {
			$sent = wp_mail( $to_email, $subject, $body, $headers );
		} finally {
			if ( $llar_mfa_otp_logo_cid !== '' ) {
				remove_action( 'phpmailer_init', array( __CLASS__, 'phpmailer_embed_otp_logo' ), 10 );
				self::$otp_email_embed_path = '';
				self::$otp_email_embed_cid  = '';
			}
		}
		if ( $sent ) {
			return array(
				'success' => true,
				'message' => null,
			);
		}
		return array(
			'success' => false,
			'message' => 'Failed to send email',
		);
	}

	/**
	 * Config fields for admin. Endpoint from constants (LLA_MFA_API_BASE_URL, LLA_MFA_API_PATH).
	 *
	 * @return array
	 */
	public function get_config_fields() {
		return array();
	}

	/**
	 * OTP / code for email body: no HTML, length-limited.
	 *
	 * @param mixed $code Raw code from API.
	 * @return string
	 */
	private function sanitize_mfa_email_code( $code ) {
		$s = is_string( $code ) ? $code : '';
		$s = preg_replace( '/\s+/', '', wp_strip_all_tags( $s ) );
		if ( strlen( $s ) > 64 ) {
			$s = substr( $s, 0, 64 );
		}
		return $s;
	}

	/**
	 * Single-line display field for email meta rows.
	 *
	 * @param mixed $value      Context value.
	 * @param int   $max_length Max bytes (ASCII-safe truncation).
	 * @return string
	 */
	private function sanitize_mfa_email_field( $value, $max_length ) {
		if ( ! is_string( $value ) ) {
			$value = '';
		}
		$out = sanitize_text_field( $value );
		if ( $max_length > 0 && strlen( $out ) > $max_length ) {
			$out = substr( $out, 0, $max_length );
		}
		return $out;
	}

	/**
	 * IP for display: valid IPv4/IPv6 or sanitized fallback.
	 *
	 * @param mixed $ip Context IP string.
	 * @return string
	 */
	private function sanitize_mfa_email_ip( $ip ) {
		if ( ! is_string( $ip ) ) {
			return '';
		}
		$ip = trim( $ip );
		if ( $ip !== '' && filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 ) ) {
			return $ip;
		}
		return $this->sanitize_mfa_email_field( $ip, 45 );
	}

	/**
	 * Build request options (base_url from LLA_MFA_API_BASE_URL + LLA_MFA_API_PATH).
	 *
	 * @return array
	 */
	private function get_request_options() {
		$base = defined( 'LLA_MFA_API_BASE_URL' ) ? rtrim( (string) LLA_MFA_API_BASE_URL, '/' ) : 'https://api.limitloginattempts.com';
		$path = defined( 'LLA_MFA_API_PATH' ) ? (string) LLA_MFA_API_PATH : '/mfa';
		if ( $path !== '' && substr( $path, 0, 1 ) !== '/' ) {
			$path = '/' . $path;
		}
		$base_url = $path !== '' ? $base . $path : $base;
		return array( 'base_url' => $base_url );
	}

	/**
	 * Add bundled PNG as inline image (CID) for HTML multipart/related.
	 *
	 * @param object $phpmailer PHPMailer instance from wp_mail.
	 */
	public static function phpmailer_embed_otp_logo( $phpmailer ) {
		if ( self::$otp_email_embed_path === '' || ! is_string( self::$otp_email_embed_path ) ) {
			return;
		}
		if ( ! file_exists( self::$otp_email_embed_path ) || ! is_readable( self::$otp_email_embed_path ) ) {
			return;
		}
		$cid = self::$otp_email_embed_cid !== '' ? self::$otp_email_embed_cid : self::OTP_EMAIL_LOGO_CID;
		if ( method_exists( $phpmailer, 'addEmbeddedImage' ) ) {
			$phpmailer->addEmbeddedImage( self::$otp_email_embed_path, $cid, 'llar-logo.png', 'base64', 'image/png' );
		} elseif ( method_exists( $phpmailer, 'AddEmbeddedImage' ) ) {
			$phpmailer->AddEmbeddedImage( self::$otp_email_embed_path, $cid, 'llar-logo.png', 'base64', 'image/png' );
		}
	}
}
