<?php
/**
 * User Meta type condition Rules
 *
 * @link       https://personalizewp.com/
 * @since      3.0.0
 *
 * @package    PersonalizeWP
 * @subpackage PersonalizeWP/Rule_Conditions
 */

namespace PersonalizeWP\Rule_Conditions;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.

/**
 * Checks for the existence and values of WordPress user meta fields
 */
class User_Meta extends RuleCondition {

	/**
	 * Condition identifier
	 *
	 * @var string
	 */
	public string $identifier = 'core_user_meta';

	/**
	 * Measure used to adjust the admin form for meta key input
	 *
	 * @var string
	 */
	public string $measure_key = 'meta_key';

	/**
	 * Constructor
	 */
	public function __construct() {
		parent::__construct();

		$this->category = __( 'Core', 'personalizewp' );

		$this->description = __( 'User Meta', 'personalizewp' );

		$this->comparators = array(
			'any_value'        => _x( 'Has any Value (Exists)', 'Comparator', 'personalizewp' ),
			'no_value'         => _x( 'Has no Value (Does not exist)', 'Comparator', 'personalizewp' ),
			'equals'           => _x( 'Is Equal To', 'Comparator', 'personalizewp' ),
			'does_not_equal'   => _x( 'Does Not Equal', 'Comparator', 'personalizewp' ),
			'contains'         => _x( 'Contains', 'Comparator', 'personalizewp' ),
			'does_not_contain' => _x( 'Does Not Contain', 'Comparator', 'personalizewp' ),
		);

		$this->comparison_values = array();

		$this->comparison_type = 'text';
	}

	/**
	 * Test data against condition
	 *
	 * @param string $comparator Comparator test to run.
	 * @param string $value      Comparison to check (meta value).
	 * @param string $action     Action to take.
	 * @param mixed  $meta       Additional meta data, meta key/name.
	 *
	 * @return bool
	 */
	public function matchesCriteria( $comparator, $value, $action, $meta = array() ): bool {
		// User must be logged in for meta checks.
		if ( ! is_user_logged_in() || empty( $meta ) ) {
			switch ( $comparator ) {
				case 'no_value':
				case 'does_not_equal':
				case 'does_not_contain':
					return true;
				default:
					return false;
			}
		}

		switch ( $comparator ) {
			case 'any_value':
				return $this->comparatorAnyValue( $value, $meta->meta_key );
			case 'no_value':
				return $this->comparatorNoValue( $value, $meta->meta_key );
			case 'equals':
				return $this->comparatorEquals( $value, $meta->meta_key );
			case 'does_not_equal':
				return $this->comparatorDoesNotEqual( $value, $meta->meta_key );
			case 'contains':
				return $this->comparatorContains( $value, $meta->meta_key );
			case 'does_not_contain':
				return $this->comparatorDoesNotContain( $value, $meta->meta_key );
		}

		return false;
	}

	/**
	 * "Any value" and exists test
	 * Meta key is sanitized before testing
	 *
	 * @param string $value   Value of meta to test for, unused.
	 * @param string $metakey Name of meta key to test for.
	 *
	 * @return bool
	 */
	private function comparatorAnyValue( $value, $metakey ) {
		$metakey    = sanitize_text_field( $metakey );
		$user_id    = get_current_user_id();
		$meta_value = get_user_meta( $user_id, $metakey, true );

		if ( ! empty( $meta_value ) ) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * "No value" or doesn't exist test
	 * Meta key is sanitized before testing
	 *
	 * @param string $value   Value of meta to test for, unused.
	 * @param string $metakey Name of meta key to test for.
	 *
	 * @return bool
	 */
	private function comparatorNoValue( $value, $metakey ) {
		$metakey    = sanitize_text_field( $metakey );
		$user_id    = get_current_user_id();
		$meta_value = get_user_meta( $user_id, $metakey, true );

		if ( empty( $meta_value ) ) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * "Equal" value test
	 * Meta key/value is sanitized before testing
	 *
	 * @param string $value   Value of meta to test for.
	 * @param string $metakey Name of meta key to test for.
	 *
	 * @return bool
	 */
	private function comparatorEquals( $value, $metakey ) {
		$metakey   = sanitize_text_field( $metakey );
		$user_id   = get_current_user_id();
		$metavalue = get_user_meta( $user_id, $metakey, true );
		$metavalue = is_string( $metavalue ) ? sanitize_text_field( $metavalue ) : (string) $metavalue;

		return $metavalue === $value;
	}

	/**
	 * "Does not equal" test
	 * Meta key/value is sanitized before testing
	 *
	 * @param string $value   Value of meta to test for.
	 * @param string $metakey Name of meta key to test for.
	 *
	 * @return bool
	 */
	private function comparatorDoesNotEqual( $value, $metakey ) {
		$metakey   = sanitize_text_field( $metakey );
		$user_id   = get_current_user_id();
		$metavalue = get_user_meta( $user_id, $metakey, true );
		$metavalue = is_string( $metavalue ) ? sanitize_text_field( $metavalue ) : (string) $metavalue;

		return $metavalue !== $value;
	}

	/**
	 * "Contains" test
	 * Meta key/value is sanitized before testing
	 *
	 * @param string $value   Value of meta to test for.
	 * @param string $metakey Name of meta key to test for.
	 *
	 * @return bool
	 */
	private function comparatorContains( $value, $metakey ) {
		$metakey   = sanitize_text_field( $metakey );
		$user_id   = get_current_user_id();
		$metavalue = get_user_meta( $user_id, $metakey, true );
		$metavalue = is_string( $metavalue ) ? sanitize_text_field( $metavalue ) : (string) $metavalue;

		return str_contains( $metavalue, $value );
	}

	/**
	 * "Does not contain" test
	 * Meta key/value is sanitized before testing
	 *
	 * @param string $value   Value of meta to test for.
	 * @param string $metakey Name of meta key to test for.
	 *
	 * @return bool
	 */
	private function comparatorDoesNotContain( $value, $metakey ) {
		$metakey   = sanitize_text_field( $metakey );
		$user_id   = get_current_user_id();
		$metavalue = get_user_meta( $user_id, $metakey, true );
		$metavalue = is_string( $metavalue ) ? sanitize_text_field( $metavalue ) : (string) $metavalue;

		return ! str_contains( $metavalue, $value );
	}
}
