<?php
/**
 * Service for handling guest order linking verification.
 *
 * When a user creates an account and has guest orders matching their email,
 * this service manages the email verification flow to securely link those orders.
 *
 * @package Awesomemotive\Rewardswp\Services
 * @since 1.2.0
 */

namespace Awesomemotive\Rewardswp\Services;

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

use Awesomemotive\Rewardswp\Interfaces\IntegrationInterface;

/**
 * Class OrderLinkingVerificationService
 *
 * @since 1.2.0
 */
class OrderLinkingVerificationService {

	/**
	 * User meta key for the verification token hash.
	 *
	 * @var string
	 */
	const META_TOKEN = '_rewardswp_order_linking_token';

	/**
	 * User meta key for the token creation timestamp.
	 *
	 * @var string
	 */
	const META_TOKEN_CREATED = '_rewardswp_order_linking_token_created';

	/**
	 * User meta key for verification completion status.
	 *
	 * @var string
	 */
	const META_VERIFIED = '_rewardswp_order_linking_verified';

	/**
	 * Default token expiration in seconds (48 hours).
	 *
	 * @var int
	 */
	const TOKEN_EXPIRATION = 172800;

	/**
	 * Verification endpoint slug.
	 *
	 * @var string
	 */
	const VERIFICATION_ENDPOINT = 'rewardswp-verify-orders';

	/**
	 * Integration instance.
	 *
	 * @since 1.2.0
	 *
	 * @var IntegrationInterface
	 */
	private IntegrationInterface $integration;

	/**
	 * Constructor.
	 *
	 * @since 1.2.0
	 *
	 * @param IntegrationInterface $integration The integration instance.
	 */
	public function __construct( IntegrationInterface $integration ) {
		$this->integration = $integration;
	}

	/**
	 * Check if a user has guest orders that can be linked.
	 *
	 * @since 1.2.0
	 *
	 * @param int $user_id The WordPress user ID.
	 *
	 * @return bool True if guest orders exist and verification should be initiated.
	 */
	public function should_initiate_verification( int $user_id ): bool {
		// Skip if integration is not active.
		if ( ! $this->integration->is_active() ) {
			return false;
		}

		// Skip if already verified.
		if ( $this->is_already_verified( $user_id ) ) {
			return false;
		}

		// Check if guest orders exist.
		$user = get_userdata( $user_id );
		if ( ! $user || ! is_email( $user->user_email ) ) {
			return false;
		}

		return $this->count_guest_orders_for_email( $user->user_email ) > 0;
	}

	/**
	 * Count the number of guest orders for a given email.
	 *
	 * Delegates to the integration to count guest orders.
	 *
	 * @since 1.2.0
	 *
	 * @param string $email The email address to check.
	 *
	 * @return int Number of guest orders found.
	 */
	public function count_guest_orders_for_email( string $email ): int {
		return $this->integration->count_guest_orders( $email );
	}

	/**
	 * Generate and store a verification token for a user.
	 *
	 * @since 1.2.0
	 *
	 * @param int $user_id The WordPress user ID.
	 *
	 * @return string The generated token (unhashed).
	 */
	public function generate_verification_token( int $user_id ): string {
		// Generate cryptographically secure token (64 character hex string).
		$token = bin2hex( random_bytes( 32 ) );

		// Store hashed token and creation time.
		update_user_meta( $user_id, self::META_TOKEN, wp_hash( $token ) );
		update_user_meta( $user_id, self::META_TOKEN_CREATED, time() );

		// Clear any previous verification status (in case of re-verification).
		delete_user_meta( $user_id, self::META_VERIFIED );

		return $token;
	}

