<?php
/**
 * Audit context redaction.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Audit;

/**
 * Removes common credential and secret values before persistence.
 */
final class AuditRedactor {
	private const REDACTED = '[redacted]';

	/**
	 * Redacts a structured context recursively.
	 *
	 * @param array<string,mixed> $context Event context.
	 * @return array<string,mixed>
	 */
	public function redact( array $context ): array {
		$result = array();

		foreach ( $context as $key => $value ) {
			if ( $this->isSensitiveKey( (string) $key ) ) {
				$result[ $key ] = self::REDACTED;
				continue;
			}

			$result[ $key ] = $this->redactValue( $value );
		}

		return $result;
	}

	/**
	 * Redacts nested list or map values.
	 *
	 * @param array<mixed> $values Nested values.
	 * @return array<mixed>
	 */
	private function redactArray( array $values ): array {
		$result = array();

		foreach ( $values as $key => $value ) {
			if ( is_string( $key ) && $this->isSensitiveKey( $key ) ) {
				$result[ $key ] = self::REDACTED;
				continue;
			}

			$result[ $key ] = $this->redactValue( $value );
		}

		return $result;
	}

	/**
	 * Redacts nested values and scalar strings that carry destinations or local paths.
	 *
	 * @param mixed $value Context value.
	 * @return mixed
	 */
	private function redactValue( $value ) {
		if ( is_array( $value ) ) {
			return $this->redactArray( $value );
		}

		if (
			is_string( $value )
			&& 1 === preg_match( '#(?:https?://|(?:^|\s)/(?:[^\s/]+/)+|[A-Za-z]:\\\\|(?:token|secret|password|authorization)=)#i', $value )
		) {
			return self::REDACTED;
		}

		return $value;
	}

	/**
	 * Checks a context key for known secret vocabulary.
	 *
	 * @param string $key Context key.
	 */
	private function isSensitiveKey( string $key ): bool {
		$normalized = strtolower( str_replace( array( '-', ' ' ), '_', $key ) );

		return 1 === preg_match(
			'/(^|_)(password|passwd|secret|token|api_key|authorization|private_key|client_secret|url|uri|destination|path|file|prompt|tool_result|assistant_response|provider_response|conversation)($|_)/',
			$normalized
		);
	}
}
