<?php

namespace Simplyin\Simplyin_W_Plugin\Payments\Gateway\Validation;

use InvalidArgumentException;

/**
 * Card Brand Validator
 *
 * Validates and normalizes card brand names for SimplyIN payment processing.
 * Ensures only supported card brands are processed.
 *
 * @package Simplyin\Simplyin_W_Plugin\Payments\Gateway\Validation
 */
class Card_Brand_Validator {

	/**
	 * Canonical brands known to the system
	 *
	 * Note: This is not the same as "allowed by provider".
	 *
	 * @var array<string, string> Array of normalized brand names
	 */
	private const KNOWN_BRANDS = array(
		'visa'       => 'visa',
		'mastercard' => 'mastercard',
		'maestro'    => 'maestro',
	);

	/**
	 * Brand aliases mapping to canonical names
	 *
	 * @var array<string, string> Array of aliases to canonical brand names
	 */
	private const BRAND_ALIASES = array(
		// Visa aliases.
		'visa'         => 'visa',
		'visa card'    => 'visa',
		'visacard'     => 'visa',

		// Mastercard aliases.
		'mastercard'   => 'mastercard',
		'master card'  => 'mastercard',
		'master'       => 'mastercard',
		'mc'           => 'mastercard',
		'm/c'          => 'mastercard',

		// Maestro aliases.
		'maestro'      => 'maestro',
		'maestro card' => 'maestro',
	);

	/**
	 * Normalize card brand name
	 *
	 * Converts input to lowercase, trims whitespace, and maps aliases to canonical names.
	 *
	 * @param string $brand Raw brand name from frontend.
	 * @return string Normalized brand name.
	 */
	public static function normalize_brand( string $brand ): string {
		$normalized = strtolower( trim( $brand ) );

		// Map aliases to canonical names.
		if ( isset( self::BRAND_ALIASES[ $normalized ] ) ) {
			return self::BRAND_ALIASES[ $normalized ];
		}

		return $normalized;
	}

	/**
	 * Validate if brand is known (canonical list)
	 *
	 * @param string $brand Brand name to validate.
	 * @return bool True if brand is known, false otherwise.
	 */
	public static function is_known_brand( string $brand ): bool {
		$normalized = self::normalize_brand( $brand );
		return isset( self::KNOWN_BRANDS[ $normalized ] );
	}

	/**
	 * Provider-specific allowed brands
	 *
	 * @param string $provider_id Payment provider id (e.g. 'payu').
	 * @return array<string> Allowed brand names for the provider.
	 */
	public static function get_allowed_brands_for_provider( string $provider_id ): array {
		$provider_id = strtolower( trim( $provider_id ) );

		// Default baseline — conservative.
		$map = array(
			// PayU brand policy (observed upstream: visa/mastercard only).
			'payu' => array( 'visa', 'mastercard' ),
		);

		$allowed = $map[ $provider_id ] ?? array( 'visa', 'mastercard' );

		// Log provider configuration.
		if ( function_exists( 'simplyin' ) && method_exists( simplyin(), 'get_simplyin_logger' ) ) {
			simplyin()->get_simplyin_logger()->log_if_wpdebug_on(
				sprintf(
					"[Card_Brand_Validator] [get_allowed_brands_for_provider] Provider '%s' allows: %s",
					esc_html( $provider_id ),
					esc_html( wp_json_encode( $allowed ) )
				),
				'payments'
			);
		}

		return $allowed;
	}

	/**
	 * Get canonical brand name for API
	 *
	 * @param string $brand Raw brand name from frontend.
	 * @return string Canonical brand name for API.
	 * @throws InvalidArgumentException If brand is not supported.
	 */
	public static function get_canonical_brand( string $brand ): string {
		$normalized = self::normalize_brand( $brand );

		if ( ! self::is_known_brand( $brand ) ) {
			throw new InvalidArgumentException(
				wp_kses(
					sprintf(
					/* translators: %s: unsupported card brand name. */
						esc_html__( 'Unsupported card type: %s', 'simplyin' ),
						esc_html( $brand )
					),
					array()
				)
			);
		}

		return self::KNOWN_BRANDS[ $normalized ];
	}

