<?php
/**
 * Renders a printable product overview (checkout catalog) as A4 PDF.
 *
 * @package PhoenixWP\PriceLabels
 */

declare(strict_types=1);

namespace PhoenixWP\PriceLabels\Pdf;

use Dompdf\Dompdf;
use Dompdf\Options;

defined( 'ABSPATH' ) || exit;

/**
 * Builds a scannable product list for deposit at the checkout.
 */
final class Product_Overview_Renderer {

	/**
	 * @var array<string, bool>
	 */
	private array $visible_fields = array();

	/**
	 * @param array<int, array<string, mixed>> $labels  Label data rows.
	 * @param array<string, mixed>             $options Optional render options.
	 */
	public function stream( array $labels, string $filename = 'price-labels-overview.pdf', array $options = array() ): void {
		if ( ! class_exists( Dompdf::class ) || ! class_exists( \Dompdf\Cpdf::class ) ) {
			wp_die(
				esc_html__( 'PDF library incomplete. Re-run scripts/install-deps.ps1 or deploy a build ZIP with vendor/.', 'phoenix-price-labels-for-woocommerce' ),
				esc_html__( 'PDF Error', 'phoenix-price-labels-for-woocommerce' ),
				array( 'response' => 500 )
			);
		}

		$html = $this->build_html( $labels, $options );

		$options = new Options();
		$options->set( 'isRemoteEnabled', false );
		$options->set( 'defaultFont', 'DejaVu Sans' );

		$dompdf = new Dompdf( $options );
		$dompdf->loadHtml( $html );
		$dompdf->setPaper( 'A4', 'portrait' );
		$dompdf->render();

		$dompdf->stream( sanitize_file_name( $filename ), array( 'Attachment' => true ) );
		exit;
	}

	/**
	 * @param array<int, array<string, mixed>> $labels  Label data rows.
	 * @param array<string, mixed>             $options Render options.
	 */
	private function build_html( array $labels, array $options = array() ): string {
		$this->visible_fields = isset( $options['visible_fields'] ) && is_array( $options['visible_fields'] )
			? $options['visible_fields']
			: array();

		$columns = $this->resolve_columns( $labels, $options );
		$css     = $this->build_css();
		$header  = $this->render_document_header();
		$table   = $this->render_table( $labels, $columns );

		$this->visible_fields = array();

		return '<!DOCTYPE html><html><head><meta charset="utf-8"><style>' . $css . '</style></head><body>'
			. $header
			. $table
			. '</body></html>'; // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet
	}

	private function render_document_header(): string {
		$shop_name = get_bloginfo( 'name' );
		$date      = wp_date( get_option( 'date_format' ) );

		$html  = '<div class="doc-header">';
		$html .= '<h1>' . esc_html__( 'Product overview', 'phoenix-price-labels-for-woocommerce' ) . '</h1>';

		if ( '' !== $shop_name ) {
			$html .= '<p class="doc-meta">' . esc_html( $shop_name );
			if ( '' !== $date ) {
				$html .= ' · ' . esc_html( $date );
			}
			$html .= '</p>';
		}

		$html .= '</div>';

		return $html;
	}

	/**
	 * @param array<int, array<string, mixed>> $labels  Label data rows.
	 * @param array<int, array<string, string>> $columns Column definitions.
	 */
	private function render_table( array $labels, array $columns ): string {
		$html = '<table><thead><tr>';

		foreach ( $columns as $column ) {
			$html .= '<th scope="col">' . esc_html( (string) $column['label'] ) . '</th>';
		}

		$html .= '</tr></thead><tbody>';

		foreach ( $labels as $index => $label ) {
			$html .= '<tr>';

			foreach ( $columns as $column ) {
				$html .= '<td>' . $this->render_cell( (string) $column['key'], $index, $label ) . '</td>';
			}

			$html .= '</tr>';
		}

		$html .= '</tbody></table>';

		return $html;
	}

	/**
	 * @param array<string, mixed> $label Label row.
	 */
	private function render_cell( string $key, int $index, array $label ): string {
		switch ( $key ) {
			case 'index':
				return esc_html( (string) ( $index + 1 ) );
			case 'brand':
				return esc_html( (string) ( $label['brand'] ?? '' ) );
			case 'name':
				return esc_html( (string) ( $label['name'] ?? '' ) );
			case 'variation':
				return esc_html( (string) ( $label['variation_text'] ?? '' ) );
			case 'price':
				return $this->render_price_cell( $label );
			case 'unit_price':
				return esc_html( (string) ( $label['unit_price'] ?? '' ) );
			case 'stock':
				return esc_html( (string) ( $label['stock'] ?? '' ) );
			case 'sku':
				return esc_html( (string) ( $label['sku'] ?? '' ) );
			case 'ean':
				return $this->render_ean_cell( $label );
			default:
				/**
				 * Filters overview table cell HTML for custom columns.
				 *
				 * @param string               $html  Cell HTML.
				 * @param string               $key   Column key.
				 * @param array<string, mixed> $label Label row.
				 */
				return (string) apply_filters( 'phoenix_wp_price_labels_overview_cell', '', $key, $label );
		}
	}

	/**
	 * @param array<string, mixed> $label Label row.
	 */
	private function render_price_cell( array $label ): string {
		$on_sale = ! empty( $label['on_sale'] );
		$regular = (string) ( $label['regular_price'] ?? '' );
		$price   = (string) ( $label['price'] ?? '' );

		if ( $on_sale && '' !== $regular ) {
			return '<span class="regular">' . esc_html( $regular ) . '</span> '
				. '<span class="sale">' . esc_html( $price ) . '</span>';
		}

		return esc_html( $price );
	}

