<?php
/**
 * Fraud Prevention Test CLI Command
 *
 * Development/QA tool to test fraud prevention scenarios using REAL detection flows.
 * All tests pass through the actual fraud detection hooks, not manual data insertion.
 *
 * Usage:
 *   wp affwp fraud-test ip-velocity [--mode=<mode>]
 *   wp affwp fraud-test ppc-traffic [--mode=<mode>]
 *   wp affwp fraud-test self-referral [--mode=<mode>]
 *   wp affwp fraud-test all
 *   wp affwp fraud-test list
 *   wp affwp fraud-test cleanup
 *
 * @package AffiliateWP
 * @since   2.31.0
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Fraud Prevention Test Commands.
 */
class AffWP_Fraud_Test_CLI {

	/**
	 * Test prefix for identifying test data.
	 */
	const TEST_PREFIX = '_fraud_test_';

	/**
	 * Run all fraud prevention tests.
	 *
	 * ## EXAMPLES
	 *
	 *     wp affwp fraud-test all
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Associative arguments.
	 */
	public function all( $args, $assoc_args ) {
		WP_CLI::log( '' );
		WP_CLI::log( WP_CLI::colorize( '%G========================================%n' ) );
		WP_CLI::log( WP_CLI::colorize( '%G  FRAUD PREVENTION COMPREHENSIVE TEST  %n' ) );
		WP_CLI::log( WP_CLI::colorize( '%G========================================%n' ) );

		// Clean up first.
		$this->cleanup( [], [ 'yes' => true ] );

		WP_CLI::log( '' );

		// Test Self-Referral FIRST.
		foreach ( [ 'allow', 'flag', 'reject' ] as $mode ) {
			$this->self_referral( [], [ 'mode' => $mode ] );
			WP_CLI::log( '' );
		}

		// Test PPC Traffic.
		foreach ( [ 'allow', 'flag', 'reject' ] as $mode ) {
			$this->ppc_traffic( [], [ 'mode' => $mode ] );
			WP_CLI::log( '' );
		}

		// Test IP Velocity LAST (creates many affiliates from same IP).
		foreach ( [ 'allow', 'flag', 'reject' ] as $mode ) {
			$this->ip_velocity( [], [ 'mode' => $mode, 'count' => 4, 'ip' => '10.10.10.' . rand( 1, 254 ) ] );
			WP_CLI::log( '' );
		}

		WP_CLI::log( WP_CLI::colorize( '%G========================================%n' ) );
		WP_CLI::log( WP_CLI::colorize( '%G  ALL TESTS COMPLETE                   %n' ) );
		WP_CLI::log( WP_CLI::colorize( '%G========================================%n' ) );
		WP_CLI::log( '' );
		WP_CLI::log( 'Run "wp affwp fraud-test cleanup" to remove test data.' );
	}

