<?php
/**
 * Compression analyzer.
 *
 * Detects GZIP and Brotli compression on the site.
 *
 * @package MonitorCat\Modules\Speed\Analyzer
 */

declare(strict_types=1);

namespace MonitorCat\Modules\Speed\Analyzer;

defined( 'ABSPATH' ) || exit;

/**
 * Analyzes HTTP compression (GZIP/Brotli) configuration.
 */
final class CompressionAnalyzer {

	/**
	 * Pre-fetched HTTP response to avoid duplicate requests.
	 *
	 * @var array<string, mixed>|null
	 */
	private ?array $prefetched_response = null;

	/**
	 * Set a pre-fetched HTTP response to avoid duplicate home page requests.
	 *
	 * @param array<string, mixed> $response The wp_remote_get response array.
	 */
	public function set_prefetched_response( array $response ): void {
		$this->prefetched_response = $response;
	}

	/**
	 * Run the compression 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_http_compression();
		$checks[] = $this->check_php_compression();
		$checks[] = $this->check_htaccess_compression();

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

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

	/**
	 * Check HTTP response for compression encoding.
	 *
	 * @return array{label: string, status: string, message: string, value: string|null}
	 */
	private function check_http_compression(): array {
		if ( null !== $this->prefetched_response ) {
			$response = $this->prefetched_response;
		} else {
			// Try with gzip + Brotli first; fall back to gzip-only if cURL lacks Brotli support.
			$response = wp_remote_get(
				home_url( '/' ),
				array(
					'timeout'   => 5,
					'sslverify' => false,
					'headers'   => array(
						'Accept-Encoding' => 'gzip, br',
					),
				)
			);

			if ( is_wp_error( $response ) ) {
				$response = wp_remote_get(
					home_url( '/' ),
					array(
						'timeout'   => 5,
						'sslverify' => false,
						'headers'   => array(
							'Accept-Encoding' => 'gzip',
						),
					)
				);
			}
		}

		if ( is_wp_error( $response ) ) {
			return array(
				'label'   => __( 'HTTP Compression', 'monitor-cat' ),
				'status'  => 'warning',
				'message' => __( 'Could not check HTTP compression. Request failed.', 'monitor-cat' ),
				'value'   => null,
			);
		}

		$headers          = wp_remote_retrieve_headers( $response );
		$content_encoding = $headers['content-encoding'] ?? '';

		if ( stripos( $content_encoding, 'br' ) !== false ) {
			return array(
				'label'   => __( 'HTTP Compression', 'monitor-cat' ),
				'status'  => 'good',
				'message' => __( 'Brotli compression is active. This provides excellent compression ratios.', 'monitor-cat' ),
				'value'   => 'Brotli',
			);
		}

		if ( stripos( $content_encoding, 'gzip' ) !== false ) {
			return array(
				'label'   => __( 'HTTP Compression', 'monitor-cat' ),
				'status'  => 'good',
				'message' => __( 'GZIP compression is active. Responses are compressed before transfer.', 'monitor-cat' ),
				'value'   => 'GZIP',
			);
		}

		return array(
			'label'   => __( 'HTTP Compression', 'monitor-cat' ),
			'status'  => 'critical',
			'message' => __( 'No HTTP compression detected. Enable GZIP or Brotli to reduce transfer sizes significantly.', 'monitor-cat' ),
			'value'   => __( 'None', 'monitor-cat' ),
		);
	}

	/**
	 * Check if PHP zlib output compression is enabled.
	 *
	 * @return array{label: string, status: string, message: string, value: string|null}
	 */
	private function check_php_compression(): array {
		$zlib_enabled = (bool) ini_get( 'zlib.output_compression' );

		if ( $zlib_enabled ) {
			return array(
				'label'   => __( 'PHP Compression', 'monitor-cat' ),
				'status'  => 'good',
				'message' => __( 'PHP zlib.output_compression is enabled.', 'monitor-cat' ),
				'value'   => __( 'Enabled', 'monitor-cat' ),
			);
		}

		return array(
			'label'   => __( 'PHP Compression', 'monitor-cat' ),
			'status'  => 'warning',
			'message' => __( 'PHP zlib.output_compression is disabled. This is fine if server-level compression (mod_deflate/Brotli) is active.', 'monitor-cat' ),
			'value'   => __( 'Disabled', 'monitor-cat' ),
		);
	}

	/**
	 * Check .htaccess for compression directives (Apache only).
	 *
	 * @return array{label: string, status: string, message: string, value: string|null}
	 */
	private function check_htaccess_compression(): array {
		// Only relevant for Apache.
		$server_software = isset( $_SERVER['SERVER_SOFTWARE'] )
			? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) )
			: '';

		if ( stripos( $server_software, 'apache' ) === false ) {
			return array(
				'label'   => __( 'Server Compression Config', 'monitor-cat' ),
				'status'  => 'good',
				/* translators: %s: server software name */
				'message' => sprintf( __( 'Non-Apache server detected (%s). Check server configuration for compression settings.', 'monitor-cat' ), $server_software ? esc_html( $server_software ) : __( 'Unknown', 'monitor-cat' ) ),
				'value'   => $server_software ? $server_software : null,
			);
		}

		$htaccess_path = ABSPATH . '.htaccess';

		if ( ! file_exists( $htaccess_path ) || ! is_readable( $htaccess_path ) ) {
			return array(
				'label'   => __( 'Server Compression Config', 'monitor-cat' ),
				'status'  => 'warning',
				'message' => __( 'Could not read .htaccess file to check for compression directives.', 'monitor-cat' ),
				'value'   => null,
			);
		}

		// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Reading local file, not a URL.
		$htaccess_content = file_get_contents( $htaccess_path );

		if ( false === $htaccess_content ) {
			return array(
				'label'   => __( 'Server Compression Config', 'monitor-cat' ),
				'status'  => 'warning',
				'message' => __( 'Could not read .htaccess file contents.', 'monitor-cat' ),
				'value'   => null,
			);
		}

		$has_deflate = (bool) preg_match( '/mod_deflate|AddOutputFilterByType\s+DEFLATE/i', $htaccess_content );
		$has_gzip    = (bool) preg_match( '/mod_gzip|mod_gzip_on/i', $htaccess_content );

		if ( $has_deflate || $has_gzip ) {
			$type = $has_deflate ? 'mod_deflate' : 'mod_gzip';

			return array(
				'label'   => __( 'Server Compression Config', 'monitor-cat' ),
				'status'  => 'good',
				/* translators: %s: compression module name */
				'message' => sprintf( __( 'Apache compression module detected in .htaccess: %s.', 'monitor-cat' ), $type ),
				'value'   => $type,
			);
		}

		return array(
			'label'   => __( 'Server Compression Config', 'monitor-cat' ),
			'status'  => 'warning',
			'message' => __( 'No compression directives found in .htaccess. Consider adding mod_deflate rules.', 'monitor-cat' ),
			'value'   => null,
		);
	}

	/**
	 * Calculate the overall score from checks.
	 *
	 * @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( 60, 20, 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';
	}
}
