<?php
/**
 * Image alt text analyzer.
 *
 * Checks images for proper alt text attributes.
 *
 * @package MonitorCat\Modules\Seo\Analyzer
 */

declare(strict_types=1);

namespace MonitorCat\Modules\Seo\Analyzer;

defined( 'ABSPATH' ) || exit;

/**
 * Analyzes images in post content for alt text presence.
 */
final class ImageAltAnalyzer {

	/**
	 * Run the image alt text analysis on a post.
	 *
	 * @param int $post_id The post ID to analyze.
	 * @return array{score: int, status: string, checks: array<int, array{label: string, status: string, message: string, value: string|null}>}
	 */
	public function analyze( int $post_id ): array {
		$content = get_post_field( 'post_content', $post_id );
		$content = is_string( $content ) ? $content : '';
		$checks  = array();

		$image_stats = $this->count_images( $content );
		$checks[]    = $this->check_image_alt_coverage( $image_stats );
		$checks[]    = $this->check_images_without_alt( $image_stats );
		$checks[]    = $this->check_featured_image_alt( $post_id );

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

		return array(
			'score'  => $score,
			'status' => $status,
			'checks' => $checks,
		);
	}

	/**
	 * Count images and their alt text status.
	 *
	 * @param string $content The post content.
	 * @return array{total: int, with_alt: int, without_alt: int, empty_alt: int, images: array<int, array{src: string, alt: string|null}>}
	 */
	private function count_images( string $content ): array {
		$stats = array(
			'total'       => 0,
			'with_alt'    => 0,
			'without_alt' => 0,
			'empty_alt'   => 0,
			'images'      => array(),
		);

		if ( ! preg_match_all( '/<img\s[^>]*>/is', $content, $matches ) ) {
			return $stats;
		}

		foreach ( $matches[0] as $img_tag ) {
			++$stats['total'];

			$src = '';
			if ( preg_match( '/src=["\']([^"\']*)["\']/', $img_tag, $src_match ) ) {
				$src = $src_match[1];
			}

			if ( preg_match( '/alt=["\']([^"\']*)["\']/', $img_tag, $alt_match ) ) {
				$alt = $alt_match[1];

				if ( '' === trim( $alt ) ) {
					++$stats['empty_alt'];
					++$stats['without_alt'];
					$stats['images'][] = array(
						'src' => $src,
						'alt' => null,
					);
				} else {
					++$stats['with_alt'];
					$stats['images'][] = array(
						'src' => $src,
						'alt' => $alt,
					);
				}
			} else {
				++$stats['without_alt'];
				$stats['images'][] = array(
					'src' => $src,
					'alt' => null,
				);
			}
		}

		return $stats;
	}

	/**
	 * Check overall alt text coverage.
	 *
	 * @param array{total: int, with_alt: int, without_alt: int, empty_alt: int, images: array<int, array{src: string, alt: string|null}>} $stats Image statistics.
	 * @return array{label: string, status: string, message: string, value: string|null}
	 */
	private function check_image_alt_coverage( array $stats ): array {
		if ( 0 === $stats['total'] ) {
			return array(
				'label'   => __( 'Image Alt Coverage', 'monitor-cat' ),
				'status'  => 'pass',
				'message' => __( 'No images found in content. No alt text issues.', 'monitor-cat' ),
				'value'   => '0 images',
			);
		}

		$percentage = (int) round( ( $stats['with_alt'] / $stats['total'] ) * 100 );

		if ( 100 === $percentage ) {
			return array(
				'label'   => __( 'Image Alt Coverage', 'monitor-cat' ),
				'status'  => 'pass',
				/* translators: %d: number of images */
				'message' => sprintf( __( 'All %d image(s) have alt text.', 'monitor-cat' ), $stats['total'] ),
				'value'   => sprintf( '%d/%d (100%%)', $stats['with_alt'], $stats['total'] ),
			);
		}

		$status = $percentage >= 50 ? 'warning' : 'fail';

		return array(
			'label'   => __( 'Image Alt Coverage', 'monitor-cat' ),
			'status'  => $status,
			/* translators: 1: images with alt, 2: total images, 3: percentage */
			'message' => sprintf( __( '%1$d of %2$d images have alt text (%3$d%%). All images should have descriptive alt text.', 'monitor-cat' ), $stats['with_alt'], $stats['total'], $percentage ),
			'value'   => sprintf( '%d/%d (%d%%)', $stats['with_alt'], $stats['total'], $percentage ),
		);
	}

