<?php
/**
 * Cache analyzer.
 *
 * Checks caching configuration including object cache, page cache, and browser cache.
 *
 * @package MonitorCat\Modules\Speed\Analyzer
 */

declare(strict_types=1);

namespace MonitorCat\Modules\Speed\Analyzer;

defined( 'ABSPATH' ) || exit;

/**
 * Analyzes caching configuration for optimal performance.
 */
final class CacheAnalyzer {

	/**
	 * Known page caching plugins and their main file paths.
	 *
	 * @var array<string, string>
	 */
	private const CACHING_PLUGINS = array(
		'W3 Total Cache'   => 'w3-total-cache/w3-total-cache.php',
		'WP Super Cache'   => 'wp-super-cache/wp-cache.php',
		'WP Rocket'        => 'wp-rocket/wp-rocket.php',
		'LiteSpeed Cache'  => 'litespeed-cache/litespeed-cache.php',
		'WP Fastest Cache' => 'wp-fastest-cache/wpFastestCache.php',
		'Cache Enabler'    => 'cache-enabler/cache-enabler.php',
		'Comet Cache'      => 'comet-cache/comet-cache.php',
		'Breeze'           => 'breeze/breeze.php',
	);

	/**
	 * Pre-fetched HEAD response to avoid duplicate requests.
	 *
	 * @var array<string, mixed>|\WP_Error|null
	 */
	private $prefetched_head_response = null;

	/**
	 * Set a pre-fetched HEAD response to avoid duplicate home page requests.
	 *
	 * @param array<string, mixed>|\WP_Error $response The wp_remote_head response.
	 */
	public function set_prefetched_head_response( $response ): void {
		$this->prefetched_head_response = $response;
	}

	/**
	 * Run the cache analysis.
	 *
	 * @return array{score: int, status: string, label: string, checks: array<int, array{label: string, status: string, message: string, value: string|null}>}
	 */
	public function analyze(): array {
		$checks = array();

		$checks[] = $this->check_object_cache();
		$checks[] = $this->check_page_cache();
		$checks[] = $this->check_browser_cache();

		$score  = $this->calculate_score( $checks );
		$status = $this->determine_status( $score );

		return array(
			'score'  => $score,
			'status' => $status,
			'label'  => __( 'Caching', 'monitor-cat' ),
			'checks' => $checks,
		);
	}

	/**
	 * Check if an external object cache is in use.
	 *
	 * @return array{label: string, status: string, message: string, value: string|null}
	 */
	private function check_object_cache(): array {
		$using_ext_cache = wp_using_ext_object_cache();

		if ( $using_ext_cache ) {
			$cache_type = $this->detect_object_cache_type();

			return array(
				'label'   => __( 'Object Cache', 'monitor-cat' ),
				'status'  => 'good',
				/* translators: %s: cache backend type */
				'message' => sprintf( __( 'External object cache is active (%s). Database queries are reduced.', 'monitor-cat' ), $cache_type ),
				'value'   => $cache_type,
			);
		}

		return array(
			'label'   => __( 'Object Cache', 'monitor-cat' ),
			'status'  => 'critical',
			'message' => __( 'No external object cache detected. Install Redis or Memcached for better performance.', 'monitor-cat' ),
			'value'   => __( 'Not active', 'monitor-cat' ),
		);
	}

	/**
	 * Detect the type of object cache backend in use.
	 *
	 * @return string The detected cache type name.
	 */
	private function detect_object_cache_type(): string {
		if ( class_exists( 'Redis' ) || class_exists( 'Predis\Client' ) ) {
			return 'Redis';
		}

		if ( class_exists( 'Memcached' ) ) {
			return 'Memcached';
		}

		if ( class_exists( 'Memcache' ) ) {
			return 'Memcache';
		}

		return __( 'Unknown backend', 'monitor-cat' );
	}

