<?php

declare( strict_types=1 );

namespace Brightfern\Invoice;

/**
 * EN 16931 VAT category codes (BT-151 line, BT-118 breakdown), drawn from
 * UN/CEFACT code list UNCL5305. These drive the most error-prone schematron
 * rules in Peppol BIS, so the mapper must classify every line into exactly one.
 *
 * The codes we support in v1 and what they mean for WooCommerce:
 *
 *   S  Standard rated        — normal taxed line (rate > 0).
 *   Z  Zero rated            — taxed at 0% (e.g. certain goods); rate == 0.
 *   E  Exempt from VAT       — small-business / exempt supply; rate == 0,
 *                              requires an exemption reason (BT-120/BT-121).
 *   AE Reverse charge        — VAT accounted for by the buyer (domestic B2B
 *                              reverse charge); rate == 0, exemption reason
 *                              "Reverse charge".
 *   K  Intra-community supply— goods/services to a VAT-registered buyer in
 *                              another EU member state; rate == 0, exemption
 *                              reason references Article 138 / 44 of 2006/112/EC.
 *   G  Export outside the EU — rate == 0, exemption reason "Export".
 *   O  Out of scope of VAT   — not subject to VAT at all; rate must be absent.
 *
 * BR-* / BR-S-* / BR-AE-* / BR-IC-* schematron rules each constrain which of
 * these may coexist and what reason text/codes are mandatory. See classify().
 */
final class VatCategory {

	public const STANDARD          = 'S';
	public const ZERO              = 'Z';
	public const EXEMPT            = 'E';
	public const REVERSE_CHARGE    = 'AE';
	public const INTRA_COMMUNITY   = 'K';
	public const EXPORT            = 'G';
	public const OUT_OF_SCOPE      = 'O';

	/**
	 * VAT Exemption reason codes (VATEX, used in BT-121). A minimal, correct
	 * subset; extend as national rules demand.
	 */
	public const VATEX_EU_AE  = 'VATEX-EU-AE';   // Reverse charge.
	public const VATEX_EU_IC  = 'VATEX-EU-IC';   // Intra-community supply.
	public const VATEX_EU_G   = 'VATEX-EU-G';    // Export outside the EU.
	public const VATEX_EU_O   = 'VATEX-EU-O';    // Not subject to VAT.

	/**
	 * Default human-readable exemption reason text (BT-120) per category.
	 * Schematron (BR-E-10, BR-AE-10, BR-IC-10, BR-G-10, BR-O-10) requires a
	 * reason *or* a reason code on the breakdown for these categories.
	 *
	 * @return array{0:?string,1:?string} [reasonText, reasonCode]
	 */
	public static function exemptionReason( string $category ): array {
		return match ( $category ) {
			self::REVERSE_CHARGE  => array( 'Reverse charge', self::VATEX_EU_AE ),
			self::INTRA_COMMUNITY => array( 'Intra-Community supply', self::VATEX_EU_IC ),
			self::EXPORT          => array( 'Export outside the EU', self::VATEX_EU_G ),
			self::OUT_OF_SCOPE    => array( 'Not subject to VAT', self::VATEX_EU_O ),
			self::EXEMPT          => array( 'Exempt from VAT', null ),
			default               => array( null, null ),
		};
	}

	/** Categories whose rate is, by rule, always 0. */
	public static function isZeroRateByRule( string $category ): bool {
		return in_array(
			$category,
			array( self::ZERO, self::EXEMPT, self::REVERSE_CHARGE, self::INTRA_COMMUNITY, self::EXPORT ),
			true
		);
	}

	/** Does this category require an exemption reason on the VAT breakdown? */
	public static function requiresExemptionReason( string $category ): bool {
		return in_array(
			$category,
			array( self::EXEMPT, self::REVERSE_CHARGE, self::INTRA_COMMUNITY, self::EXPORT, self::OUT_OF_SCOPE ),
			true
		);
	}

	/**
	 * Decide the VAT category for a line, given the resolved tax rate and the
	 * commercial context. This is where most real-world e-invoicing bugs live,
	 * so the logic is explicit and ordered by precedence.
	 *
	 * @param float       $ratePercent     The line's VAT rate (e.g. 21.0). 0 for untaxed.
	 * @param bool        $sellerHasVat     Seller is VAT registered.
	 * @param bool        $reverseCharge    Domestic reverse-charge applies (B2B, same country).
	 * @param bool        $intraCommunity   Buyer is VAT-registered in another EU member state.
	 * @param bool        $export           Ship-to is outside the EU.
	 * @param bool        $sellerExempt     Seller is a VAT-exempt small business.
	 */
	public static function classify(
		float $ratePercent,
		bool $sellerHasVat,
		bool $reverseCharge,
		bool $intraCommunity,
		bool $export,
		bool $sellerExempt
	): string {
		// A taxed line is unambiguously standard-rated.
		if ( $ratePercent > 0.0 ) {
			return self::STANDARD;
		}

		// From here the rate is 0 — the *reason* it is 0 decides the category.
		if ( $sellerExempt ) {
			return self::EXEMPT;
		}
		if ( $export ) {
			return self::EXPORT;
		}
		if ( $intraCommunity ) {
			return self::INTRA_COMMUNITY;
		}
		if ( $reverseCharge ) {
			return self::REVERSE_CHARGE;
		}

		// A genuine 0% rate (zero-rated goods) vs. seller simply not charging VAT.
		return $sellerHasVat ? self::ZERO : self::OUT_OF_SCOPE;
	}
}
