<?php
declare(strict_types=1);

namespace Wohno\Plugin\Block;

use Wohno\Plugin\Api\Client;
use Wohno\Plugin\Api\ListingQuery;
use Wohno\Plugin\Render\ListingsRenderer;

use const Wohno\Plugin\VERSION;

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

/**
 * Dynamic (server-rendered) Gutenberg block `wohno/listings`.
 *
 * Renders via the shared {@see ListingsRenderer}. The editor preview uses
 * `wp.serverSideRender` so no build step is required (buildless block).
 */
final class ListingsBlock {

	/**
	 * Constructor.
	 *
	 * @param Client           $client   API client.
	 * @param ListingsRenderer $renderer HTML renderer.
	 */
	public function __construct(
		private readonly Client $client,
		private readonly ListingsRenderer $renderer
	) {
	}

	/**
	 * Hook block registration into `init`.
	 *
	 * @return void
	 */
	public function register(): void {
		add_action( 'init', array( $this, 'register_block' ) );
	}

	/**
	 * Register the editor script and the dynamic block type.
	 *
	 * @return void
	 */
	public function register_block(): void {
		if ( ! function_exists( 'register_block_type' ) ) {
			return;
		}

		wp_register_script(
			'wohno-block-editor',
			WOHNO_PLUGIN_URL . 'assets/js/block-editor.js',
			array( 'wp-blocks', 'wp-element', 'wp-block-editor', 'wp-components', 'wp-server-side-render', 'wp-i18n' ),
			VERSION,
			true
		);
		wp_set_script_translations( 'wohno-block-editor', 'wohno', WOHNO_PLUGIN_DIR . 'languages' );

		register_block_type(
			__DIR__ . '/block.json',
			array(
				'render_callback' => array( $this, 'render' ),
			)
		);
	}

	/**
	 * Server-side render callback for the block.
	 *
	 * @param array<string, mixed> $attributes Block attributes.
	 * @return string Escaped HTML.
	 */
	public function render( array $attributes ): string {
		$search_on = ! empty( $attributes['search'] );

		// Keep the author-defined (static) attributes separate from the visitor's
		// submitted selection: only the static view may seed the prewarm registry.
		$base_attributes = $attributes;

		// With the filter bar on, the visitor's submitted selection overrides
		// the block defaults (empty field = cleared).
		if ( $search_on ) {
			$attributes = array_merge( $attributes, ListingQuery::request_values() );
		}

		$params = ListingQuery::from_atts( $attributes );
		// Visitor-driven ($_GET) queries must not poison the prewarm registry;
		// seed it from the author's static view instead (see prewarm_register).
		$result = $this->client->listings( $params, ! $search_on );
		if ( $search_on ) {
			$this->client->prewarm_register( ListingQuery::from_atts( $base_attributes ) );
		}
		if ( is_wp_error( $result ) ) {
			return current_user_can( 'edit_posts' )
				? '<p class="wohno-error">' . esc_html( $result->get_error_message() ) . '</p>'
				: '';
		}

		$opts = array(
			'columns'    => isset( $attributes['columns'] ) ? (int) $attributes['columns'] : 3,
			'layout'     => ListingsRenderer::sanitize_layout( $attributes['layout'] ?? 'grid' ),
			'query'      => $params,
			'pagination' => $result['pagination'],
			'load_more'  => ! isset( $attributes['load_more'] ) || false !== $attributes['load_more'],
		);

		$results = isset( $attributes['layout'] ) && 'map' === $attributes['layout']
			? $this->renderer->map( $result['listings'], $opts )
			: $this->renderer->grid( $result['listings'], $opts );

		if ( ! $search_on ) {
			return $results;
		}

		$action = get_permalink();
		$form   = $this->renderer->search_form(
			$attributes,
			array(
				'action'    => is_string( $action ) ? $action : home_url( '/' ),
				'preserved' => ListingQuery::preserved_query(),
			)
		);

		return $form . $results;
	}
}