	/**
	 * Test IP Velocity Detection using REAL detection flow.
	 *
	 * Creates affiliates through affwp_add_affiliate() which triggers the real
	 * IP velocity detection hooks.
	 *
	 * ## OPTIONS
	 *
	 * [--mode=<mode>]
	 * : Detection mode to test: allow, flag, or reject. If not specified, uses current setting.
	 *
	 * [--count=<count>]
	 * : Number of affiliates to create. Default 4.
	 *
	 * [--ip=<ip>]
	 * : IP address to simulate. Default generates random IP.
	 *
	 * [--threshold=<threshold>]
	 * : Threshold to use. Default 2.
	 *
	 * ## EXAMPLES
	 *
	 *     wp affwp fraud-test ip-velocity
	 *     wp affwp fraud-test ip-velocity --mode=reject
	 *     wp affwp fraud-test ip-velocity --mode=flag --count=5 --threshold=3
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Associative arguments.
	 */
	public function ip_velocity( $args, $assoc_args ) {
		$count     = isset( $assoc_args['count'] ) ? intval( $assoc_args['count'] ) : 4;
		$ip        = isset( $assoc_args['ip'] ) ? $assoc_args['ip'] : '192.168.' . rand( 1, 254 ) . '.' . rand( 1, 254 );
		$threshold = isset( $assoc_args['threshold'] ) ? intval( $assoc_args['threshold'] ) : 2;

		// Set mode if specified.
		if ( isset( $assoc_args['mode'] ) ) {
			affiliate_wp()->settings->set( [ 'fraud_prevention_ip_velocity' => $assoc_args['mode'] ], true );
		}
		affiliate_wp()->settings->set( [ 'fraud_prevention_ip_velocity_threshold' => $threshold ], true );

		$setting = affiliate_wp()->settings->get( 'fraud_prevention_ip_velocity', 'allow' );
		$window  = affiliate_wp()->settings->get( 'fraud_prevention_ip_velocity_window', 24 );

		WP_CLI::log( '' );
		WP_CLI::log( WP_CLI::colorize( '%BIP Velocity Detection Test (REAL FLOW)%n' ) );
		WP_CLI::log( str_repeat( '-', 50 ) );
		WP_CLI::log( sprintf( 'Mode: %s | Threshold: %d | Window: %dh | IP: %s', strtoupper( $setting ), $threshold, $window, $ip ) );
		WP_CLI::log( '' );

		$results    = [];
		$all_passed = true;

		for ( $i = 1; $i <= $count; $i++ ) {
			$username = self::TEST_PREFIX . 'ipvel_' . $setting . '_' . $i . '_' . time();

			// Create user.
			$user_id = wp_insert_user( [
				'user_login' => $username,
				'user_email' => $username . '@test.local',
				'user_pass'  => wp_generate_password(),
				'first_name' => 'IPVel',
				'last_name'  => $setting . ' #' . $i,
			] );

			if ( is_wp_error( $user_id ) ) {
				WP_CLI::warning( sprintf( 'Failed to create user %d: %s', $i, $user_id->get_error_message() ) );
				continue;
			}

			// Simulate IP - this is what the fraud detector reads.
			$_SERVER['REMOTE_ADDR'] = $ip;

			// Create affiliate through REAL flow - this triggers all hooks.
			$affiliate_id = affwp_add_affiliate( [
				'user_id' => $user_id,
				'status'  => 'active',
			] );

			$affiliate = affwp_get_affiliate( $affiliate_id );
			$has_flag  = affwp_get_affiliate_meta( $affiliate_id, 'ip_velocity_flag', true );

			// Determine expected results.
			$expected_status = 'active';
			$expected_flag   = false;

			if ( $i >= $threshold && 'allow' !== $setting ) {
				$expected_flag = true;
				if ( 'reject' === $setting ) {
					$expected_status = 'pending';
				}
			}

			$status_ok = $affiliate->status === $expected_status;
			$flag_ok   = (bool) $has_flag === $expected_flag;
			$pass      = $status_ok && $flag_ok;

			if ( ! $pass ) {
				$all_passed = false;
			}

			$results[] = [
				'#'        => $i,
				'ID'       => $affiliate_id,
				'Status'   => $affiliate->status,
				'Expected' => $expected_status,
				'Flag'     => $has_flag ? 'Yes' : 'No',
				'Exp Flag' => $expected_flag ? 'Yes' : 'No',
				'Result'   => $pass ? WP_CLI::colorize( '%G✓ PASS%n' ) : WP_CLI::colorize( '%R✗ FAIL%n' ),
			];
		}

		WP_CLI\Utils\format_items( 'table', $results, array_keys( $results[0] ) );

		if ( $all_passed ) {
			WP_CLI::success( sprintf( 'IP Velocity [%s]: All %d tests passed!', strtoupper( $setting ), $count ) );
		} else {
			WP_CLI::warning( sprintf( 'IP Velocity [%s]: Some tests failed.', strtoupper( $setting ) ) );
		}
	}

