<?php
/**
 * Safe throwable diagnostics for protected result and audit contexts.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Support;

/**
 * Converts throwables into bounded, redacted, structured diagnostics.
 */
final class ThrowableDiagnostic {
	private const MAX_MESSAGE_LENGTH = 240;

	/**
	 * @return array{class:class-string,code:string,message:string,previous_class?:class-string}
	 */
	public static function from( \Throwable $throwable ): array {
		$diagnostic = array(
			'class'   => get_class( $throwable ),
			'code'    => (string) $throwable->getCode(),
			'message' => self::redactMessage( $throwable->getMessage() ),
		);
		$previous   = $throwable->getPrevious();

		if ( null !== $previous ) {
			$diagnostic['previous_class'] = get_class( $previous );
		}

		return $diagnostic;
	}

	/**
	 * Redacts likely secrets and local paths while preserving benign context.
	 */
	public static function redactMessage( string $message ): string {
		$message = trim( $message );

		if ( '' === $message ) {
			return 'Internal error.';
		}

		$message = (string) preg_replace( '#https?://\S*[?&](?:token|secret|password|authorization|api[ _-]?key|client_secret|private_key)=\S*#i', '[redacted-url]', $message );
		$message = (string) preg_replace( '#\bauthorization\s*[:=]\s*bearer\s+\S+#i', 'Authorization: Bearer [redacted]', $message );
		$message = (string) preg_replace( '#\bbearer\s+\S+#i', 'Bearer [redacted]', $message );
		$message = (string) preg_replace( '#\bauthorization\s*[:=]\s*(?!bearer\b)\S+#i', 'authorization=[redacted]', $message );
		$message = (string) preg_replace( '#\b(token|secret|password|api[ _-]?key|client_secret|private_key)\s*[:=]\s*\S+#i', '$1=[redacted]', $message );
		$message = (string) preg_replace( '#(^|[\s(\["\'])/(?!/)(?:[^\s/]+/)+[^\s]*#', '$1[redacted-path]', $message );
		$message = (string) preg_replace( '#\b[A-Za-z]:\\\\(?:[^\\\\\s]+\\\\?)+#', '[redacted-path]', $message );
		$message = trim( $message );

		if ( '' === $message ) {
			return 'Internal error.';
		}

		if ( strlen( $message ) > self::MAX_MESSAGE_LENGTH ) {
			return substr( $message, 0, self::MAX_MESSAGE_LENGTH - 3 ) . '...';
		}

		return $message;
	}
}
