<?php
/**
 * Product readiness rule engine.
 *
 * @package CertaCart_EU_Product_Readiness
 */

namespace CertaCart\EU_Product_Readiness;

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

final class Rules {
	public const META_PREFIX = '_ccpr_';

	/**
	 * Evaluate a WooCommerce product.
	 *
	 * The score is a product-readiness indicator only. It is deliberately not
	 * presented as a legal-compliance determination.
	 *
	 * @param \WC_Product $product Product object.
	 * @param array       $settings Plugin settings.
	 * @return array{score:float,score_available:bool,in_scope:bool,critical:int,warning:int,info:int,findings:array}
	 */
	public static function evaluate( \WC_Product $product, array $settings ): array {
		$findings = array();
		$target   = self::meta_or_default( $product, 'targets_eu', $settings['default_targets_eu'] ?? 'review' );

		if ( 'no' === $target ) {
			return array(
				'score'           => 100.0,
				'score_available' => false,
				'in_scope'        => false,
				'critical' => 0,
				'warning'  => 0,
				'info'     => 1,
				'findings' => array(
					self::finding( 'outside_scope', 'info', __( 'Product is marked as not offered to EU consumers.', 'certacart-eu-product-readiness' ), 'targets_eu' ),
				),
			);
		}

		$score_available = 'yes' === $target;
		if ( ! $score_available ) {
			$findings[] = self::finding( 'scope_not_configured', 'info', __( 'Confirm whether these products are sold to EU customers. CertaCart still checks every supported field, but leaves the readiness score ungraded.', 'certacart-eu-product-readiness' ), 'targets_eu' );
		}

		self::require_value( $findings, $product, 'manufacturer_name', 'manufacturer_name', __( 'Manufacturer name or registered trade name is missing.', 'certacart-eu-product-readiness' ), 'critical', 14 );
		self::require_value( $findings, $product, 'manufacturer_address', 'manufacturer_address', __( 'Manufacturer postal address is missing.', 'certacart-eu-product-readiness' ), 'critical', 12 );
		self::require_electronic_address( $findings, $product, 'manufacturer_email', 'manufacturer_url', 'manufacturer_contact', __( 'Manufacturer electronic address is missing.', 'certacart-eu-product-readiness' ), 10 );

		$manufacturer_in_eu = self::meta_or_default( $product, 'manufacturer_in_eu', $settings['default_manufacturer_in_eu'] ?? 'unknown' );
		if ( 'unknown' === $manufacturer_in_eu ) {
			$findings[] = self::finding( 'manufacturer_location_unknown', 'warning', __( 'Confirm whether the manufacturer is established in the EU.', 'certacart-eu-product-readiness' ), 'manufacturer_in_eu', 8 );
		} elseif ( 'no' === $manufacturer_in_eu ) {
			self::require_value( $findings, $product, 'responsible_person_name', 'responsible_person_name', __( 'EU responsible person name is missing for a non-EU manufacturer.', 'certacart-eu-product-readiness' ), 'critical', 12 );
			self::require_value( $findings, $product, 'responsible_person_address', 'responsible_person_address', __( 'EU responsible person postal address is missing.', 'certacart-eu-product-readiness' ), 'critical', 10 );
			self::require_electronic_address( $findings, $product, 'responsible_person_email', 'responsible_person_url', 'responsible_person_contact', __( 'EU responsible person electronic address is missing.', 'certacart-eu-product-readiness' ), 8 );
		}

		$identifier = trim( (string) $product->get_meta( self::META_PREFIX . 'product_identifier', true ) );
		if ( '' === $identifier ) {
			$identifier = trim( (string) $product->get_sku() );
		}
		if ( '' === $identifier ) {
			$findings[] = self::finding( 'product_identifier_missing', 'critical', __( 'Product identifier is missing. Add a model, batch, type, or SKU.', 'certacart-eu-product-readiness' ), 'product_identifier', 12 );
		}

		if ( ! $product->get_image_id() ) {
			$findings[] = self::finding( 'product_image_missing', 'warning', __( 'Product image is missing.', 'certacart-eu-product-readiness' ), 'product_image', 5 );
		}

		$safety_state = (string) $product->get_meta( self::META_PREFIX . 'safety_state', true );
		$safety_text  = trim( (string) $product->get_meta( self::META_PREFIX . 'safety_warning', true ) );
		if ( 'not_applicable' === $safety_state ) {
			$findings[] = self::finding( 'safety_not_applicable', 'info', __( 'Safety information is explicitly marked as not applicable; retain the basis for that decision.', 'certacart-eu-product-readiness' ), 'safety_state' );
		} elseif ( 'provided' !== $safety_state || '' === $safety_text ) {
			$findings[] = self::finding( 'safety_information_missing', 'warning', __( 'Warnings or safety information are missing, or their applicability has not been confirmed.', 'certacart-eu-product-readiness' ), 'safety_warning', 8 );
		}

		$display = (string) $product->get_meta( self::META_PREFIX . 'display_information', true );
		if ( '' === $display || 'inherit' === $display ) {
			$display = $settings['display_product_information'] ?? 'yes';
		}
		if ( 'yes' !== $display ) {
			$findings[] = self::finding( 'storefront_display_disabled', 'critical', __( 'Stored product information is not configured for storefront display.', 'certacart-eu-product-readiness' ), 'display_information', 10 );
		}


		$penalty  = 0;
		$critical = 0;
		$warning  = 0;
		$info     = 0;
		foreach ( $findings as $finding ) {
			$penalty += (int) ( $finding['weight'] ?? 0 );
			if ( 'critical' === $finding['severity'] ) {
				++$critical;
			} elseif ( 'warning' === $finding['severity'] ) {
				++$warning;
			} else {
				++$info;
			}
		}

		return array(
			'score'           => (float) max( 0, min( 100, 100 - $penalty ) ),
			'score_available' => $score_available,
			'in_scope'        => true,
			'critical' => $critical,
			'warning'  => $warning,
			'info'     => $info,
			'findings' => $findings,
		);
	}

	private static function require_value( array &$findings, \WC_Product $product, string $meta, string $rule, string $message, string $severity, int $weight ): void {
		if ( '' === trim( (string) $product->get_meta( self::META_PREFIX . $meta, true ) ) ) {
			$findings[] = self::finding( $rule, $severity, $message, $meta, $weight );
		}
	}

	private static function require_electronic_address( array &$findings, \WC_Product $product, string $email_meta, string $url_meta, string $rule, string $message, int $weight ): void {
		$email = sanitize_email( (string) $product->get_meta( self::META_PREFIX . $email_meta, true ) );
		$url   = esc_url_raw( (string) $product->get_meta( self::META_PREFIX . $url_meta, true ), array( 'http', 'https' ) );
		if ( ! is_email( $email ) && ! wp_http_validate_url( $url ) ) {
			$findings[] = self::finding( $rule, 'critical', $message, $email_meta, $weight );
		}
	}

	private static function meta_or_default( \WC_Product $product, string $key, string $default ): string {
		$value = (string) $product->get_meta( self::META_PREFIX . $key, true );
		return '' === $value || 'inherit' === $value ? $default : $value;
	}


	private static function finding( string $rule, string $severity, string $message, string $field, int $weight = 0 ): array {
		return array(
			'rule'     => $rule,
			'severity' => $severity,
			'message'  => $message,
			'field'    => $field,
			'weight'   => $weight,
		);
	}
}