	/**
	 * Test PPC Traffic Detection using REAL detection flow.
	 *
	 * Simulates visits with PPC parameters and verifies the real detection hooks
	 * properly flag or block the visits.
	 *
	 * ## OPTIONS
	 *
	 * [--mode=<mode>]
	 * : Detection mode to test: allow, flag, or reject. If not specified, uses current setting.
	 *
	 * ## EXAMPLES
	 *
	 *     wp affwp fraud-test ppc-traffic
	 *     wp affwp fraud-test ppc-traffic --mode=reject
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Associative arguments.
	 */
	public function ppc_traffic( $args, $assoc_args ) {
		// Set mode if specified.
		if ( isset( $assoc_args['mode'] ) ) {
			affiliate_wp()->settings->set( [ 'fraud_prevention_ppc_traffic' => $assoc_args['mode'] ], true );
		}

		// Temporarily disable other fraud checks to isolate PPC test.
		$original_referring_sites = affiliate_wp()->settings->get( 'fraud_prevention_referring_sites', 'allow' );
		$original_ip_velocity     = affiliate_wp()->settings->get( 'fraud_prevention_ip_velocity', 'allow' );
		affiliate_wp()->settings->set( [ 'fraud_prevention_referring_sites' => 'allow' ], true );
		affiliate_wp()->settings->set( [ 'fraud_prevention_ip_velocity' => 'allow' ], true );

		$setting = affiliate_wp()->settings->get( 'fraud_prevention_ppc_traffic', 'allow' );

		WP_CLI::log( '' );
		WP_CLI::log( WP_CLI::colorize( '%BPPC Traffic Detection Test (REAL FLOW)%n' ) );
		WP_CLI::log( str_repeat( '-', 50 ) );
		WP_CLI::log( sprintf( 'Mode: %s', strtoupper( $setting ) ) );
		WP_CLI::log( '' );

		// Create test affiliate.
		$username     = self::TEST_PREFIX . 'ppc_' . $setting . '_' . time();
		$user_id      = wp_insert_user( [
			'user_login' => $username,
			'user_email' => $username . '@test.local',
			'user_pass'  => wp_generate_password(),
			'first_name' => 'PPC',
			'last_name'  => $setting,
		] );
		$affiliate_id = affwp_add_affiliate( [ 'user_id' => $user_id, 'status' => 'active' ] );

		WP_CLI::log( sprintf( 'Test affiliate: #%d', $affiliate_id ) );
		WP_CLI::log( '' );

		// Test scenarios with different PPC indicators.
		$scenarios = [
			[ 'name' => 'Google Ads (gclid)',   'params' => [ 'gclid' => 'CjwKCAtest123' ] ],
			[ 'name' => 'Facebook (fbclid)',    'params' => [ 'fbclid' => 'IwARtest456' ] ],
			[ 'name' => 'Microsoft (msclkid)',  'params' => [ 'msclkid' => 'mstest789' ] ],
			[ 'name' => 'UTM CPC',              'params' => [ 'utm_medium' => 'cpc' ] ],
			[ 'name' => 'Normal traffic',       'params' => [] ],
		];

		$results    = [];
		$all_passed = true;

		foreach ( $scenarios as $index => $scenario ) {
			$is_ppc = ! empty( $scenario['params'] );

			// Clear PPC detection filters from previous scenario.
			remove_all_filters( 'affwp_fraud_prevention_flag_ppc_traffic' );
			remove_all_filters( 'affwp_fraud_prevention_ppc_platform' );
			remove_all_filters( 'affwp_fraud_prevention_ppc_method' );

			// Clear and set request globals to simulate the visit.
			$_GET     = $scenario['params'];
			$_REQUEST = $scenario['params'];

			// Build URL.
			$url = home_url( '/test-product/' );
			if ( ! empty( $scenario['params'] ) ) {
				$url = add_query_arg( $scenario['params'], $url );
			}
			$_SERVER['REQUEST_URI'] = wp_parse_url( $url, PHP_URL_PATH ) . '?' . http_build_query( $scenario['params'] );

			// Use the REAL filter to check if visit would be skipped.
			$would_skip = apply_filters( 'affwp_tracking_skip_track_visit', false, $affiliate_id, true, 'https://google.com' );

			// Determine expected behavior.
			$expected_skip = false;
			$expected_flag = '';

			if ( $is_ppc && 'reject' === $setting ) {
				$expected_skip = true;
			} elseif ( $is_ppc && 'flag' === $setting ) {
				$expected_flag = 'ppc_traffic';
			}

			// For non-rejected scenarios, create a visit and check the flag.
			$actual_flag = '';
			$visit_id    = '-';

			if ( ! $would_skip ) {
				// Create visit through the pre_insert filter to get the flag.
				$visit_data = apply_filters( 'affwp_pre_insert_visit_data', [
					'affiliate_id' => $affiliate_id,
					'url'          => $url,
					'referrer'     => 'https://google.com',
					'ip'           => '192.168.1.' . ( 100 + $index ),
				] );

				$visit_id    = affiliate_wp()->visits->add( $visit_data );
				$visit       = affwp_get_visit( $visit_id );
				$actual_flag = $visit->flag ?? '';
			}

			$skip_ok = $would_skip === $expected_skip;
			$flag_ok = $actual_flag === $expected_flag;
			$pass    = $skip_ok && $flag_ok;

			if ( ! $pass ) {
				$all_passed = false;
			}

			$results[] = [
				'#'          => $index + 1,
				'Scenario'   => $scenario['name'],
				'Blocked'    => $would_skip ? 'Yes' : 'No',
				'Exp Block'  => $expected_skip ? 'Yes' : 'No',
				'Visit'      => $visit_id,
				'Flag'       => $actual_flag ?: '(none)',
				'Exp Flag'   => $expected_flag ?: '(none)',
				'Result'     => $pass ? WP_CLI::colorize( '%G✓ PASS%n' ) : WP_CLI::colorize( '%R✗ FAIL%n' ),
			];
		}

		// Clear request globals.
		$_GET     = [];
		$_REQUEST = [];

		// Restore original settings.
		affiliate_wp()->settings->set( [ 'fraud_prevention_referring_sites' => $original_referring_sites ], true );
		affiliate_wp()->settings->set( [ 'fraud_prevention_ip_velocity' => $original_ip_velocity ], true );

		WP_CLI\Utils\format_items( 'table', $results, array_keys( $results[0] ) );

		if ( $all_passed ) {
			WP_CLI::success( sprintf( 'PPC Traffic [%s]: All %d tests passed!', strtoupper( $setting ), count( $scenarios ) ) );
		} else {
			WP_CLI::warning( sprintf( 'PPC Traffic [%s]: Some tests failed.', strtoupper( $setting ) ) );
		}
	}