	/**
	 * Check if a page caching plugin is active.
	 *
	 * @return array{label: string, status: string, message: string, value: string|null}
	 */
	private function check_page_cache(): array {
		if ( ! function_exists( 'is_plugin_active' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}

		$active_plugins = array();

		foreach ( self::CACHING_PLUGINS as $name => $path ) {
			if ( is_plugin_active( $path ) ) {
				$active_plugins[] = $name;
			}
		}

		if ( ! empty( $active_plugins ) ) {
			return array(
				'label'   => __( 'Page Cache', 'monitor-cat' ),
				'status'  => 'good',
				/* translators: %s: comma-separated list of active caching plugins */
				'message' => sprintf( __( 'Page caching is active via: %s.', 'monitor-cat' ), implode( ', ', $active_plugins ) ),
				'value'   => implode( ', ', $active_plugins ),
			);
		}

		return array(
			'label'   => __( 'Page Cache', 'monitor-cat' ),
			'status'  => 'critical',
			'message' => __( 'No page caching plugin detected. Install a caching plugin for significantly faster load times.', 'monitor-cat' ),
			'value'   => null,
		);
	}

	/**
	 * Check browser caching headers on the home page.
	 *
	 * @return array{label: string, status: string, message: string, value: string|null}
	 */
	private function check_browser_cache(): array {
		if ( null !== $this->prefetched_head_response ) {
			$response = $this->prefetched_head_response;
		} else {
			$response = wp_remote_head(
				home_url( '/' ),
				array(
					'timeout'   => 5,
					'sslverify' => false,
				)
			);
		}

		if ( is_wp_error( $response ) ) {
			return array(
				'label'   => __( 'Browser Cache', 'monitor-cat' ),
				'status'  => 'warning',
				'message' => __( 'Could not check browser caching headers.', 'monitor-cat' ),
				'value'   => null,
			);
		}

		$headers       = wp_remote_retrieve_headers( $response );
		$cache_control = $headers['cache-control'] ?? '';
		$expires       = $headers['expires'] ?? '';
		$etag          = $headers['etag'] ?? '';

		$has_cache_headers = ! empty( $cache_control ) || ! empty( $expires ) || ! empty( $etag );

		if ( $has_cache_headers ) {
			$details = array();
			if ( ! empty( $cache_control ) ) {
				$details[] = 'Cache-Control';
			}
			if ( ! empty( $expires ) ) {
				$details[] = 'Expires';
			}
			if ( ! empty( $etag ) ) {
				$details[] = 'ETag';
			}

			return array(
				'label'   => __( 'Browser Cache', 'monitor-cat' ),
				'status'  => 'good',
				/* translators: %s: comma-separated list of cache header types */
				'message' => sprintf( __( 'Browser caching headers detected: %s.', 'monitor-cat' ), implode( ', ', $details ) ),
				'value'   => implode( ', ', $details ),
			);
		}

		return array(
			'label'   => __( 'Browser Cache', 'monitor-cat' ),
			'status'  => 'warning',
			'message' => __( 'No browser caching headers found. Configure Cache-Control or Expires headers for static assets.', 'monitor-cat' ),
			'value'   => null,
		);
	}

	/**
	 * Calculate the overall score from checks.
	 *
	 * Weighted: Object cache 40%, Page cache 40%, Browser cache 20%.
	 *
	 * @param array<int, array{label: string, status: string, message: string, value: string|null}> $checks The checks array.
	 * @return int Score from 0 to 100.
	 */
	private function calculate_score( array $checks ): int {
		if ( empty( $checks ) ) {
			return 0;
		}

		$total_weight = 0;
		$earned       = 0;
		$weights      = array( 40, 40, 20 );

		foreach ( $checks as $index => $check ) {
			$weight        = $weights[ $index ] ?? 20;
			$total_weight += $weight;

			if ( 'good' === $check['status'] ) {
				$earned += $weight;
			} elseif ( 'warning' === $check['status'] ) {
				$earned += (int) round( $weight * 0.5 );
			}
		}

		return $total_weight > 0 ? (int) round( ( $earned / $total_weight ) * 100 ) : 0;
	}

	/**
	 * Determine overall status from score.
	 *
	 * @param int $score The calculated score.
	 * @return string 'good', 'warning', or 'critical'.
	 */
	private function determine_status( int $score ): string {
		if ( $score >= 80 ) {
			return 'good';
		}

		if ( $score >= 50 ) {
			return 'warning';
		}

		return 'critical';
	}
}
