<?php
/**
 * File Path: includes/admin/class-mbs-layout-preview.php
 *
 * Layout Preview — My Book Showroom
 *
 * Handles the AJAX-driven live layout preview rendered inside an
 * <iframe srcdoc> on the Settings → Formatting tab.
 *
 * Flow:
 *   1. Admin changes the Default Layout dropdown (or thumbnail).
 *   2. child-admin-formatting.js POSTs action=mbs_layout_preview.
 *   3. This handler fetches up to 4 published books, renders via the
 *      requested layout class, wraps in an HTML document, and returns JSON.
 *   4. JS sets `iframe.srcdoc = data.html` — srcdoc isolation prevents XSS
 *      from affecting the parent admin document.
 *
 * PRO layout on FREE tier:
 *   Returns a visible overlay inside the iframe (NOT on the main admin page).
 *   This is an explicit exemption from SEC-02 (HTML comment only, no visible
 *   element) which applies to front-end output — admin preview panels are
 *   permitted to show discoverability information in iframe context.
 *
 * Orchestrator wiring (class-mbs-plugin.php, inside is_admin() block):
 *   require_once MBS_INCLUDES_DIR . 'admin/class-mbs-layout-preview.php';
 *   new MBS_Layout_Preview();
 *
 * Story 14-7 — Sprint 14.
 *
 * @package    My_Book_Showroom
 * @subpackage Includes/Admin
 * @since      1.0.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class MBS_Layout_Preview { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- mbs_/MBS_ prefix is correct; scanner not configured with plugin prefix

	/** PRO-only layout slugs (confirmed from class-mbs-layout-registry.php). */
	const PRO_SLUGS = array( 'slider', 'flip', 'filter', 'popup' );

	/** Number of books to render in the preview. */
	const PREVIEW_BOOK_LIMIT = 4;

	// ── Bootstrap ─────────────────────────────────────────────────────────────

	public function __construct() {
		add_action( 'wp_ajax_mbs_layout_preview',  array( $this, 'handle' ) );
		add_action( 'admin_enqueue_scripts',        array( $this, 'localize_nonce' ) );
	}

	// ── Nonce localisation ────────────────────────────────────────────────────

	/**
	 * Attach the layout preview nonce to the formatting tab script.
	 *
	 * The handle `mbs-child-admin-formatting` is registered by
	 * MBS_Settings::enqueue_assets() when $current_tab === 'formatting'.
	 * wp_localize_script() silently no-ops if the handle is not registered yet,
	 * so this is safe to call on every admin page.
	 *
	 * @param string $hook Current admin page hook.
	 */
	public function localize_nonce( string $hook ): void {
		wp_localize_script(
			'mbs-admin-formatting',
			'MBS_LayoutPreview',
			array(
				'nonce'   => wp_create_nonce( 'mbs_layout_preview' ),
				'ajaxurl' => admin_url( 'admin-ajax.php' ),
			)
		);
	}

	// ── AJAX handler ──────────────────────────────────────────────────────────

	/**
	 * Handle the mbs_layout_preview AJAX action.
	 *
	 * Returns JSON: { success: true, data: { html: string } }
	 *
	 * @return void  Sends JSON and exits.
	 */
	public function handle(): void {
		check_ajax_referer( 'mbs_layout_preview', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => 'Permission denied.' ), 403 );
		}

		$slug = sanitize_key( wp_unslash( $_POST['layout'] ?? 'grid' ) );

		// PRO layout on FREE tier — return discoverability overlay (iframe only).
		if ( in_array( $slug, self::PRO_SLUGS, true ) && ! mbs_is_pro() ) {
			wp_send_json_success( array( 'html' => $this->pro_overlay_html( $slug ) ) );
		}

		// Fetch books for preview.
		$query = new WP_Query( array(
			'post_type'      => MBS_POST_TYPE,
			'posts_per_page' => self::PREVIEW_BOOK_LIMIT,
			'post_status'    => 'publish',
			'orderby'        => 'date',
			'order'          => 'DESC',
			'no_found_rows'  => true, // Skip COUNT(*) — not paginating.
		) );

		if ( ! $query->have_posts() ) {
			$html = '<p style="padding:40px;text-align:center;font-family:sans-serif;color:#646970;font-size:14px;">'
			      . esc_html__( 'No books found. Add some books to preview this layout.', 'my-book-showroom' )
			      . '</p>';
			wp_send_json_success( array( 'html' => $this->wrap_document( $html ) ) );
		}

		// Render via the layout class.
		$layout = MBS_Layout_Registry::get_layout( $slug );
		$body   = $layout->render( $query );

		wp_send_json_success( array( 'html' => $this->wrap_document( $body ) ) );
	}

	// ── Private helpers ───────────────────────────────────────────────────────

	/**
	 * Wrap rendered layout HTML in a minimal HTML5 document that loads the
	 * MBS front-end stylesheet for an accurate in-admin preview.
	 *
	 * The wrapped document is sent raw (not htmlspecialchars'd) because
	 * child-admin-formatting.js sets `iframe.srcdoc` via DOM property
	 * assignment, which takes the raw HTML string directly — the browser
	 * does not apply an extra round of entity decoding for property assignment.
	 *
	 * @param string $body Rendered layout HTML.
	 * @return string Full HTML document.
	 */
	private function wrap_document( string $body ): string {
		$front_css = MBS_PLUGIN_URL . 'assets/css/mbs-front.css';
		return '<!DOCTYPE html>'
		     . '<html lang="en">'
		     . '<head>'
		     . '<meta charset="UTF-8">'
		     . '<meta name="viewport" content="width=device-width,initial-scale=1">'
		     . '<link rel="stylesheet" href="' . esc_url( $front_css ) . '">' // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- inline style tag required for layout-preview functionality; cannot be enqueued via wp_enqueue_style
		     . '<style>body{margin:0;padding:8px;font-size:14px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;}'
		     . 'img{max-width:100%;height:auto;}'
		     . '</style>'
		     . '</head>'
		     . '<body>'
		     . $body
		     . '</body>'
		     . '</html>';
	}

	/**
	 * HTML for the PRO-gated overlay shown inside the preview iframe.
	 *
	 * Includes the required HTML comment plus a visible panel for discoverability.
	 * This visible element is acceptable inside a preview iframe (admin context)
	 * per the SEC-02 exemption noted in the class docblock.
	 *
	 * @param string $slug The requested PRO layout slug.
	 * @return string Wrapped HTML document.
	 */
	private function pro_overlay_html( string $slug ): string {
		$slug_label = ucfirst( esc_html( $slug ) );
		$body = '<!-- MBS: ' . esc_html( $slug ) . ' requires PRO license -->'
		      . '<div style="display:flex;align-items:center;justify-content:center;height:200px;">'
		      . '<div style="text-align:center;padding:20px;border:2px dashed #c3c4c7;border-radius:6px;background:#f6f7f7;min-width:260px;">'
		      . '<div style="font-size:36px;margin-bottom:8px;">🔒</div>'
		      . '<strong style="display:block;font-size:14px;color:#1d2327;margin-bottom:6px;">' . esc_html__( 'PRO License Required', 'my-book-showroom' ) . '</strong>'
		      . '<span style="font-size:12px;color:#646970;">The <strong>' . $slug_label . '</strong> layout requires a PRO license.</span>'
		      . '</div>'
		      . '</div>';
		return $this->wrap_document( $body );
	}
}
