<?php

declare( strict_types=1 );

namespace Brightfern\Invoice;

use FPDF;
use Brightfern\Invoice\Model\Invoice;
use Brightfern\Invoice\Model\Party;
use Brightfern\Support\Num;

/**
 * The human-readable PDF page Factur-X wraps around the embedded CII XML —
 * what a person opening the file actually sees. Built directly on FPDF
 * (already pulled in transitively by atgp/factur-x's own dependency chain,
 * so this adds no new library). Deliberately a single, functional, unstyled
 * layout for v1: seller/buyer blocks, a line table, totals, payment info —
 * not a themeable template system. Flagged to the user as a v1 scope choice;
 * easy to restyle later without touching the CII/PDF-A3 machinery.
 */
final class FacturXPdfLayout {

	public function render( Invoice $invoice ): string {
		$pdf = new FPDF( 'P', 'mm', 'A4' );
		$pdf->SetMargins( 15, 15, 15 );
		$pdf->SetAutoPageBreak( true, 20 );
		$pdf->AddPage();
		$pdf->SetTitle( ( $invoice->isCreditNote() ? 'Credit note ' : 'Invoice ' ) . $invoice->number );

		$this->header( $pdf, $invoice );
		$this->parties( $pdf, $invoice );
		$this->lines( $pdf, $invoice );
		$this->totals( $pdf, $invoice );
		$this->paymentFooter( $pdf, $invoice );

		return (string) $pdf->Output( 'S' );
	}

	private function header( FPDF $pdf, Invoice $invoice ): void {
		$pdf->SetFont( 'Helvetica', 'B', 18 );
		$pdf->Cell( 0, 10, $this->ascii( $invoice->seller->tradingName ?: $invoice->seller->registrationName ), 0, 1 );

		$pdf->SetFont( 'Helvetica', 'B', 13 );
		$title = $invoice->isCreditNote() ? 'CREDIT NOTE' : 'INVOICE';
		$pdf->Cell( 0, 8, $title . ' ' . $this->ascii( $invoice->number ), 0, 1 );

		$pdf->SetFont( 'Helvetica', '', 10 );
		$pdf->Cell( 0, 6, 'Issue date: ' . $invoice->issueDate, 0, 1 );
		if ( ! $invoice->isCreditNote() && '' !== $invoice->dueDate ) {
			$pdf->Cell( 0, 6, 'Due date: ' . $invoice->dueDate, 0, 1 );
		}
		if ( $invoice->isCreditNote() && '' !== $invoice->precedingInvoiceNumber ) {
			$pdf->Cell( 0, 6, 'Credit note for invoice: ' . $this->ascii( $invoice->precedingInvoiceNumber ), 0, 1 );
		}
		$pdf->Ln( 4 );
	}

	private function parties( FPDF $pdf, Invoice $invoice ): void {
		$startY = $pdf->GetY();
		$colW   = 90;

		$pdf->SetFont( 'Helvetica', 'B', 10 );
		$pdf->Cell( $colW, 6, 'Seller', 0, 0 );
		$pdf->Cell( $colW, 6, 'Buyer', 0, 1 );

		$sellerLines = $this->partyLines( $invoice->seller );
		$buyerLines  = $this->partyLines( $invoice->buyer );
		$rows        = max( count( $sellerLines ), count( $buyerLines ) );

		$pdf->SetFont( 'Helvetica', '', 9 );
		for ( $i = 0; $i < $rows; $i++ ) {
			$pdf->Cell( $colW, 5, $this->ascii( $sellerLines[ $i ] ?? '' ), 0, 0 );
			$pdf->Cell( $colW, 5, $this->ascii( $buyerLines[ $i ] ?? '' ), 0, 1 );
		}
		$pdf->SetY( max( $startY + 6 + 5 * $rows, $pdf->GetY() ) );
		$pdf->Ln( 4 );
	}

	/** @return string[] */
	private function partyLines( Party $party ): array {
		$lines   = array();
		$lines[] = '' !== $party->tradingName ? $party->tradingName : $party->registrationName;
		if ( $party->address ) {
			if ( '' !== $party->address->line1 ) {
				$lines[] = $party->address->line1;
			}
			if ( '' !== $party->address->line2 ) {
				$lines[] = $party->address->line2;
			}
			$cityLine = trim( $party->address->postalZone . ' ' . $party->address->city );
			if ( '' !== $cityLine ) {
				$lines[] = $cityLine;
			}
			if ( '' !== $party->address->countryCode ) {
				$lines[] = $party->address->countryCode;
			}
		}
		if ( $party->hasVat() ) {
			$lines[] = 'VAT: ' . $party->vatNumber;
		}
		if ( '' !== $party->contactEmail ) {
			$lines[] = $party->contactEmail;
		}
		return $lines;
	}