	/**
	 * Validate and return canonical brand allowed for provider
	 *
	 * @param string $brand Raw brand name from frontend.
	 * @param string $provider_id Provider identifier.
	 * @return string Canonical brand name allowed for provider.
	 * @throws InvalidArgumentException If brand is unknown or not allowed for provider.
	 */
	public static function get_canonical_brand_for_provider( string $brand, string $provider_id ): string {
		// Log input parameters.
		if ( function_exists( 'simplyin' ) && method_exists( simplyin(), 'get_simplyin_logger' ) ) {
			simplyin()->get_simplyin_logger()->log_if_wpdebug_on(
				sprintf(
					"[Card_Brand_Validator] [get_canonical_brand_for_provider] Input: brand='%s', provider='%s'",
					esc_html( $brand ),
					esc_html( $provider_id )
				),
				'payments'
			);
		}

		$canonical = self::get_canonical_brand( $brand );
		$allowed   = self::get_allowed_brands_for_provider( $provider_id );

		// Log validation details.
		if ( function_exists( 'simplyin' ) && method_exists( simplyin(), 'get_simplyin_logger' ) ) {
			simplyin()->get_simplyin_logger()->log_if_wpdebug_on(
				sprintf(
					"[Card_Brand_Validator] [get_canonical_brand_for_provider] Validation: canonical='%s', allowed=%s",
					esc_html( $canonical ),
					esc_html( wp_json_encode( $allowed ) )
				),
				'payments'
			);
		}

		if ( ! in_array( $canonical, $allowed, true ) ) {
			// Build user-friendly card brand names.
			$brand_names = array(
				'visa'       => 'Visa',
				'mastercard' => 'Mastercard',
				'maestro'    => 'Maestro',
			);

			$brand_display_name  = $brand_names[ $canonical ] ?? ucfirst( $canonical );
			$allowed_brands_list = array_map(
				function ( $brand ) use ( $brand_names ) {
					return $brand_names[ $brand ] ?? ucfirst( $brand );
				},
				$allowed
			);
			$allowed_brands_text = count( $allowed_brands_list ) === 2
				? sprintf(
					/* translators: %1$s and %2$s are card brand names like "Visa" and "Mastercard". */
					esc_html__( '%1$s or %2$s', 'simplyin' ),
					$allowed_brands_list[0],
					$allowed_brands_list[1]
				)
				: implode( ', ', array_slice( $allowed_brands_list, 0, -1 ) ) . ' ' . esc_html__( 'or', 'simplyin' ) . ' ' . end( $allowed_brands_list );

			$error_msg = sprintf(
				/* translators: 1: card brand name like "Maestro", 2: supported brands like "Visa or Mastercard". */
				esc_html__( 'Sorry, %1$s cards are not supported. Please use %2$s.', 'simplyin' ),
				$brand_display_name,
				$allowed_brands_text
			);

			// Log rejection.
			if ( function_exists( 'simplyin' ) && method_exists( simplyin(), 'get_simplyin_logger' ) ) {
				simplyin()->get_simplyin_logger()->log_if_wpdebug_on(
					sprintf(
						"[Card_Brand_Validator] [get_canonical_brand_for_provider] REJECTED: '%s' not in allowed list for provider '%s'",
						esc_html( $canonical ),
						esc_html( $provider_id )
					),
					'payments'
				);
			}

			throw new InvalidArgumentException( wp_kses( $error_msg, array() ) );
		}

		// Log success.
		if ( function_exists( 'simplyin' ) && method_exists( simplyin(), 'get_simplyin_logger' ) ) {
			simplyin()->get_simplyin_logger()->log_if_wpdebug_on(
				sprintf(
					"[Card_Brand_Validator] [get_canonical_brand_for_provider] APPROVED: '%s' is allowed for provider '%s'",
					esc_html( $canonical ),
					esc_html( $provider_id )
				),
				'payments'
			);
		}

		return $canonical;
	}

	/**
	 * Get all supported brands
	 *
	 * @return array<string> Array of supported brand names.
	 */
	public static function get_supported_brands(): array {
		return array_values( self::KNOWN_BRANDS );
	}

	/**
	 * Get user-friendly error message for unsupported brand
	 *
	 * @param string $brand Unsupported brand name.
	 * @return string User-friendly error message.
	 */
	public static function get_unsupported_brand_message( string $brand ): string {
		$supported      = self::get_supported_brands();
		$supported_list = implode( ', ', $supported );

		return sprintf(
			/* translators: %1$s: unsupported brand, %2$s: list of supported brands. */
			esc_html__( 'Card type "%1$s" is not supported. Supported types cards: %2$s', 'simplyin' ),
			esc_html( $brand ),
			esc_html( $supported_list )
		);
	}
}
