<?php
/**
 * Plugin Name: WPSmooth Fatal Error Handler
 * Description: Defensive MU-plugin to catch WSOD, log errors to transients, and expose health via REST API authenticated by MainWP Child ID.
 * Version: 1.0.0
 * Author: WPSmooth LLC
 */

namespace WPSmooth\Health;

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

class Fatal_Error_Handler {

	const LOG_TRANSIENT = 'wpsmooth_health_logs';
	const MAX_LOGS      = 50;

	public static function init() {
		// 1. Hook early to catch fatals at shutdown
		register_shutdown_function( [ __CLASS__, 'catch_fatal_errors' ] );
		
		// 2. Intercept Tier 2/3 warnings for logging
		set_error_handler( [ __CLASS__, 'log_warnings' ] );
		
		// 3. Register authenticated health endpoint
		add_action( 'rest_api_init', [ __CLASS__, 'register_rest_route' ] );
	}

	public static function catch_fatal_errors() {
		$error  = error_get_last();
		$fatals = [ E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR ];

		if ( $error && in_array( $error['type'], $fatals, true ) ) {
			self::record_log( $error, 'fatal' );

			// Purge the output buffer to clear partial DOM or exposed stack traces
			while ( ob_get_level() ) {
				ob_end_clean();
			}

			// Intercept HTTP 500s during API requests to return graceful JSON
			if ( wp_is_json_request() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
				wp_send_json_error( [
					'code'    => 'internal_server_error',
					'message' => 'A critical error occurred. Logged by WPSmooth Health.',
				], 500 );
			} else {
				// Frontend graceful degradation
				wp_die( 'A critical error has occurred. The infrastructure team has been notified.', 'Site Maintenance', [ 'response' => 500 ] );
			}
		}
	}

	public static function log_warnings( $errno, $errstr, $errfile, $errline ) {
		$warnings = [ E_WARNING, E_NOTICE, E_USER_WARNING, E_USER_NOTICE ];
		
		if ( in_array( $errno, $warnings, true ) ) {
			self::record_log( [
				'type'    => $errno,
				'message' => $errstr,
				'file'    => $errfile,
				'line'    => $errline,
			], 'warning' );
		}
		
		// Return false to allow the default PHP error handler to continue execution
		return false; 
	}

	private static function record_log( $error_data, $severity ) {
		$logs = get_transient( self::LOG_TRANSIENT ) ?: [];
		
		array_unshift( $logs, [
			'timestamp' => time(),
			'severity'  => $severity,
			'data'      => $error_data,
		] );

		if ( count( $logs ) > self::MAX_LOGS ) {
			$logs = array_slice( $logs, 0, self::MAX_LOGS );
		}

		set_transient( self::LOG_TRANSIENT, $logs, 7 * DAY_IN_SECONDS );
	}

	public static function register_rest_route() {
		register_rest_route( 'wpsmooth/v1', '/health-logs', [
			'methods'             => \WP_REST_Server::READABLE,
			'callback'            => [ __CLASS__, 'get_health_logs' ],
			'permission_callback' => [ __CLASS__, 'validate_mainwp_auth' ],
		] );
	}

	public static function get_health_logs( \WP_REST_Request $request ) {
		$logs = get_transient( self::LOG_TRANSIENT ) ?: [];
		return rest_ensure_response( [ 'success' => true, 'data' => $logs ] );
	}

	public static function validate_mainwp_auth( \WP_REST_Request $request ) {
		$client_header = $request->get_header( 'X-WPSmooth-Auth' );
		
		if ( empty( $client_header ) ) {
			return new \WP_Error( 'rest_forbidden', 'Missing authentication header.', [ 'status' => 401 ] );
		}

		// Pull the unique security ID directly from the MainWP Child plugin configuration
		$mainwp_id = get_option( 'mainwp_child_uniqueId' );
		
		if ( empty( $mainwp_id ) ) {
			return new \WP_Error( 'rest_forbidden', 'MainWP Child not configured on this node.', [ 'status' => 403 ] );
		}

		// Constant-time comparison to mitigate timing attacks on the API endpoint
		if ( ! hash_equals( $mainwp_id, $client_header ) ) {
			return new \WP_Error( 'rest_forbidden', 'Invalid security token.', [ 'status' => 403 ] );
		}

		return true;
	}
}

Fatal_Error_Handler::init();