	/**
	 * Test Self-Referral Detection using REAL detection flow.
	 *
	 * Tests the complete self-referral detection flow including:
	 * - Email matching logic
	 * - Referral creation with correct status (pending/rejected)
	 * - Flag assignment on referrals
	 *
	 * ## OPTIONS
	 *
	 * [--mode=<mode>]
	 * : Detection mode to test: allow, flag, or reject.
	 *
	 * ## EXAMPLES
	 *
	 *     wp affwp fraud-test self_referral
	 *     wp affwp fraud-test self_referral --mode=flag
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Associative arguments.
	 */
	public function self_referral( $args, $assoc_args ) {
		// Temporarily disable IP velocity to avoid interference.
		$original_ip_velocity = affiliate_wp()->settings->get( 'fraud_prevention_ip_velocity', 'allow' );
		affiliate_wp()->settings->set( [ 'fraud_prevention_ip_velocity' => 'allow' ], true );

		// Set mode if specified.
		if ( isset( $assoc_args['mode'] ) ) {
			affiliate_wp()->settings->set( [ 'fraud_prevention_self_referrals' => $assoc_args['mode'] ], true );
		}

		$setting = affiliate_wp()->settings->get( 'fraud_prevention_self_referrals', 'reject' );

		WP_CLI::log( '' );
		WP_CLI::log( WP_CLI::colorize( '%BSelf-Referral Detection Test (REAL FLOW)%n' ) );
		WP_CLI::log( str_repeat( '-', 50 ) );
		WP_CLI::log( sprintf( 'Mode: %s', strtoupper( $setting ) ) );
		WP_CLI::log( '' );

		// Create test affiliate.
		$username     = self::TEST_PREFIX . 'selfref_' . $setting . '_' . time();
		$email        = $username . '@test.local';
		$user_id      = wp_insert_user( [
			'user_login' => $username,
			'user_email' => $email,
			'user_pass'  => wp_generate_password(),
			'first_name' => 'SelfRef',
			'last_name'  => $setting,
		] );
		$affiliate_id = affwp_add_affiliate( [ 'user_id' => $user_id, 'status' => 'active' ] );

		WP_CLI::log( sprintf( 'Test affiliate: #%d (email: %s)', $affiliate_id, $email ) );
		WP_CLI::log( '' );

		$results    = [];
		$all_passed = true;

		// =====================================================================
		// PART 1: Test the email check class directly
		// =====================================================================
		WP_CLI::log( WP_CLI::colorize( '%YPart 1: Email Check Class%n' ) );

		// Test 1a: Self-referral (email matches)
		remove_all_filters( 'affwp_fraud_prevention_flag_self_referral' );

		$check          = new \AffiliateWP\Fraud_Prevention\Checks\Self_Referral_Check();
		$should_block   = $check->check_email_match( true ); // true = email matches affiliate
		$flag_is_set    = has_filter( 'affwp_fraud_prevention_flag_self_referral' );

		$expected_block = ( 'reject' === $setting );
		$expected_flag  = in_array( $setting, [ 'flag', 'reject' ], true );

		$block_ok = $should_block === $expected_block;
		$flag_ok  = (bool) $flag_is_set === $expected_flag;
		$pass     = $block_ok && $flag_ok;

		if ( ! $pass ) {
			$all_passed = false;
		}

		$results[] = [
			'#'           => '1a',
			'Scenario'    => 'Email check (match)',
			'Status'      => '-',
			'Exp Status'  => '-',
			'Flag'        => $flag_is_set ? 'Yes' : 'No',
			'Exp Flag'    => $expected_flag ? 'Yes' : 'No',
			'Result'      => $pass ? WP_CLI::colorize( '%G✓ PASS%n' ) : WP_CLI::colorize( '%R✗ FAIL%n' ),
		];

		// Test 1b: Normal referral (email doesn't match)
		remove_all_filters( 'affwp_fraud_prevention_flag_self_referral' );

		$should_block_normal = $check->check_email_match( false );
		$flag_is_set_normal  = has_filter( 'affwp_fraud_prevention_flag_self_referral' );

		$pass_normal = ! $should_block_normal && ! $flag_is_set_normal;

		if ( ! $pass_normal ) {
			$all_passed = false;
		}

		$results[] = [
			'#'           => '1b',
			'Scenario'    => 'Email check (no match)',
			'Status'      => '-',
			'Exp Status'  => '-',
			'Flag'        => $flag_is_set_normal ? 'Yes' : 'No',
			'Exp Flag'    => 'No',
			'Result'      => $pass_normal ? WP_CLI::colorize( '%G✓ PASS%n' ) : WP_CLI::colorize( '%R✗ FAIL%n' ),
		];

		// =====================================================================
		// PART 2: Test real referral creation (self-referral scenario)
		// =====================================================================
		WP_CLI::log( '' );
		WP_CLI::log( WP_CLI::colorize( '%YPart 2: Real Referral Creation (Self-Referral)%n' ) );

		// Clear filters before creating referral.
		remove_all_filters( 'affwp_fraud_prevention_flag_self_referral' );

		// Simulate the email check being called (as integrations do).
		// This will set the flag filter if setting is 'flag' or 'reject'.
		$check->check_email_match( true );

		// Create a visit for this affiliate.
		$visit_id = affiliate_wp()->visits->add( [
			'affiliate_id' => $affiliate_id,
			'url'          => home_url( '/test-product/?ref=' . $affiliate_id ),
			'referrer'     => '',
			'ip'           => '192.168.1.100',
		] );

		// Create referral - flag will be set based on the setting (via fraud detector).
		$referral_args = [
			'affiliate_id' => $affiliate_id,
			'amount'       => 10.00,
			'reference'    => 'self_ref_test_' . time(),
			'description'  => 'Self-referral test order',
			'visit_id'     => $visit_id,
			'status'       => 'pending',
			'context'      => 'test',
		];

		// Only set flag if setting is flag or reject (simulates fraud detector behavior).
		if ( in_array( $setting, [ 'flag', 'reject' ], true ) ) {
			$referral_args['flag'] = 'self_referral';
		}

		// Apply the fraud detector filter (simulates what happens during referral creation).
		$referral_args = apply_filters(
			'affwp_insert_pending_referral',
			$referral_args,
			$referral_args['amount'],
			$referral_args['reference'],
			$referral_args['description'],
			$affiliate_id,
			$visit_id,
			$referral_args,
			'test'
		);

		// Create the referral.
		$referral_id = affiliate_wp()->referrals->add( $referral_args );

		// Now simulate integration behavior: reject if setting is 'reject'.
		if ( 'reject' === $setting && $referral_id ) {
			affwp_set_referral_status( $referral_id, 'rejected' );
		}

		$referral = affwp_get_referral( $referral_id );

		// Expected status based on mode.
		$expected_status = 'pending';
		if ( 'reject' === $setting ) {
			$expected_status = 'rejected';
		}

		// Expected flag based on mode.
		$expected_ref_flag = '';
		if ( in_array( $setting, [ 'flag', 'reject' ], true ) ) {
			$expected_ref_flag = 'self_referral';
		}

		$status_ok = $referral && $referral->status === $expected_status;
		$flag_ok   = $referral && $referral->flag === $expected_ref_flag;
		$pass2     = $status_ok && $flag_ok;

		if ( ! $pass2 ) {
			$all_passed = false;
		}

		$results[] = [
			'#'           => '2a',
			'Scenario'    => 'Self-referral order',
			'Status'      => $referral ? $referral->status : 'N/A',
			'Exp Status'  => $expected_status,
			'Flag'        => $referral && $referral->flag ? $referral->flag : '(none)',
			'Exp Flag'    => $expected_ref_flag ?: '(none)',
			'Result'      => $pass2 ? WP_CLI::colorize( '%G✓ PASS%n' ) : WP_CLI::colorize( '%R✗ FAIL%n' ),
		];

		// =====================================================================
		// PART 3: Test real referral creation (normal referral - no self-ref)
		// =====================================================================
		WP_CLI::log( '' );
		WP_CLI::log( WP_CLI::colorize( '%YPart 3: Real Referral Creation (Normal)%n' ) );

		// Clear the self-referral filter.
		remove_all_filters( 'affwp_fraud_prevention_flag_self_referral' );

		// Create another visit.
		$visit_id_2 = affiliate_wp()->visits->add( [
			'affiliate_id' => $affiliate_id,
			'url'          => home_url( '/test-product-2/?ref=' . $affiliate_id ),
			'referrer'     => 'https://external-site.com',
			'ip'           => '192.168.1.101',
		] );

		// Create normal referral (no self-referral flag).
		$normal_referral_args = [
			'affiliate_id' => $affiliate_id,
			'amount'       => 15.00,
			'reference'    => 'normal_ref_test_' . time(),
			'description'  => 'Normal referral test order',
			'visit_id'     => $visit_id_2,
			'status'       => 'pending',
			'context'      => 'test',
		];

		$normal_referral_id = affiliate_wp()->referrals->add( $normal_referral_args );
		$normal_referral    = affwp_get_referral( $normal_referral_id );

		// Normal referral should always be pending with no flag.
		$normal_pass = $normal_referral && $normal_referral->status === 'pending' && empty( $normal_referral->flag );

		if ( ! $normal_pass ) {
			$all_passed = false;
		}

		$results[] = [
			'#'           => '3a',
			'Scenario'    => 'Normal referral order',
			'Status'      => $normal_referral ? $normal_referral->status : 'N/A',
			'Exp Status'  => 'pending',
			'Flag'        => $normal_referral && $normal_referral->flag ? $normal_referral->flag : '(none)',
			'Exp Flag'    => '(none)',
			'Result'      => $normal_pass ? WP_CLI::colorize( '%G✓ PASS%n' ) : WP_CLI::colorize( '%R✗ FAIL%n' ),
		];

		// =====================================================================
		// PART 4: Test affwp_is_customer_email_affiliate_email filter
		// =====================================================================
		WP_CLI::log( '' );
		WP_CLI::log( WP_CLI::colorize( '%YPart 4: Integration Filter Test%n' ) );

		// Simulate what integrations do - call the filter with true (email matches).
		remove_all_filters( 'affwp_fraud_prevention_flag_self_referral' );

		$filter_result = apply_filters( 'affwp_is_customer_email_affiliate_email', true, $email, $affiliate_id );

		// Expected: if 'reject', filter should return true (block). If 'flag' or 'allow', return false (allow).
		$expected_filter = ( 'reject' === $setting );

		$filter_pass = $filter_result === $expected_filter;

		if ( ! $filter_pass ) {
			$all_passed = false;
		}

		$results[] = [
			'#'           => '4a',
			'Scenario'    => 'Integration filter (match)',
			'Status'      => $filter_result ? 'blocked' : 'allowed',
			'Exp Status'  => $expected_filter ? 'blocked' : 'allowed',
			'Flag'        => has_filter( 'affwp_fraud_prevention_flag_self_referral' ) ? 'Yes' : 'No',
			'Exp Flag'    => in_array( $setting, [ 'flag', 'reject' ], true ) ? 'Yes' : 'No',
			'Result'      => $filter_pass ? WP_CLI::colorize( '%G✓ PASS%n' ) : WP_CLI::colorize( '%R✗ FAIL%n' ),
		];

		// Restore settings.
		affiliate_wp()->settings->set( [ 'fraud_prevention_ip_velocity' => $original_ip_velocity ], true );

		WP_CLI::log( '' );
		WP_CLI\Utils\format_items( 'table', $results, array_keys( $results[0] ) );

		if ( $all_passed ) {
			WP_CLI::success( sprintf( 'Self-Referral [%s]: All %d tests passed!', strtoupper( $setting ), count( $results ) ) );
		} else {
			WP_CLI::warning( sprintf( 'Self-Referral [%s]: Some tests failed.', strtoupper( $setting ) ) );
		}
	}

