<?php
/**
 * QR code meta box for vehicle listings.
 *
 * Renders a side meta box on the listing edit screen showing a QR code
 * that resolves to the public listing permalink. The QR is generated
 * server-side as an SVG (no rasterisation, no GD dependency) using
 * chillerlan/php-qrcode and cached against the post's modified time so
 * the editor screen never re-encodes on a simple refresh.
 *
 * A small download link below the preview returns the same SVG.
 *
 * @package ThemeREX\AutoDealer
 * @version 1.0.0
 */

declare(strict_types=1);

namespace ThemeREX\AutoDealer\Admin;

defined( 'ABSPATH' ) || exit;
use ThemeREX\AutoDealer\Vendor\chillerlan\QRCode\QRCode;
use ThemeREX\AutoDealer\Vendor\chillerlan\QRCode\QROptions;
use ThemeREX\AutoDealer\PostTypes\VehiclePostType;

/**
 * Side-column meta box that previews a permalink QR code on the edit screen.
 */
class QRCodeMetaBox {

	/**
	 * Cache key prefix for rendered SVGs (per post + modified-time).
	 */
	private const CACHE_PREFIX = 'trx_ad_qr_';

	/**
	 * Register hooks.
	 *
	 * @return void
	 */
	public function register(): void {
		add_action( 'add_meta_boxes_' . VehiclePostType::POST_TYPE, array( $this, 'addMetaBox' ) );
		add_action( 'admin_post_trx_ad_download_qr', array( $this, 'handleDownload' ) );
	}

	/**
	 * Register the side meta box on the listing edit screen.
	 *
	 * @return void
	 */
	public function addMetaBox(): void {
		add_meta_box(
			'trx_ad_qr_code',
			__( 'Listing QR Code', 'trx-auto-dealer' ),
			array( $this, 'render' ),
			VehiclePostType::POST_TYPE,
			'side',
			'low'
		);
	}

	/**
	 * Render the meta box body.
	 *
	 * @param \WP_Post $post Current post.
	 * @return void
	 */
	public function render( \WP_Post $post ): void {
		// Auto-drafts and brand-new posts have no permalink to encode yet.
		if ( 'auto-draft' === $post->post_status || '' === $post->post_name ) {
			echo '<p>' . esc_html__( 'Save the listing to generate a QR code.', 'trx-auto-dealer' ) . '</p>';
			return;
		}

		$svg = $this->getCachedSvg( $post );
		if ( '' === $svg ) {
			echo '<p>' . esc_html__( 'QR code unavailable.', 'trx-auto-dealer' ) . '</p>';
			return;
		}

		$download_url = wp_nonce_url(
			admin_url( 'admin-post.php?action=trx_ad_download_qr&post=' . $post->ID ),
			'trx_ad_download_qr_' . $post->ID
		);

		printf(
			'<div class="trx-ad-qr-preview" style="text-align:center;padding:8px 0;">%s</div>',
			wp_kses( $svg, \ThemeREX\AutoDealer\allowedSvgTags() )
		);

		printf(
			'<p class="trx-ad-qr-actions" style="text-align:center;margin:0;"><a class="button" href="%1$s">%2$s</a></p>',
			esc_url( $download_url ),
			esc_html__( 'Download SVG', 'trx-auto-dealer' )
		);
	}

	/**
	 * Cached SVG accessor.
	 *
	 * Cache key is keyed on the post's modified timestamp so editing the
	 * permalink (which would change the encoded URL) busts the cache
	 * automatically without any explicit invalidation hook.
	 *
	 * @param \WP_Post $post Current post.
	 * @return string SVG markup, or '' on failure.
	 */
	private function getCachedSvg( \WP_Post $post ): string {
		$key = self::CACHE_PREFIX . $post->ID . '_' . strtotime( $post->post_modified_gmt );

		$cached = get_transient( $key );
		if ( is_string( $cached ) && '' !== $cached ) {
			return $cached;
		}

		$svg = $this->buildSvg( get_permalink( $post ) ?: '' );
		if ( '' === $svg ) {
			return '';
		}

		set_transient( $key, $svg, DAY_IN_SECONDS );
		return $svg;
	}

	/**
	 * Encode a URL into a self-contained SVG QR code.
	 *
	 * @param string $url URL to encode.
	 * @return string SVG markup, or '' on failure.
	 */
	private function buildSvg( string $url ): string {
		if ( '' === $url ) {
			return '';
		}

		try {
			$options = new QROptions(
				array(
					'outputType'     => QRCode::OUTPUT_MARKUP_SVG,
					'eccLevel'       => QRCode::ECC_M,
					'scale'          => 4,
					'imageBase64'    => false,
					'addQuietzone'   => true,
					'quietzoneSize'  => 2,
					'svgViewBoxSize' => 0,
				)
			);

			$svg = ( new QRCode( $options ) )->render( $url );
			return is_string( $svg ) ? $svg : '';
		} catch ( \Throwable $e ) {
			return '';
		}
	}

	/**
	 * Handle GET ?action=trx_ad_download_qr&post=ID — stream the SVG as a download.
	 *
	 * @return void
	 */
	public function handleDownload(): void {
		$post_id = isset( $_GET['post'] ) ? (int) $_GET['post'] : 0;
		if ( $post_id <= 0 ) {
			wp_die( esc_html__( 'Invalid request.', 'trx-auto-dealer' ), 400 );
		}

		check_admin_referer( 'trx_ad_download_qr_' . $post_id );

		if ( ! current_user_can( 'edit_post', $post_id ) ) {
			wp_die( esc_html__( 'You are not allowed to download this QR code.', 'trx-auto-dealer' ), 403 );
		}

		$post = get_post( $post_id );
		if ( null === $post || VehiclePostType::POST_TYPE !== $post->post_type ) {
			wp_die( esc_html__( 'Listing not found.', 'trx-auto-dealer' ), 404 );
		}

		$svg = $this->getCachedSvg( $post );
		if ( '' === $svg ) {
			wp_die( esc_html__( 'QR code unavailable.', 'trx-auto-dealer' ), 500 );
		}

		nocache_headers();
		header( 'Content-Type: image/svg+xml; charset=utf-8' );
		header( 'Content-Disposition: attachment; filename="qr-' . $post->post_name . '.svg"' );
		// Generated-file download stream (image/svg+xml attachment built by the
		// bundled QR library from the listing permalink) -- not an HTML context,
		// so HTML escaping functions do not apply here.
		echo $svg; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		exit;
	}
}
