<?php
/**
 * Silent error logging for DataStream.
 *
 * @package DataStream
 */

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

/**
 * Class DataStream_Logger
 */
class DataStream_Logger {

	/**
	 * Log prefix.
	 */
	private const PREFIX = '[DataStream]';

	/**
	 * Level priority map.
	 *
	 * @var array<string, int>
	 */
	private static $levels = array(
		'debug' => 10,
		'info'  => 20,
		'error' => 30,
	);

	/**
	 * Whether logging is enabled.
	 *
	 * @return bool
	 */
	public static function is_enabled() {
		return (bool) DataStream_Config::get( 'enable_logging' );
	}

	/**
	 * Whether a level should be written.
	 *
	 * @param string $level Log level.
	 * @return bool
	 */
	private static function should_log( $level ) {
		if ( ! self::is_enabled() ) {
			return false;
		}

		$configured = sanitize_key( (string) DataStream_Config::get( 'log_level' ) );
		$configured = isset( self::$levels[ $configured ] ) ? $configured : 'error';

		return self::$levels[ $level ] >= self::$levels[ $configured ];
	}

	/**
	 * Write a sanitized message to the PHP error log.
	 *
	 * @param string               $message Human-readable message.
	 * @param array<string, mixed> $context Optional structured context.
	 * @param string               $level   Log level.
	 */
	public static function log( $message, $context = array(), $level = 'error' ) {
		if ( ! self::should_log( $level ) ) {
			return;
		}

		$safe_message = sanitize_text_field( $message );
		$entry        = self::PREFIX . ' [' . strtoupper( $level ) . '] ' . $safe_message;

		if ( ! empty( $context ) ) {
			$safe_context = self::sanitize_context( $context );
			$encoded      = wp_json_encode( $safe_context );

			if ( false !== $encoded ) {
				$entry .= ' ' . $encoded;
			}
		}

		error_log( $entry ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
	}

	/**
	 * Log an error-level event.
	 *
	 * @param string               $message Error message.
	 * @param array<string, mixed> $context Optional context.
	 */
	public static function error( $message, $context = array() ) {
		self::log( $message, $context, 'error' );
	}

	/**
	 * Log an info-level event.
	 *
	 * @param string               $message Info message.
	 * @param array<string, mixed> $context Optional context.
	 */
	public static function info( $message, $context = array() ) {
		self::log( $message, $context, 'info' );
	}

	/**
	 * Log a debug-level event.
	 *
	 * @param string               $message Debug message.
	 * @param array<string, mixed> $context Optional context.
	 */
	public static function debug( $message, $context = array() ) {
		self::log( $message, $context, 'debug' );
	}

	/**
	 * Strip sensitive values from log context.
	 *
	 * @param array<string, mixed> $context Raw context.
	 * @return array<string, mixed>
	 */
	private static function sanitize_context( $context ) {
		$safe     = array();
		$denylist = array( 'password', 'secret', 'token', 'api_key', 'authorization', 'bearer' );

		foreach ( $context as $key => $value ) {
			$safe_key = sanitize_key( (string) $key );

			if ( in_array( $safe_key, $denylist, true ) ) {
				$safe[ $safe_key ] = '[redacted]';
				continue;
			}

			if ( is_scalar( $value ) || null === $value ) {
				$safe[ $safe_key ] = is_string( $value ) ? sanitize_text_field( $value ) : $value;
			} elseif ( is_array( $value ) ) {
				$safe[ $safe_key ] = self::sanitize_context( $value );
			}
		}

		return $safe;
	}
}
