<?php
/**
 * Conservative metadata denylist.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Discovery;

/**
 * Rejects generated, credential, cursor, session, and scheduler metadata.
 */
final class MetadataDenylist {
	/**
	 * @var array<string,string>
	 */
	private const EXACT = array(
		'_edit_lock'            => 'WordPress edit lock.',
		'_edit_last'            => 'WordPress edit marker.',
		'_wp_old_slug'          => 'WordPress revision marker.',
		'_wp_desired_post_slug' => 'WordPress revision marker.',
	);

	/**
	 * @var array<string,string>
	 */
	private const PATTERNS = array(
		'/^_scheduled_changes_/'               => 'Internal Scheduled Changes metadata.',
		'/(?:^|_)(?:session|nonce)(?:_|$)/i'   => 'Session or nonce value.',
		'/(?:license|licence|credential|password|passwd|secret|private_key|api_key|access_token|refresh_token)/i' => 'Credential or license material.',
		'/(?:^|_)(?:auth|bearer|webhook|jwt|signing)?_?token(?:_|$)|authorization|signing_key|webhook_signature|(?:^|_)jwt(?:_|$)/i' => 'Authorization token or signing material.',
		'/(?:encrypt|cipher|initialization_vector|encryption_key|key_material)/i' => 'Encryption material.',
		'/(?:sync|import|export)_(?:cursor|offset|checkpoint|position)/i' => 'Synchronization cursor.',
		'/(?:^|_)(?:cache|transient)(?:_|$)/i' => 'Generated cache value.',
		'/(?:analytics|impression|view|click)_(?:count|total)/i' => 'Generated analytics counter.',
		'/(?:lookup|index)_(?:value|hash|version|cursor)/i' => 'Generated lookup value.',
	);

	public function isDenied( string $key ): bool {
		return '' !== $this->reason( $key );
	}

	public function isSensitive( string $key ): bool {
		return 1 === preg_match(
			'/(?:license|licence|credential|password|passwd|secret|private_key|api_key|access_token|refresh_token|(?:^|_)(?:auth|bearer|webhook|jwt|signing)?_?token(?:_|$)|authorization|signing_key|webhook_signature|(?:^|_)jwt(?:_|$)|encrypt|cipher|key_material)/i',
			$key
		);
	}

	public function reason( string $key ): string {
		if ( isset( self::EXACT[ $key ] ) ) {
			return self::EXACT[ $key ];
		}

		foreach ( self::PATTERNS as $pattern => $reason ) {
			if ( 1 === preg_match( $pattern, $key ) ) {
				return $reason;
			}
		}

		return '';
	}

	public function version(): string {
		return '1';
	}
}