	/**
	 * Report images missing alt text.
	 *
	 * @param array{total: int, with_alt: int, without_alt: int, empty_alt: int, images: array<int, array{src: string, alt: string|null}>} $stats Image statistics.
	 * @return array{label: string, status: string, message: string, value: string|null}
	 */
	private function check_images_without_alt( array $stats ): array {
		if ( 0 === $stats['without_alt'] ) {
			return array(
				'label'   => __( 'Missing Alt Text', 'monitor-cat' ),
				'status'  => 'pass',
				'message' => __( 'No images are missing alt text.', 'monitor-cat' ),
				'value'   => '0',
			);
		}

		$missing_sources = array();
		foreach ( $stats['images'] as $image ) {
			if ( null === $image['alt'] ) {
				$missing_sources[] = ! empty( $image['src'] ) ? basename( $image['src'] ) : __( '(unknown source)', 'monitor-cat' );
			}
		}

		return array(
			'label'   => __( 'Missing Alt Text', 'monitor-cat' ),
			'status'  => 'fail',
			/* translators: 1: count, 2: list of filenames */
			'message' => sprintf( __( '%1$d image(s) missing alt text: %2$s', 'monitor-cat' ), $stats['without_alt'], implode( ', ', $missing_sources ) ),
			'value'   => (string) $stats['without_alt'],
		);
	}

	/**
	 * Check if the featured image has alt text.
	 *
	 * @param int $post_id The post ID.
	 * @return array{label: string, status: string, message: string, value: string|null}
	 */
	private function check_featured_image_alt( int $post_id ): array {
		if ( ! has_post_thumbnail( $post_id ) ) {
			return array(
				'label'   => __( 'Featured Image Alt', 'monitor-cat' ),
				'status'  => 'warning',
				'message' => __( 'No featured image set.', 'monitor-cat' ),
				'value'   => null,
			);
		}

		$thumbnail_id = get_post_thumbnail_id( $post_id );

		if ( ! $thumbnail_id ) {
			return array(
				'label'   => __( 'Featured Image Alt', 'monitor-cat' ),
				'status'  => 'warning',
				'message' => __( 'Could not retrieve featured image ID.', 'monitor-cat' ),
				'value'   => null,
			);
		}

		$alt_text = get_post_meta( (int) $thumbnail_id, '_wp_attachment_image_alt', true );

		if ( ! empty( $alt_text ) && is_string( $alt_text ) ) {
			return array(
				'label'   => __( 'Featured Image Alt', 'monitor-cat' ),
				'status'  => 'pass',
				'message' => __( 'Featured image has alt text.', 'monitor-cat' ),
				'value'   => $alt_text,
			);
		}

		return array(
			'label'   => __( 'Featured Image Alt', 'monitor-cat' ),
			'status'  => 'fail',
			'message' => __( 'Featured image is missing alt text. Add alt text in the Media Library.', 'monitor-cat' ),
			'value'   => null,
		);
	}

	/**
	 * Calculate score based on alt text coverage.
	 *
	 * @param array{total: int, with_alt: int, without_alt: int, empty_alt: int, images: array<int, array{src: string, alt: string|null}>} $stats Image statistics.
	 * @return int Score from 0 to 100.
	 */
	private function calculate_score( array $stats ): int {
		if ( 0 === $stats['total'] ) {
			return 100;
		}

		return (int) round( ( $stats['with_alt'] / $stats['total'] ) * 100 );
	}

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

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

		return 'fail';
	}
}