	private function lines( FPDF $pdf, Invoice $invoice ): void {
		$widths = array( 'name' => 80, 'qty' => 20, 'price' => 30, 'vat' => 20, 'total' => 30 );

		$pdf->SetFont( 'Helvetica', 'B', 9 );
		$pdf->SetFillColor( 235, 235, 235 );
		$pdf->Cell( $widths['name'], 7, 'Description', 1, 0, 'L', true );
		$pdf->Cell( $widths['qty'], 7, 'Qty', 1, 0, 'R', true );
		$pdf->Cell( $widths['price'], 7, 'Unit price', 1, 0, 'R', true );
		$pdf->Cell( $widths['vat'], 7, 'VAT %', 1, 0, 'R', true );
		$pdf->Cell( $widths['total'], 7, 'Total', 1, 1, 'R', true );

		$pdf->SetFont( 'Helvetica', '', 9 );
		foreach ( $invoice->lines as $line ) {
			$pdf->Cell( $widths['name'], 6, $this->ascii( $line->name ), 1, 0, 'L' );
			$pdf->Cell( $widths['qty'], 6, Num::quantity( $line->quantity ), 1, 0, 'R' );
			$pdf->Cell( $widths['price'], 6, Num::amount( $line->netPriceAmount ), 1, 0, 'R' );
			$pdf->Cell( $widths['vat'], 6, Num::percent( $line->vatPercent ), 1, 0, 'R' );
			$pdf->Cell( $widths['total'], 6, Num::amount( $line->lineExtensionAmount ), 1, 1, 'R' );
		}
		$pdf->Ln( 4 );
	}

	private function totals( FPDF $pdf, Invoice $invoice ): void {
		$t     = $invoice->totals;
		$cur   = $invoice->currency;
		$label = 100;
		$value = 40;

		$this->totalRow( $pdf, $label, $value, 'Total excl. VAT', $t->taxExclusiveAmount, $cur );

		// Per-rate VAT breakdown. This is not decoration: where VAT isn't charged
		// the printed invoice must say WHY (reverse charge, intra-community
		// supply, small-business exemption), and a bare "Total VAT 0.00" doesn't.
		// The exemption reason is in the XML; a human reading the PDF needs it too.
		foreach ( $invoice->taxSubtotals as $row ) {
			$rateLabel = VatCategory::OUT_OF_SCOPE === $row->categoryCode
				? 'VAT (out of scope)'
				: sprintf( 'VAT %s%% (%s)', Num::percent( $row->percent ), $row->categoryCode );
			$this->totalRow( $pdf, $label, $value, $rateLabel, $row->taxAmount, $cur );

			$reason = (string) ( $row->exemptionReasonText ?? '' );
			if ( '' !== $reason ) {
				$pdf->SetFont( 'Helvetica', 'I', 8 );
				$pdf->Cell( 0, 5, $this->ascii( '   ' . $reason ), 0, 1, 'R' );
				$pdf->SetFont( 'Helvetica', '', 9 );
			}
		}

		$this->totalRow( $pdf, $label, $value, 'Total incl. VAT', $t->taxInclusiveAmount, $cur );
		if ( $t->prepaidAmount > 0.0 ) {
			$this->totalRow( $pdf, $label, $value, 'Already paid', $t->prepaidAmount, $cur );
		}
		$pdf->SetFont( 'Helvetica', 'B', 10 );
		$this->totalRow( $pdf, $label, $value, 'Total due', $t->payableAmount, $cur );
		$pdf->SetFont( 'Helvetica', '', 9 );
		$pdf->Ln( 4 );
	}

	private function totalRow( FPDF $pdf, float $labelW, float $valueW, string $label, float $amount, string $cur ): void {
		$pdf->Cell( $labelW, 6, '', 0, 0 );
		$pdf->Cell( $valueW - 15, 6, $label, 0, 0, 'R' );
		$pdf->Cell( 15, 6, $cur, 0, 0, 'L' );
		$pdf->Cell( 0, 6, Num::amount( $amount ), 0, 1, 'R' );
	}

	private function paymentFooter( FPDF $pdf, Invoice $invoice ): void {
		$p = $invoice->payment;
		if ( '' === $p->payeeIban && '' === $p->terms ) {
			return;
		}
		$pdf->SetFont( 'Helvetica', '', 9 );
		if ( '' !== $p->payeeIban ) {
			$pdf->Cell( 0, 5, 'IBAN: ' . $p->payeeIban . ( '' !== $p->payeeBic ? '  BIC: ' . $p->payeeBic : '' ), 0, 1 );
		}
		if ( '' !== $p->terms ) {
			$pdf->Cell( 0, 5, 'Payment terms: ' . $this->ascii( $p->terms ), 0, 1 );
		}
	}

	/**
	 * FPDF's core fonts are Latin-1 (cp1252) only — no native UTF-8 support.
	 * Brightfern's model strings are UTF-8 (from WordPress/WooCommerce), so
	 * transliterate for display. This is a display-layer concern only: the
	 * embedded CII XML (what actually carries legal data) stays full UTF-8,
	 * unaffected by this conversion.
	 */
	private function ascii( string $text ): string {
		$converted = @iconv( 'UTF-8', 'CP1252//TRANSLIT', $text );
		return false !== $converted ? $converted : $text;
	}
}