	/**
	 * Validate a verification token.
	 *
	 * @since 1.2.0
	 *
	 * @param int    $user_id The WordPress user ID.
	 * @param string $token   The token to validate (unhashed).
	 *
	 * @return array{valid: bool, error?: string, message?: string} Validation result.
	 */
	public function validate_token( int $user_id, string $token ): array {
		// Check if already verified.
		if ( $this->is_already_verified( $user_id ) ) {
			return [
				'valid'   => false,
				'error'   => 'already_verified',
				'message' => __( 'Your orders have already been linked to your account.', 'rewardswp' ),
			];
		}

		// Get stored token hash.
		$stored_hash = get_user_meta( $user_id, self::META_TOKEN, true );
		$created_at  = get_user_meta( $user_id, self::META_TOKEN_CREATED, true );

		// Check if token exists.
		if ( empty( $stored_hash ) || empty( $created_at ) ) {
			return [
				'valid'   => false,
				'error'   => 'invalid_token',
				'message' => __( 'This verification link is invalid. Please request a new one.', 'rewardswp' ),
			];
		}

		// Check expiration.
		$expiration = $this->get_token_expiration();
		if ( ( time() - (int) $created_at ) > $expiration ) {
			return [
				'valid'   => false,
				'error'   => 'expired_token',
				'message' => __( 'This verification link has expired. Please request a new one.', 'rewardswp' ),
			];
		}

		// Verify token hash (timing-safe comparison).
		if ( ! hash_equals( $stored_hash, wp_hash( $token ) ) ) {
			return [
				'valid'   => false,
				'error'   => 'invalid_token',
				'message' => __( 'This verification link is invalid. Please request a new one.', 'rewardswp' ),
			];
		}

		return [ 'valid' => true ];
	}

	/**
	 * Complete verification and link orders to the user.
	 *
	 * @since 1.2.0
	 *
	 * @param int $user_id The WordPress user ID.
	 *
	 * @return int Number of orders linked.
	 */
	public function complete_verification( int $user_id ): int {
		$user = get_userdata( $user_id );
		if ( ! $user ) {
			return 0;
		}

		// Mark as verified first to prevent race conditions.
		update_user_meta( $user_id, self::META_VERIFIED, true );

		// Clean up token data.
		delete_user_meta( $user_id, self::META_TOKEN );
		delete_user_meta( $user_id, self::META_TOKEN_CREATED );

		// Use the integration to link orders.
		return $this->integration->link_guest_orders( $user->user_email, $user_id );
	}

	/**
	 * Check if a user has already completed order linking verification.
	 *
	 * @since 1.2.0
	 *
	 * @param int $user_id The WordPress user ID.
	 *
	 * @return bool True if already verified.
	 */
	public function is_already_verified( int $user_id ): bool {
		return (bool) get_user_meta( $user_id, self::META_VERIFIED, true );
	}

	/**
	 * Get the verification URL for a user.
	 *
	 * URL format: /rewardswp-verify-orders/{user_id}/{token}
	 *
	 * @since 1.2.0
	 *
	 * @param int    $user_id The WordPress user ID.
	 * @param string $token   The verification token (unhashed).
	 *
	 * @return string The full verification URL.
	 */
	public function get_verification_url( int $user_id, string $token ): string {
		return trailingslashit( home_url( self::VERIFICATION_ENDPOINT . '/' . $user_id . '/' . $token ) );
	}

	/**
	 * Get the token expiration time in seconds.
	 *
	 * @since 1.2.0
	 *
	 * @return int Expiration time in seconds.
	 */
	public function get_token_expiration(): int {
		/**
		 * Filter the order linking verification token expiration time.
		 *
		 * @since 1.2.0
		 *
		 * @param int $expiration Expiration time in seconds. Default 172800 (48 hours).
		 */
		return (int) apply_filters( 'rewardswp_order_linking_token_expiration', self::TOKEN_EXPIRATION );
	}

	/**
	 * Get the token expiration time in hours (for display purposes).
	 *
	 * @since 1.2.0
	 *
	 * @return int Expiration time in hours.
	 */
	public function get_token_expiration_hours(): int {
		return (int) ceil( $this->get_token_expiration() / 3600 );
	}
}

