<?php
/**
 * Read-only WordPress object-cache collector.
 *
 * @package MarketingConsole
 */

namespace MarketingConsole\Modules\Speed\Collectors;

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

/**
 * Reports external/drop-in/backend state without ratios or estimates.
 */
final class ObjectCacheCollector implements CollectorInterface {

	/** @var array<string,mixed> */
	private array $probes;

	/**
	 * @param array<string,mixed> $probes Deterministic read-only test probes.
	 */
	public function __construct( array $probes = array() ) {
		$this->probes = $probes;
	}

	/**
	 * @return string
	 */
	public function key(): string {
		return 'object_cache';
	}

	/**
	 * @return array<string,array<string,mixed>>
	 */
	public function collect(): array {
		$now      = gmdate( 'Y-m-d\TH:i:s\Z' );
		$external = $this->external();
		$dropin   = $this->dropin_present();

		if ( ! is_bool( $external ) || ! is_bool( $dropin ) ) {
			return array(
				'object_cache' => array(
					'value'       => null,
					'status'      => 'unsupported',
					'source'      => 'wordpress_runtime',
					'observed_at' => $now,
				),
			);
		}

		return array(
			'object_cache' => array(
				'value'       => array(
					'external'       => $external,
					'dropin_present' => $dropin,
					'backend'        => $this->backend(),
				),
				'status'      => 'ok',
				'source'      => 'wordpress_runtime',
				'observed_at' => $now,
			),
		);
	}

	/**
	 * @return mixed
	 */
	private function external() {
		if ( array_key_exists( 'external', $this->probes ) ) {
			return $this->probes['external'];
		}

		if ( ! function_exists( 'wp_using_ext_object_cache' ) ) {
			return null;
		}

		/*
		 * wp_using_ext_object_cache() returns null until the object cache
		 * subsystem is initialized (cron, early admin_init), which would
		 * collapse the whole field to "unsupported". Persistent-cache presence
		 * is determined reliably by the drop-in file; an indeterminate flag
		 * means no active external cache in this context, so report false.
		 */
		$external = wp_using_ext_object_cache();

		return is_bool( $external ) ? $external : false;
	}

	/**
	 * @return mixed
	 */
	private function dropin_present() {
		if ( array_key_exists( 'dropin_present', $this->probes ) ) {
			return $this->probes['dropin_present'];
		}

		return defined( 'WP_CONTENT_DIR' ) ? is_file( constant( 'WP_CONTENT_DIR' ) . '/object-cache.php' ) : null;
	}

	/**
	 * Unknown is an allowed backend value, never a status.
	 *
	 * @return string
	 */
	private function backend(): string {
		if ( isset( $this->probes['backend'] ) && in_array( $this->probes['backend'], array( 'redis', 'memcached', 'unknown' ), true ) ) {
			return $this->probes['backend'];
		}

		global $wp_object_cache;

		$class = is_object( $wp_object_cache ) ? strtolower( get_class( $wp_object_cache ) ) : '';

		if ( false !== strpos( $class, 'redis' ) || defined( 'WP_REDIS_CONFIG' ) || defined( 'WP_REDIS_HOST' ) ) {
			return 'redis';
		}

		if ( false !== strpos( $class, 'memcached' ) || false !== strpos( $class, 'memcache' ) ) {
			return 'memcached';
		}

		return 'unknown';
	}
}
