<?php
/**
 * Alt text quality scoring and weak-text detection.
 *
 * @package AIAltTextGenerator
 * @since 1.4.0
 */

namespace AIAltTextGenerator;

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

/**
 * Quality scorer for attachment alt text.
 *
 * @since 1.4.0
 */
class QualityScorer {

	/** @var list<string> */
	private const WEAK_EXACT = [
		'image',
		'photo',
		'picture',
		'logo',
		'banner',
		'screenshot',
		'untitled',
		'product image',
		'img',
		'pic',
	];

	/** @return list<string> */
	public static function weakExactWords(): array {
		return self::WEAK_EXACT;
	}

	/**
	 * SQL fragment matching weak alt heuristics used in library scans and bulk queries.
	 */
	public static function weakAltSqlClause( string $column = 'am.meta_value' ): string {
		$parts = [ 'CHAR_LENGTH(' . $column . ') < 5' ];

		foreach ( self::WEAK_EXACT as $word ) {
			$parts[] = 'LOWER(' . $column . ") = '" . esc_sql( $word ) . "'";
		}

		$parts[] = $column . " REGEXP '^IMG[_-]?[0-9]+'";

		return implode( ' OR ', $parts );
	}

	/**
	 * Score alt text and return a status slug.
	 */
	public static function score( string $alt_text, int $max_chars = 125 ): string {
		$alt_text = trim( $alt_text );

		if ( $alt_text === '' ) {
			return 'missing';
		}

		$lower = strtolower( $alt_text );

		if ( in_array( $lower, self::weakExactWords(), true ) ) {
			return 'weak';
		}

		if ( preg_match( '/^img[_-]?\d+/i', $alt_text ) ) {
			return 'weak';
		}

		if ( mb_strlen( $alt_text ) < 5 ) {
			return 'too_short';
		}

		if ( $max_chars > 0 && mb_strlen( $alt_text ) > ( $max_chars + 20 ) ) {
			return 'too_long';
		}

		if ( self::looksKeywordStuffed( $alt_text ) ) {
			return 'keyword_stuffed';
		}

		return 'good';
	}

	/**
	 * Whether alt text is considered weak or problematic.
	 */
	public static function isWeak( string $alt_text, int $max_chars = 125 ): bool {
		$status = self::score( $alt_text, $max_chars );

		return in_array( $status, [ 'weak', 'too_short', 'keyword_stuffed' ], true );
	}

	/**
	 * Basic keyword stuffing heuristic.
	 */
	private static function looksKeywordStuffed( string $alt_text ): bool {
		$words = preg_split( '/\s+/', strtolower( trim( $alt_text ) ) ) ?: [];
		$words = array_filter( $words );

		if ( count( $words ) < 6 ) {
			return false;
		}

		$unique = count( array_unique( $words ) );
		$ratio  = $unique / count( $words );

		return $ratio < 0.45 || count( $words ) > 18;
	}

	/**
	 * Human-readable label for a status slug.
	 */
	public static function label( string $status ): string {
		$labels = [
			'missing'          => __( 'Missing', 'wp-powersuite' ),
			'weak'             => __( 'Weak', 'wp-powersuite' ),
			'too_short'        => __( 'Too short', 'wp-powersuite' ),
			'too_long'         => __( 'Too long', 'wp-powersuite' ),
			'keyword_stuffed'  => __( 'Keyword stuffed', 'wp-powersuite' ),
			'duplicate'        => __( 'Duplicate', 'wp-powersuite' ),
			'good'             => __( 'Looks good', 'wp-powersuite' ),
			'decorative'       => __( 'Decorative', 'wp-powersuite' ),
			'needs_review'     => __( 'Needs review', 'wp-powersuite' ),
			'generated'        => __( 'Generated', 'wp-powersuite' ),
			'failed'           => __( 'Failed', 'wp-powersuite' ),
		];

		return $labels[ $status ] ?? ucfirst( str_replace( '_', ' ', $status ) );
	}
}