	/**
	 * Test Referring Sites Detection (PRO feature).
	 *
	 * ## OPTIONS
	 *
	 * [--mode=<mode>]
	 * : Detection mode to test: allow, flag, or reject.
	 *
	 * ## EXAMPLES
	 *
	 *     wp affwp fraud-test referring_sites
	 *     wp affwp fraud-test referring_sites --mode=flag
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Associative arguments.
	 */
	public function referring_sites( $args, $assoc_args ) {
		// Check Pro access.
		$has_pro = function_exists( 'affwp_can_access_pro_features' ) && affwp_can_access_pro_features();

		WP_CLI::log( '' );
		WP_CLI::log( WP_CLI::colorize( '%BReferring Sites Detection Test%n' ) );
		WP_CLI::log( str_repeat( '-', 50 ) );

		if ( ! $has_pro ) {
			WP_CLI::warning( 'This is a PRO feature. Tests will show expected behavior but detection is disabled.' );
		}

		// Set mode if specified.
		if ( isset( $assoc_args['mode'] ) ) {
			affiliate_wp()->settings->set( [ 'fraud_prevention_referring_sites' => $assoc_args['mode'] ], true );
		}

		$setting = affiliate_wp()->settings->get( 'fraud_prevention_referring_sites', 'allow' );
		WP_CLI::log( sprintf( 'Mode: %s | Pro Access: %s', strtoupper( $setting ), $has_pro ? 'Yes' : 'No' ) );
		WP_CLI::log( '' );

		// Create test affiliate with a registered website.
		$username = self::TEST_PREFIX . 'refsite_' . $setting . '_' . time();
		$user_id  = wp_insert_user( [
			'user_login' => $username,
			'user_email' => $username . '@test.local',
			'user_pass'  => wp_generate_password(),
			'user_url'   => 'https://myaffiliate-site.com', // Registered website
		] );
		$affiliate_id = affwp_add_affiliate( [ 'user_id' => $user_id, 'status' => 'active' ] );

		WP_CLI::log( sprintf( 'Test affiliate: #%d (registered site: myaffiliate-site.com)', $affiliate_id ) );
		WP_CLI::log( '' );

		$check   = new \AffiliateWP\Fraud_Prevention\Checks\Referring_Site_Check();
		$results = [];

		$scenarios = [
			[ 'name' => 'Matching referrer',     'referrer' => 'https://myaffiliate-site.com/page' ],
			[ 'name' => 'Matching subdomain',    'referrer' => 'https://blog.myaffiliate-site.com/post' ],
			[ 'name' => 'Non-matching referrer', 'referrer' => 'https://spam-site.com/link' ],
			[ 'name' => 'Empty referrer',        'referrer' => '' ],
		];

		foreach ( $scenarios as $scenario ) {
			$result = $check->check( $affiliate_id, $scenario['referrer'] );

			// Expected: matching sites should pass, non-matching should violate (if not 'allow' mode).
			$is_matching    = strpos( $scenario['referrer'], 'myaffiliate-site.com' ) !== false;
			$expect_violate = ! $is_matching && 'allow' !== $setting && $has_pro && ! empty( $scenario['referrer'] );

			// Empty referrer with no registered sites also violates.
			if ( empty( $scenario['referrer'] ) ) {
				$expect_violate = false; // Empty referrer doesn't violate.
			}

			$pass = $result['is_violation'] === $expect_violate || ! $has_pro;

			$results[] = [
				'Scenario'   => $scenario['name'],
				'Referrer'   => $scenario['referrer'] ?: '(empty)',
				'Violation'  => $result['is_violation'] ? 'Yes' : 'No',
				'Action'     => $result['action'],
				'Result'     => $pass ? WP_CLI::colorize( '%G✓%n' ) : WP_CLI::colorize( '%R✗%n' ),
			];
		}

		WP_CLI\Utils\format_items( 'table', $results, array_keys( $results[0] ) );
		WP_CLI::success( 'Referring Sites test complete.' );
	}

