<?php
/**
 * Consent state constants.
 *
 * @package Carticy\Conversions\Domain
 */

declare(strict_types=1);

namespace Carticy\Conversions\Domain;

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

/**
 * Normalizes the plugin's tri-state consent contract.
 */
final class ConsentState {

	public const GRANTED = 'granted';
	public const DENIED  = 'denied';
	public const UNSET   = 'unset';

	/**
	 * Raw values an external consent source may use to express a grant.
	 *
	 * External CMPs/SaaS scripts/dataLayer pushes do not agree on a vocabulary,
	 * so the intake layer must map their tokens before the gate reads them. Kept
	 * deliberately conservative: only unambiguous affirmatives, so an unknown
	 * value still resolves to UNSET (fail closed), never an accidental grant.
	 *
	 * @var array<int, string>
	 */
	private const GRANT_TOKENS = array( 'granted', 'grant', 'yes', 'true', '1', 'allow', 'allowed', 'on', 'approved', 'accept', 'accepted', 'consented' );

	/**
	 * Raw values an external consent source may use to express a denial.
	 *
	 * @var array<int, string>
	 */
	private const DENY_TOKENS = array( 'denied', 'deny', 'no', 'false', '0', 'disallow', 'disallowed', 'off', 'rejected', 'reject', 'declined', 'decline' );

	/**
	 * Normalize an arbitrary consent value.
	 *
	 * Unknown, empty, and null values intentionally become "unset", not denied.
	 * Accepts the heterogeneous vocabularies real CMPs and SaaS scripts emit
	 * (booleans, 1/0, "allow"/"deny", "accepted"/"rejected", case-insensitive)
	 * so a per-source adapter does not have to pre-translate every dialect - but
	 * anything outside the explicit allow/deny token sets still fails closed.
	 *
	 * @param mixed $state Raw consent state.
	 * @return string
	 */
	public static function normalize( $state ): string {
		if ( true === $state ) {
			return self::GRANTED;
		}

		if ( false === $state ) {
			return self::DENIED;
		}

		if ( is_int( $state ) ) {
			return 1 === $state ? self::GRANTED : ( 0 === $state ? self::DENIED : self::UNSET );
		}

		if ( is_string( $state ) ) {
			$token = strtolower( trim( $state ) );

			if ( in_array( $token, self::GRANT_TOKENS, true ) ) {
				return self::GRANTED;
			}

			if ( in_array( $token, self::DENY_TOKENS, true ) ) {
				return self::DENIED;
			}
		}

		return self::UNSET;
	}

	/**
	 * Check whether a state is an explicit grant.
	 *
	 * @param mixed $state Raw consent state.
	 * @return bool
	 */
	public static function is_granted( $state ): bool {
		return self::GRANTED === self::normalize( $state );
	}

	/**
	 * Get valid consent states.
	 *
	 * @return array<int, string>
	 */
	public static function values(): array {
		return array( self::GRANTED, self::DENIED, self::UNSET );
	}
}