	/**
	 * @param array<string, mixed> $label Label row.
	 */
	private function render_ean_cell( array $label ): string {
		$ean          = (string) ( $label['ean'] ?? '' );
		$barcode_html = (string) ( $label['barcode_html'] ?? '' );

		if ( '' === $ean && '' === $barcode_html ) {
			return '';
		}

		$visible = $this->visible_fields;
		$show_ean     = array() === $visible || ! empty( $visible['ean'] );
		$show_barcode = array() === $visible || ! empty( $visible['barcode'] );

		if ( array() !== $visible && empty( $visible['ean'] ) && empty( $visible['barcode'] ) ) {
			return '';
		}

		$html = '';

		if ( $show_ean && '' !== $ean ) {
			$html .= '<span class="ean-digits">' . esc_html( $ean ) . '</span>';
		}

		return $html;
	}

	/**
	 * @param array<int, array<string, mixed>> $labels  Label data rows.
	 * @param array<string, mixed>             $options Render options.
	 *
	 * @return array<int, array<string, string>>
	 */
	private function resolve_columns( array $labels, array $options = array() ): array {
		$visible = isset( $options['visible_fields'] ) && is_array( $options['visible_fields'] )
			? $options['visible_fields']
			: array();

		$columns = array(
			array(
				'key'   => 'index',
				'label' => '#',
			),
			array(
				'key'   => 'brand',
				'label' => __( 'Brand', 'phoenix-price-labels-for-woocommerce' ),
			),
			array(
				'key'   => 'name',
				'label' => __( 'Product', 'phoenix-price-labels-for-woocommerce' ),
			),
			array(
				'key'   => 'variation',
				'label' => __( 'Variation', 'phoenix-price-labels-for-woocommerce' ),
			),
			array(
				'key'   => 'price',
				'label' => __( 'Price', 'phoenix-price-labels-for-woocommerce' ),
			),
			array(
				'key'   => 'unit_price',
				'label' => __( 'Unit price', 'phoenix-price-labels-for-woocommerce' ),
			),
			array(
				'key'   => 'stock',
				'label' => __( 'Stock', 'phoenix-price-labels-for-woocommerce' ),
			),
			array(
				'key'   => 'sku',
				'label' => __( 'SKU', 'phoenix-price-labels-for-woocommerce' ),
			),
			array(
				'key'   => 'ean',
				'label' => __( 'EAN / GTIN', 'phoenix-price-labels-for-woocommerce' ),
			),
		);

		if ( array() !== $visible ) {
			$map = array(
				'brand'      => 'brand',
				'variation'  => 'variation',
				'unit_price' => 'unit_price',
				'stock'      => 'stock',
				'sku'        => 'sku',
				'ean'        => 'ean',
			);

			$columns = array_values(
				array_filter(
					$columns,
					static function ( array $column ) use ( $map, $visible ): bool {
						$key = $column['key'];

						if ( in_array( $key, array( 'index', 'name', 'price' ), true ) ) {
							return true;
						}

						if ( 'ean' === $key ) {
							return ! empty( $visible['ean'] ) || ! empty( $visible['barcode'] );
						}

						if ( ! isset( $map[ $key ] ) ) {
							return true;
						}

						return ! empty( $visible[ $map[ $key ] ] );
					}
				)
			);
		}

		/**
		 * Filters columns shown on the checkout product overview PDF.
		 *
		 * @param array<int, array<string, string>> $columns Column key + label pairs.
		 * @param array<int, array<string, mixed>>  $labels  Label data rows.
		 */
		return (array) apply_filters( 'phoenix_wp_price_labels_overview_columns', $columns, $labels );
	}

	private function build_css(): string {
		$barcode_w = 0.0;
		$barcode_h = 0.0;

		return 'body { margin: 12mm 14mm; font-family: DejaVu Sans, sans-serif; font-size: 9pt; color: #111; }'
			. '.doc-header { margin-bottom: 6mm; }'
			. '.doc-header h1 { font-size: 16pt; margin: 0 0 2mm; }'
			. '.doc-meta { margin: 0 0 2mm; color: #444; font-size: 10pt; }'
			. 'table { width: 100%; border-collapse: collapse; table-layout: fixed; }'
			. 'thead { display: table-header-group; }'
			. 'th, td { border: 0.2mm solid #bbb; padding: 2mm 2.5mm; text-align: left; vertical-align: top; overflow: hidden; word-wrap: break-word; }'
			. 'th { background: #f2f2f2; font-weight: bold; font-size: 8.5pt; }'
			. 'tr { page-break-inside: avoid; }'
			. '.regular { text-decoration: line-through; color: #666; margin-right: 1.5mm; }'
			. '.sale { font-weight: bold; }'
			. '.ean-digits { display: block; font-size: 8pt; color: #333; }'
			. '.phoenix-price-labels-for-woocommerce-barcode-frame--fit { width: 100%; max-width: ' . $barcode_w . 'mm; height: auto; min-width: 0; min-height: 0; max-height: none; border-collapse: collapse; table-layout: fixed; margin-top: 1mm; }'
			. '.phoenix-price-labels-for-woocommerce-barcode-frame--fit .phoenix-price-labels-for-woocommerce-barcode-frame__cell { width: 100%; height: auto; min-width: 0; min-height: 0; max-width: 100%; max-height: ' . $barcode_h . 'mm; padding: 0; margin: 0; overflow: hidden; text-align: center; vertical-align: middle; line-height: 0; }'
			. '.phoenix-price-labels-for-woocommerce-barcode-frame--fit .phoenix-price-labels-for-woocommerce-barcode__img { display: block; width: 100%; max-width: 100%; height: auto; max-height: ' . $barcode_h . 'mm; min-width: 0; min-height: 0; margin: 0 auto; padding: 0; border: 0; }';
	}
}
