<?php

declare( strict_types=1 );

namespace Brightfern\Validation;

use Brightfern\Invoice\Model\Invoice;
use Brightfern\Invoice\VatCategory;

/**
 * The pre-flight gate — Brightfern's headline promise: "it will arrive and be
 * accepted." It chains the three independent checks into one hard gate that the
 * SEND path must pass:
 *
 *   1. {@see InvoiceValidator}  — is the document itself valid (EN 16931 /
 *      Peppol BIS / schematron)?
 *   2. {@see RecipientResolver} — is the buyer actually registered & reachable
 *      on Peppol (SMP lookup)?
 *   3. {@see PartyValidator}    — is the buyer's VAT number real and the name a
 *      plausible match (VIES)?
 *
 * Each check is ALSO usable standalone — that is exactly the free-tier feature
 * ("validate my invoice", "check my recipient", "check this VAT number"). The
 * gate is the Pro chaining of them before transmission.
 *
 * Policy (deliberately conservative so we never block on infrastructure flakiness):
 *   - Document errors            → BLOCK (hard).
 *   - Recipient definitively not registered (404) → BLOCK (configurable).
 *   - Recipient lookup indeterminate (DNS/SMP down) → WARN, allow.
 *   - VIES says INVALID          → BLOCK (it implies wrong/likely-rejected VAT).
 *   - VIES unreachable / non-EU  → INFO, allow.
 *   - VIES name mismatch         → WARN, allow.
 */
final class PreflightGate {

	public function __construct(
		private InvoiceValidator $validator,
		private ?RecipientResolver $recipientResolver = null,
		private ?PartyValidator $partyValidator = null,
	) {}

	/**
	 * @param array{checkRecipient?:bool,checkVies?:bool,requireRecipientReachable?:bool,requirePeppolAddress?:bool,requireBuyerReference?:bool,peppolBound?:bool,ksefBound?:bool} $options
	 */
	public function run( Invoice $invoice, ?string $xml = null, array $options = array() ): GateResult {
		$checkRecipient       = $options['checkRecipient'] ?? true;
		$checkVies            = $options['checkVies'] ?? true;
		$requireReach         = $options['requireRecipientReachable'] ?? true;
		$requirePeppolAddress = $options['requirePeppolAddress'] ?? true;
		$requireBuyerReference = $options['requireBuyerReference'] ?? false;
		$peppolBound           = $options['peppolBound'] ?? true;
		$ksefBound             = $options['ksefBound'] ?? false;

		// 1. Document validation (hard).
		$result = $this->validator->validate( $invoice, $xml, $requirePeppolAddress, $requireBuyerReference, $peppolBound, $ksefBound );

		// 2. SMP recipient lookup.
		$recipient = null;
		if ( $checkRecipient && $this->recipientResolver && '' !== $invoice->buyer->endpointId ) {
			$recipient = $this->recipientResolver->resolve(
				$invoice->buyer->endpointScheme,
				$invoice->buyer->endpointId
			);

			if ( $recipient->isDefinitelyUnreachable() ) {
				if ( $requireReach ) {
					$result->error( 'BRIGHTFERN-SMP-404', 'Recipient ' . $recipient->participantId . ' is not registered on the Peppol network.', 'BT-49' );
				} else {
					$result->warning( 'BRIGHTFERN-SMP-404', 'Recipient is not registered on the Peppol network; sending anyway per settings.', 'BT-49' );
				}
			} elseif ( null === $recipient->reachable ) {
				$result->warning( 'BRIGHTFERN-SMP-UNKNOWN', 'Could not confirm the recipient on Peppol (lookup unavailable): ' . $recipient->error, 'BT-49' );
			} elseif ( ! RecipientResolver::supportsBisBilling( $recipient ) && count( $recipient->supportedDocumentTypes ) > 0 ) {
				$result->warning( 'BRIGHTFERN-SMP-PROFILE', 'Recipient is registered but does not advertise Peppol BIS Billing 3.0 support.', 'BT-49' );
			}
		}

		// 3. VIES VAT + name check.
		$party = null;
		if ( $checkVies && $this->partyValidator && $invoice->buyer->hasVat() ) {
			$party             = $this->partyValidator->validateVat( $invoice->buyer->vatNumber );
			$intraCommunity     = $this->hasCategory( $invoice, VatCategory::INTRA_COMMUNITY );

			if ( $party->isInvalid() ) {
				$result->error( 'BRIGHTFERN-VIES-INVALID', 'Buyer VAT number ' . $invoice->buyer->vatNumber . ' is not valid according to VIES.', 'BT-48' );
			} elseif ( PartyValidationResult::UNKNOWN === $party->status ) {
				// Intra-community supplies legally require a verified buyer VAT, so
				// an unverifiable number there is at least a warning.
				if ( $intraCommunity ) {
					$result->warning( 'BRIGHTFERN-VIES-UNVERIFIED', 'Could not verify the buyer VAT via VIES for an intra-community supply: ' . $party->error, 'BT-48' );
				} else {
					$result->info( 'BRIGHTFERN-VIES-UNKNOWN', 'Buyer VAT not verified via VIES: ' . $party->error, 'BT-48' );
				}
			} elseif ( $party->isValid() && '' !== $party->name
				&& ! PartyValidator::namesPlausiblyMatch( $party->name, $invoice->buyer->registrationName ) ) {
				$result->warning(
					'BRIGHTFERN-VIES-NAME',
					sprintf( 'Buyer name on the order ("%s") differs from the VIES-registered name ("%s").', $invoice->buyer->registrationName, $party->name ),
					'BT-44'
				);
			}
		}

		return new GateResult( $result->isValid(), $result, $recipient, $party );
	}

	private function hasCategory( Invoice $invoice, string $category ): bool {
		foreach ( $invoice->taxSubtotals as $row ) {
			if ( $row->categoryCode === $category ) {
				return true;
			}
		}
		return false;
	}
}