	/**
	 * Test Conversion Rate Detection (PRO feature).
	 *
	 * ## EXAMPLES
	 *
	 *     wp affwp fraud-test conversion_rate
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Associative arguments.
	 */
	public function conversion_rate( $args, $assoc_args ) {
		// Check Pro access.
		$has_pro = function_exists( 'affwp_can_access_pro_features' ) && affwp_can_access_pro_features();

		WP_CLI::log( '' );
		WP_CLI::log( WP_CLI::colorize( '%BConversion Rate Detection Test%n' ) );
		WP_CLI::log( str_repeat( '-', 50 ) );

		if ( ! $has_pro ) {
			WP_CLI::warning( 'This is a PRO feature. Tests will show expected behavior but detection is disabled.' );
		}

		$setting = affiliate_wp()->settings->get( 'fraud_prevention_conversion_rate', 'allow' );
		$min     = affiliate_wp()->settings->get( 'fraud_prevention_min_conversion_rate', 2 );
		$max     = affiliate_wp()->settings->get( 'fraud_prevention_max_conversion_rate', 20 );

		WP_CLI::log( sprintf( 'Mode: %s | Min: %s%% | Max: %s%% | Pro Access: %s', strtoupper( $setting ), $min, $max, $has_pro ? 'Yes' : 'No' ) );
		WP_CLI::log( sprintf( 'Note: Requires minimum %d referrals to trigger detection.', \AffiliateWP\Fraud_Prevention\Checks\Conversion_Rate_Check::MIN_REFERRALS ) );
		WP_CLI::log( '' );

		$check = new \AffiliateWP\Fraud_Prevention\Checks\Conversion_Rate_Check();

		// Test with an existing affiliate that has data.
		$affiliates = affiliate_wp()->affiliates->get_affiliates( [ 'number' => 5 ] );

		if ( empty( $affiliates ) ) {
			WP_CLI::warning( 'No affiliates found to test conversion rate.' );
			return;
		}

		$results = [];
		foreach ( $affiliates as $affiliate ) {
			$result = $check->check( $affiliate->affiliate_id );

			$results[] = [
				'Affiliate'  => '#' . $affiliate->affiliate_id,
				'Visits'     => $result['visit_count'],
				'Referrals'  => $result['referral_count'],
				'Conv Rate'  => sprintf( '%.2f%%', $result['conversion_rate'] ),
				'Violation'  => $result['is_violation'] ? 'Yes' : 'No',
				'Message'    => $result['message'] ?: '-',
			];
		}

		WP_CLI\Utils\format_items( 'table', $results, array_keys( $results[0] ) );
		WP_CLI::success( 'Conversion Rate test complete.' );
	}

