<?php
/**
 * Centralised, redacted logging helpers for Contentosapp Studio.
 *
 * @package Contentosapp_Studio
 */

defined( 'ABSPATH' ) || exit;
// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional debug logging; gated by costudio_debug_enabled() which checks WP_DEBUG and a plugin-specific option.

if ( ! function_exists( 'costudio_debug_enabled' ) ) {
	/**
	 * Whether verbose Contentosapp Studio diagnostics may be written to PHP error_log.
	 */
	function costudio_debug_enabled(): bool {
		$enabled = false;
		if ( function_exists( 'get_option' ) ) {
			$enabled = (bool) get_option( 'costudio_debug_enabled', false );
		}

		return (bool) apply_filters( 'costudio_debug_enabled', $enabled );
	}
}

if ( ! function_exists( 'costudio_redact_log_value' ) ) {
	/**
	 * Convert arbitrary log context to a redacted, bounded string.
	 *
	 * @param mixed $value Context value.
	 */
	function costudio_redact_log_value( $value, int $max_length = 600 ): string {
		if ( is_array( $value ) || is_object( $value ) ) {
			$value = function_exists( 'wp_json_encode' )
				? wp_json_encode( $value )
				: json_encode( $value );
		}

		$text = (string) $value;
		$text = preg_replace( '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $text );

		$patterns = [
			'/(\bAuthorization\s*:\s*Bearer\s+)[^\s,"\'}]+/i'                         => '$1[redacted]',
			'/(\bBearer\s+)(FOS2\.[A-Za-z0-9_\-.]+)/i'                                  => '$1FOS2.[redacted]',
			'/\bFOS2\.[A-Za-z0-9_\-.]+/i'                                                => 'FOS2.[redacted]',
			'/(\bX-FOS-Signature\b["\']?\s*[:=]\s*["\']?)[^,"\'}\s&]+/i'                => '$1[redacted]',
			'/(\bX-FOS-Poll-Token\b["\']?\s*[:=]\s*["\']?)[^,"\'}\s&]+/i'               => '$1[redacted]',
			'/(\bpoll_token\b["\']?\s*[:=]\s*["\']?)[^,"\'}\s&]+/i'                     => '$1[redacted]',
			'/((?:license_key|access_key|api_key|secret|password|token|signature)["\']?\s*[:=]\s*["\']?)[^,"\'}&\s]+/i' => '$1[redacted]',
			'/(sk|rk)_(test|live)_[A-Za-z0-9_\-]+/i'                                     => '$1_$2_[redacted]',
			'/whsec_[A-Za-z0-9_\-]+/i'                                                    => 'whsec_[redacted]',
			'/AIza[0-9A-Za-z_\-]{20,}/'                                                   => 'AIza[redacted]',
		];

		foreach ( $patterns as $pattern => $replacement ) {
			$text = preg_replace( $pattern, $replacement, $text );
		}

		if ( function_exists( 'mb_strlen' ) && function_exists( 'mb_substr' ) ) {
			if ( mb_strlen( $text ) > $max_length ) {
				return mb_substr( $text, 0, $max_length ) . '...';
			}
			return $text;
		}

		return strlen( $text ) > $max_length ? substr( $text, 0, $max_length ) . '...' : $text;
	}
}

if ( ! function_exists( 'costudio_log' ) ) {
	/**
	 * Write a redacted Contentosapp Studio log line.
	 *
	 * @param mixed $context Optional context.
	 */
	function costudio_log( string $message, $context = null, string $level = 'info', bool $debug_only = false ): void {
		$level = strtolower( sanitize_key( $level ) );
		$operational = in_array( $level, array( 'warning', 'warn', 'error', 'critical' ), true );

		// Verbose milestones remain opt-in. Operational warnings and errors must
		// always reach the PHP log so a failed background publication can be
		// diagnosed on a normal production install where debug mode is disabled.
		if ( ! costudio_debug_enabled() && ( $debug_only || ! $operational ) ) {
			return;
		}

		$line = 'Contentosapp Studio [' . $level . '] ' . costudio_redact_log_value( $message, 240 );
		if ( null !== $context ) {
			$line .= ' ' . costudio_redact_log_value( $context );
		}
		error_log( $line );
	}
}

if ( ! function_exists( 'costudio_log_debug' ) ) {
	/**
	 * Write a debug-only redacted log line.
	 *
	 * @param mixed $context Optional context.
	 */
	function costudio_log_debug( string $message, $context = null ): void {
		costudio_log( $message, $context, 'debug', true );
	}
}
