<?php
/**
 * Offline VAT identifier handling.
 *
 * @package Harbor_EU_EInvoicing_For_WooCommerce
 */

defined( 'ABSPATH' ) || exit;

class HEIN_VAT {

	private const FORMATS = [
		'AT' => '/^ATU[0-9]{8}$/',
		'BE' => '/^BE[01][0-9]{9}$/',
		'BG' => '/^BG[0-9]{9,10}$/',
		'CY' => '/^CY[0-9]{8}[A-Z]$/',
		'CZ' => '/^CZ[0-9]{8,10}$/',
		'DE' => '/^DE[0-9]{9}$/',
		'DK' => '/^DK[0-9]{8}$/',
		'EE' => '/^EE[0-9]{9}$/',
		'EL' => '/^EL[0-9]{9}$/',
		'ES' => '/^ES[A-Z0-9][0-9]{7}[A-Z0-9]$/',
		'FI' => '/^FI[0-9]{8}$/',
		'FR' => '/^FR[A-Z0-9]{2}[0-9]{9}$/',
		'HR' => '/^HR[0-9]{11}$/',
		'HU' => '/^HU[0-9]{8}$/',
		'IE' => '/^IE([0-9]{7}[A-W]|[7-9][A-Z0-9][0-9]{5}[A-W]|[0-9]{7}[A-W][AH])$/',
		'IT' => '/^IT[0-9]{11}$/',
		'LT' => '/^LT([0-9]{9}|[0-9]{12})$/',
		'LU' => '/^LU[0-9]{8}$/',
		'LV' => '/^LV[0-9]{11}$/',
		'MT' => '/^MT[0-9]{8}$/',
		'NL' => '/^NL[0-9]{9}B[0-9]{2}$/',
		'PL' => '/^PL[0-9]{10}$/',
		'PT' => '/^PT[0-9]{9}$/',
		'RO' => '/^RO[0-9]{2,10}$/',
		'SE' => '/^SE[0-9]{12}$/',
		'SI' => '/^SI[0-9]{8}$/',
		'SK' => '/^SK[0-9]{10}$/',
	];

	public static function normalize( string $vat ): string {
		return (string) preg_replace( '/[^A-Z0-9]/', '', strtoupper( $vat ) );
	}

	/**
	 * A customer typing bare digits must be given their country's prefix or the order is silently
	 * misclassified as domestic and the reverse charge is lost
	 */
	public static function canonical( string $vat, string $country ): string {
		$vat = self::normalize( $vat );

		if ( '' === $vat ) {
			return '';
		}

		if ( preg_match( '/^[A-Z]{2}/', $vat ) ) {
			return $vat;
		}

		return HEIN_Codes::vat_prefix( $country ) . $vat;
	}

	public static function country( string $vat ): string {
		$prefix = substr( self::normalize( $vat ), 0, 2 );

		if ( ! isset( self::FORMATS[ $prefix ] ) ) {
			return '';
		}

		return HEIN_Codes::country_from_vat_prefix( $prefix );
	}

	public static function is_valid_format( string $vat ): bool {
		$vat    = self::normalize( $vat );
		$prefix = substr( $vat, 0, 2 );

		if ( ! isset( self::FORMATS[ $prefix ] ) ) {
			return false;
		}

		return 1 === preg_match( self::FORMATS[ $prefix ], $vat );
	}

	/**
	 * Deliberately not consulted by is_valid_for(): a VAT id registered in a country other than the
	 * billing one is normal (VAT groups, branch billing, fiscal representatives) and rejecting it
	 * would state a wrong tax treatment. Kept as public API for the hein_tax_category and
	 * hein_buyer_endpoint filter surface, where a store DOES want to know the two disagree
	 */
	public static function matches_country( string $vat, string $country ): bool {
		$resolved = self::country( $vat );

		return '' !== $resolved && strtoupper( $country ) === $resolved;
	}

	/**
	 * The only gate a VAT id passes before entering the invoice array: BR-CO-09 is fatal on a
	 * PartyTaxScheme/CompanyID whose first two characters are not an ISO alpha-2 country code.
	 * A typed prefix is trusted as given, because VAT groups, branch billing and fiscal
	 * representatives all register a business in a country other than the one it is billed at:
	 * discarding such an id loses the reverse charge and states a wrong tax treatment. The billing
	 * country is only ever used to complete a number typed as bare digits
	 */
	public static function is_valid_for( string $vat, string $country ): bool {
		return self::is_valid_format( self::canonical( $vat, $country ) );
	}
}