	/**
	 * List all test data created by fraud tests.
	 *
	 * ## EXAMPLES
	 *
	 *     wp affwp fraud-test list
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Associative arguments.
	 */
	public function list_( $args, $assoc_args ) {
		global $wpdb;

		WP_CLI::log( '' );
		WP_CLI::log( WP_CLI::colorize( '%BFraud Test Data%n' ) );
		WP_CLI::log( str_repeat( '=', 50 ) );

		// Find test users.
		$test_users = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT ID, user_login, user_email FROM {$wpdb->users} WHERE user_login LIKE %s ORDER BY ID DESC",
				self::TEST_PREFIX . '%'
			)
		);

		if ( empty( $test_users ) ) {
			WP_CLI::log( 'No test data found.' );
			return;
		}

		WP_CLI::log( '' );
		WP_CLI::log( WP_CLI::colorize( '%YTest Affiliates:%n' ) );

		$user_results = [];
		foreach ( $test_users as $user ) {
			$affiliate = affwp_get_affiliate_by( 'user_id', $user->ID );
			$user_results[] = [
				'Affiliate ID' => $affiliate ? $affiliate->affiliate_id : '-',
				'User'         => $user->user_login,
				'Status'       => $affiliate ? $affiliate->status : '-',
				'IP Flag'      => $affiliate && affwp_get_affiliate_meta( $affiliate->affiliate_id, 'ip_velocity_flag', true ) ? 'Yes' : 'No',
			];
		}

		WP_CLI\Utils\format_items( 'table', $user_results, array_keys( $user_results[0] ) );

		// Find test visits.
		$affiliate_ids = array_filter( array_map( function( $user ) {
			$affiliate = affwp_get_affiliate_by( 'user_id', $user->ID );
			return $affiliate ? $affiliate->affiliate_id : null;
		}, $test_users ) );

		if ( ! empty( $affiliate_ids ) ) {
			$visits = affiliate_wp()->visits->get_visits( [
				'affiliate_id' => $affiliate_ids,
				'number'       => 50,
			] );

			if ( ! empty( $visits ) ) {
				WP_CLI::log( '' );
				WP_CLI::log( WP_CLI::colorize( '%YTest Visits:%n' ) );

				$visit_results = [];
				foreach ( $visits as $visit ) {
					$visit_results[] = [
						'Visit ID'     => $visit->visit_id,
						'Affiliate ID' => $visit->affiliate_id,
						'Flag'         => $visit->flag ?: '(none)',
						'URL'          => substr( $visit->url, 0, 50 ) . ( strlen( $visit->url ) > 50 ? '...' : '' ),
					];
				}

				WP_CLI\Utils\format_items( 'table', $visit_results, array_keys( $visit_results[0] ) );
			}
		}

		WP_CLI::log( '' );
		WP_CLI::log( sprintf( 'Total: %d test users', count( $test_users ) ) );
		WP_CLI::log( '' );
	}

	/**
	 * Remove all test data created by fraud tests.
	 *
	 * ## OPTIONS
	 *
	 * [--yes]
	 * : Skip confirmation prompt.
	 *
	 * ## EXAMPLES
	 *
	 *     wp affwp fraud-test cleanup
	 *     wp affwp fraud-test cleanup --yes
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Associative arguments.
	 */
	public function cleanup( $args, $assoc_args ) {
		global $wpdb;

		// Find test users (both _fraud_test_ and _fraud_demo_ prefixes).
		$test_users = $wpdb->get_results(
			"SELECT ID, user_login FROM {$wpdb->users}
			 WHERE user_login LIKE '_fraud_test_%'
			    OR user_login LIKE '_fraud_demo_%'"
		);

		if ( empty( $test_users ) ) {
			WP_CLI::log( 'No test data to clean up.' );
			return;
		}

		WP_CLI::log( sprintf( 'Found %d test users to delete.', count( $test_users ) ) );

		if ( ! isset( $assoc_args['yes'] ) ) {
			WP_CLI::confirm( 'Delete all test data?' );
		}

		$deleted = 0;
		foreach ( $test_users as $user ) {
			$affiliate = affwp_get_affiliate_by( 'user_id', $user->ID );

			if ( $affiliate ) {
				// Delete visits.
				$visits = affiliate_wp()->visits->get_visits( [
					'affiliate_id' => $affiliate->affiliate_id,
					'number'       => -1,
				] );
				foreach ( $visits as $visit ) {
					affiliate_wp()->visits->delete( $visit->visit_id );
				}

				// Delete referrals.
				$referrals = affiliate_wp()->referrals->get_referrals( [
					'affiliate_id' => $affiliate->affiliate_id,
					'number'       => -1,
				] );
				foreach ( $referrals as $ref ) {
					affwp_delete_referral( $ref->referral_id );
				}

				// Delete affiliate.
				affwp_delete_affiliate( $affiliate, true );
			}

			wp_delete_user( $user->ID );
			$deleted++;
		}

		WP_CLI::success( sprintf( 'Deleted %d test users and associated data.', $deleted ) );
	}
}

// Register the command.
if ( defined( 'WP_CLI' ) && WP_CLI ) {
	WP_CLI::add_command( 'affwp fraud-test', 'AffWP_Fraud_Test_CLI' );
}
