<?php

declare( strict_types=1 );

namespace Brightfern\Invoice;

use Brightfern\Invoice\Model\Invoice;

/**
 * France — Factur-X (a.k.a. ZUGFeRD in Germany): a HYBRID invoice that embeds a
 * structured EN 16931 XML inside a human-readable PDF/A-3 file. Used for the
 * French domestic flow (PPF / PDPs) rather than Peppol transit.
 *
 * Two things that made this heavier than a format switch (unlike XRechnung):
 *
 *  1. The embedded XML is UN/CEFACT **CII**, NOT UBL — {@see CiiSerializer},
 *     not {@see UblBisSerializer}. The internal {@see Invoice} model is
 *     syntax-neutral precisely so this could be added without touching the
 *     mapper. CiiSerializer's structure was verified against the official
 *     bundled EN16931-profile XSD (vendor/atgp/factur-x/xsd/), not assumed.
 *  2. The container is **PDF/A-3** with the XML attached as an AF (Associated
 *     File) relationship and Factur-X XMP metadata — handled here by
 *     `atgp/factur-x`'s `Writer`, which also re-validates the CII XML against
 *     that same official XSD at embed time (a second, independent check on
 *     top of the source-level validation this plugin already runs).
 *
 * {@see FacturXPdfLayout} builds the human-readable page the XML gets
 * embedded into.
 */
final class FacturXBuilder {

	public const PROFILE_EN16931 = CiiSerializer::GUIDELINE_EN16931;

	/**
	 * Build the Factur-X deliverable for an invoice: the CII XML and the
	 * PDF/A-3 hybrid it's embedded in.
	 *
	 * @return array{xml:string,pdf:string,profile:string,embedded:bool}
	 */
	public function build( Invoice $invoice ): array {
		$xml       = ( new CiiSerializer() )->serialize( $invoice );
		$layoutPdf = ( new FacturXPdfLayout() )->render( $invoice );
		$pdf       = ( new \Atgp\FacturX\Writer() )->generate( $layoutPdf, $xml );

		return array(
			'xml'      => $xml,
			'pdf'      => $pdf,
			'profile'  => self::PROFILE_EN16931,
			'embedded' => true,
		);
	}

	/** Whether full PDF/A-3 embedding is available in this build. */
	public function supportsPdfEmbedding(): bool {
		return true;
	}
}
