<?php
/**
 * Image optimization analyzer.
 *
 * Checks image optimization status including WebP support and lazy loading.
 *
 * @package MonitorCat\Modules\Speed\Analyzer
 */

declare(strict_types=1);

namespace MonitorCat\Modules\Speed\Analyzer;

defined( 'ABSPATH' ) || exit;

use MonitorCat\Settings;

/**
 * Analyzes image optimization across the media library.
 * Reflexive: aware of Monitor.Cat lazy media optimization.
 */
final class ImageOptAnalyzer {

	/**
	 * Plugin settings for reflexive analysis.
	 *
	 * @var Settings
	 */
	private Settings $settings;

	/**
	 * Cached image attachment IDs to avoid duplicate queries.
	 *
	 * @var int[]|null
	 */
	private ?array $cached_image_ids = null;

	/**
	 * Constructor.
	 *
	 * @param Settings $settings Plugin settings.
	 */
	public function __construct( Settings $settings ) {
		$this->settings = $settings;
	}

	/**
	 * Known image optimization plugins and their main file paths.
	 *
	 * @var array<string, string>
	 */
	private const OPTIMIZATION_PLUGINS = array(
		'Imagify'    => 'imagify/imagify.php',
		'ShortPixel' => 'shortpixel-image-optimiser/wp-shortpixel.php',
		'EWWW'       => 'ewww-image-optimizer/ewww-image-optimizer.php',
		'Smush'      => 'wp-smushit/wp-smush.php',
	);

	/**
	 * Run the image optimization 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_total_images();
		$checks[] = $this->check_webp_coverage();
		$checks[] = $this->check_lazy_loading();
		$checks[] = $this->check_optimization_plugin();

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

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

	/**
	 * Get all image attachment IDs.
	 *
	 * @return int[] Array of attachment IDs.
	 */
	private function get_image_ids(): array {
		if ( null !== $this->cached_image_ids ) {
			return $this->cached_image_ids;
		}

		$images = get_posts(
			array(
				'post_type'      => 'attachment',
				'post_mime_type' => 'image',
				'posts_per_page' => -1,
				'fields'         => 'ids',
				'post_status'    => 'inherit',
				'no_found_rows'  => true,
			)
		);

		$this->cached_image_ids = array_map( 'intval', $images );

		return $this->cached_image_ids;
	}

	/**
	 * Check total number of images in the media library.
	 *
	 * @return array{label: string, status: string, message: string, value: string|null}
	 */
	private function check_total_images(): array {
		$image_ids = $this->get_image_ids();
		$count     = count( $image_ids );

		if ( 0 === $count ) {
			return array(
				'label'   => __( 'Total Images', 'monitor-cat' ),
				'status'  => 'good',
				'message' => __( 'No images found in the media library.', 'monitor-cat' ),
				'value'   => '0',
			);
		}

		return array(
			'label'   => __( 'Total Images', 'monitor-cat' ),
			'status'  => 'good',
			/* translators: %d: number of images */
			'message' => sprintf( __( 'Found %d image(s) in the media library.', 'monitor-cat' ), $count ),
			'value'   => (string) $count,
		);
	}

	/**
	 * Check WebP coverage across image attachments.
	 *
	 * @return array{label: string, status: string, message: string, value: string|null}
	 */
	private function check_webp_coverage(): array {
		$image_ids = $this->get_image_ids();
		$total     = count( $image_ids );

		if ( 0 === $total ) {
			return array(
				'label'   => __( 'WebP Coverage', 'monitor-cat' ),
				'status'  => 'good',
				'message' => __( 'No images to check for WebP versions.', 'monitor-cat' ),
				'value'   => null,
			);
		}

		$upload_dir = wp_get_upload_dir();
		$base_dir   = $upload_dir['basedir'];
		$webp_count = 0;

		foreach ( $image_ids as $image_id ) {
			$file = get_attached_file( $image_id );

			if ( ! $file ) {
				continue;
			}

			// Check if a .webp version exists alongside the original.
			$webp_path = $file . '.webp';
			$alt_path  = preg_replace( '/\.\w+$/', '.webp', $file );

			if ( file_exists( $webp_path ) || ( $alt_path && file_exists( $alt_path ) ) ) {
				++$webp_count;
			}
		}

		$percentage = (int) round( ( $webp_count / $total ) * 100 );
		$value      = sprintf( '%d/%d (%d%%)', $webp_count, $total, $percentage );

		if ( $percentage >= 80 ) {
			return array(
				'label'   => __( 'WebP Coverage', 'monitor-cat' ),
				'status'  => 'good',
				/* translators: %s: coverage ratio and percentage */
				'message' => sprintf( __( '%s of images have WebP versions. Excellent coverage.', 'monitor-cat' ), $value ),
				'value'   => $value,
			);
		}

		if ( $percentage >= 30 ) {
			return array(
				'label'   => __( 'WebP Coverage', 'monitor-cat' ),
				'status'  => 'warning',
				/* translators: %s: coverage ratio and percentage */
				'message' => sprintf( __( '%s of images have WebP versions. Consider converting more images.', 'monitor-cat' ), $value ),
				'value'   => $value,
			);
		}

		return array(
			'label'   => __( 'WebP Coverage', 'monitor-cat' ),
			'status'  => 'critical',
			/* translators: %s: coverage ratio and percentage */
			'message' => sprintf( __( '%s of images have WebP versions. Install an image optimization plugin to convert images to WebP.', 'monitor-cat' ), $value ),
			'value'   => $value,
		);
	}

	/**
	 * Check if lazy loading is enabled.
	 *
	 * @return array{label: string, status: string, message: string, value: string|null}
	 */
	private function check_lazy_loading(): array {
		$lazy_enabled = wp_lazy_loading_enabled( 'img', 'the_content' );

		$lazy_media_active = (bool) $this->settings->get( 'speed.optimize_lazy_media', true );

		if ( $lazy_enabled ) {
			$message = __( 'Native lazy loading is enabled for images (WordPress 5.5+).', 'monitor-cat' );
			if ( $lazy_media_active ) {
				$message = __( 'Lazy loading active for images (WP native) + iframes and async decoding by Monitor.Cat.', 'monitor-cat' );
			}

			return array(
				'label'   => __( 'Lazy Loading', 'monitor-cat' ),
				'status'  => 'good',
				'message' => $message,
				'value'   => __( 'Enabled', 'monitor-cat' ),
			);
		}

		return array(
			'label'   => __( 'Lazy Loading', 'monitor-cat' ),
			'status'  => 'critical',
			'message' => __( 'Native lazy loading is disabled. Images will load eagerly, slowing initial page load.', 'monitor-cat' ),
			'value'   => __( 'Disabled', 'monitor-cat' ),
		);
	}

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

		$active_plugins = array();

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

		if ( ! empty( $active_plugins ) ) {
			return array(
				'label'   => __( 'Optimization Plugin', 'monitor-cat' ),
				'status'  => 'good',
				/* translators: %s: comma-separated list of active plugin names */
				'message' => sprintf( __( 'Active image optimization plugin(s): %s.', 'monitor-cat' ), implode( ', ', $active_plugins ) ),
				'value'   => implode( ', ', $active_plugins ),
			);
		}

		return array(
			'label'   => __( 'Optimization Plugin', 'monitor-cat' ),
			'status'  => 'warning',
			'message' => __( 'No image optimization plugin detected. Consider installing Imagify, ShortPixel, EWWW, or Smush.', '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( 10, 35, 30, 25 );

		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';
	}
}
