<?php
/**
 * Fraud prevention service for validating referral and reward actions.
 *
 * @package RewardsWP
 */

namespace Awesomemotive\Rewardswp\Services;

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

use Awesomemotive\Rewardswp\Interfaces\IntegrationInterface;
use Awesomemotive\Rewardswp\Models\Member;
use function Awesomemotive\Rewardswp\Helpers\get_member_by;
use function Awesomemotive\Rewardswp\Helpers\get_referrals;
use function Awesomemotive\Rewardswp\Helpers\get_referrals_from_current_user_ip;
class FraudPreventionService {
	/**
	 * The limit of referrals that can be generated by the same IP address.
	 *
	 * @since 1.0.0
	 *
	 * @var int
	 */
	const REFERRAL_LIMIT_PER_IP_ADDRESS = 10;

	/**
	 * The current integration being used by this site.
	 *
	 * @since 1.0.0
	 *
	 * @var IntegrationInterface
	 */
	private IntegrationInterface $integration;

	/**
	 * Tracking service.
	 *
	 * @since 1.0.0
	 *
	 * @var TrackingService
	 */
	private TrackingService $tracking_service;

	/**
	 * Constructor.
	 *
	 * @since 1.0.0
	 *
	 * @param IntegrationInterface $integration      The integration instance.
	 * @param TrackingService      $tracking_service The tracking service.
	 */
	public function __construct( IntegrationInterface $integration, TrackingService $tracking_service ) {
		$this->integration      = $integration;
		$this->tracking_service = $tracking_service;
	}

	/**
	 * Normalize an email for fraud detection.
	 *
	 * @param string $email The email to normalize.
	 *
	 * @return string Normalized email.
	 */
	private function normalize_email( string $email ): string {
		$parts = explode( '@', $email );
		if ( count( $parts ) !== 2 ) {
			return $email; // Invalid email, return as-is.
		}

		[ $local, $domain ] = $parts;

		// Normalize the local part: remove everything after "+" and convert to lowercase.
		$local = strtolower( explode( '+', $local )[0] );

		// Gmail-specific normalization: remove dots in the local part.
		if ( in_array( $domain, [ 'gmail.com', 'googlemail.com' ], true ) ) {
			$local = str_replace( '.', '', $local );
		}

		return $local . '@' . strtolower( $domain );
	}

	/**
	 * Determine if an email represents a self-referral attempt.
	 *
	 * @param string $email         The email used to claim the reward.
	 * @param string $referral_code The referrer code.
	 *
	 * @return bool Whether the emails are considered self-referrals.
	 */
	public function is_self_referral( string $email, string $referral_code ): bool {
		$advocate = get_member_by( 'referral_code', $referral_code );

		if ( ! is_a( $advocate, Member::class ) ) {
			return false;
		}

		// Normalize both emails for comparison.
		$normalized_email          = $this->normalize_email( $email );
		$normalized_advocate_email = $this->normalize_email( $advocate->email );

		if ( $normalized_email === $normalized_advocate_email ) {
			return true;
		}

		// Use Levenshtein to catch minor variations.
		if ( levenshtein( $normalized_email, $normalized_advocate_email ) <= 2 ) {
			return true; // Emails are very similar, possible fraud.
		}

		return false;
	}

	/**
	 * Check if a visit generated referrals.
	 *
	 * @since 1.0.0
	 *
	 * @param string $referral_code The referral code to check.
	 *
	 * @return bool Whether the visit already generated referrals.
	 */
	public function has_visit_generated_referral( string $referral_code ): bool {
		$advocate = get_member_by( 'referral_code', $referral_code );

		return (bool) get_referrals(
			[
				'event_id'    => $this->tracking_service->get_cookie( $this->tracking_service::VISIT_ID_COOKIE_NAME ),
				'advocate_id' => $advocate->id ?? 0,
			]
		);
	}

	/**
	 * Check if the current user surpassed the limit of referrals from the same IP address.
	 *
	 * @since 1.0.0
	 *
	 * @return bool Whether it surpassed the limit or not.
	 */
	public function has_exceeded_referrals_from_user_ip(): bool {
		if ( count( get_referrals_from_current_user_ip() ) >= self::REFERRAL_LIMIT_PER_IP_ADDRESS ) {
			return true;
		}

		return false;
	}

	/**
	 * Check if the email represents an existent customer on this site.
	 *
	 * @since 1.0.0
	 *
	 * @param string $email The email to check if it is already a customer.
	 *
	 * @return bool Whether it's already a customer or not.
	 */
	public function is_customer( string $email ): bool {
		return $this->integration->is_customer( $email );
	}
}
