<?php
/**
 * SEOvault FAQ Gutenberg block registration and static markup.
 *
 * Phase 26–28: contract + sanitizer + fallback-visible markup + FAQPage collection.
 * FAQPage JSON-LD is emitted by SchemaManager via FaqBlockCollector (not by render).
 *
 * @package SEOvaultSEO\Blocks
 */

namespace SEOvaultSEO\Blocks;

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

/**
 * Registers and renders the seovault/faq block.
 */
class FaqBlock {

	const BLOCK_NAME = 'seovault/faq';

	/**
	 * Register the block type.
	 *
	 * @return bool Whether registration succeeded.
	 */
	public static function register() {
		if ( ! function_exists( 'register_block_type' ) ) {
			return false;
		}

		$built_dir = trailingslashit( SEOVAULT_SEO_DIR ) . 'build/blocks/faq';
		$args      = array(
			'api_version'     => 2,
			'attributes'      => FaqBlockAttributeSanitizer::attribute_schema(),
			'render_callback' => array( __CLASS__, 'render' ),
		);

		if ( file_exists( $built_dir . '/block.json' ) ) {
			$result = register_block_type( $built_dir, $args );
			return $result instanceof \WP_Block_Type;
		}

		$source_json = trailingslashit( SEOVAULT_SEO_DIR ) . 'blocks/faq/src/block.json';
		if ( ! file_exists( $source_json ) ) {
			$source_json = trailingslashit( SEOVAULT_SEO_DIR ) . 'blocks/faq/block.json';
		}
		if ( file_exists( $source_json ) ) {
			$metadata = json_decode( (string) file_get_contents( $source_json ), true ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Local plugin file.
			if ( is_array( $metadata ) ) {
				$args = array_merge(
					array(
						'title'       => isset( $metadata['title'] ) ? (string) $metadata['title'] : 'FAQ',
						'description' => isset( $metadata['description'] ) ? (string) $metadata['description'] : '',
						'category'    => isset( $metadata['category'] ) ? (string) $metadata['category'] : 'text',
						'icon'        => isset( $metadata['icon'] ) ? (string) $metadata['icon'] : 'editor-help',
						'keywords'    => isset( $metadata['keywords'] ) && is_array( $metadata['keywords'] ) ? $metadata['keywords'] : array(),
						'supports'    => isset( $metadata['supports'] ) && is_array( $metadata['supports'] ) ? $metadata['supports'] : array(),
					),
					$args
				);
			}
		}

		$result = register_block_type( self::BLOCK_NAME, $args );
		return $result instanceof \WP_Block_Type;
	}

	/**
	 * Server render callback (active plugin path).
	 *
	 * Always re-sanitizes attributes. Does not emit FAQPage JSON-LD.
	 *
	 * @param array         $attributes Block attributes.
	 * @param string        $content    Saved inner content (fallback).
	 * @param \WP_Block|null $block     Block instance.
	 * @return string
	 */
	public static function render( $attributes, $content = '', $block = null ) {
		unset( $block );
		$sanitized = FaqBlockAttributeSanitizer::sanitize( is_array( $attributes ) ? $attributes : array() );
		$html      = self::build_static_markup( $sanitized['attributes'] );

		/**
		 * Filter rendered FAQ block HTML (no schema emission).
		 *
		 * The value returned from this filter is passed through wp_kses_post()
		 * immediately before output. Filtered HTML is restricted to WordPress
		 * post-content markup. Script, style, iframe, and event-handler markup
		 * are not supported.
		 *
		 * @param string $html       Markup.
		 * @param array  $attributes Sanitized attributes.
		 * @param string $content    Original saved content.
		 */
		$html = (string) apply_filters( 'seovaultseo_faq_block_render_html', $html, $sanitized['attributes'], (string) $content );

		return wp_kses_post( $html );
	}

	/**
	 * Build static/fallback-visible markup used by save and render.
	 *
	 * Markup must match blocks/faq/src/save.js (+ utils/serialize.js) so
	 * Gutenberg block validation accepts converter/server-written content
	 * without "Attempt Recovery".
	 *
	 * @param array $attributes Sanitized attributes.
	 * @return string
	 */
	public static function build_static_markup( array $attributes ) {
		$heading      = isset( $attributes['heading'] ) ? (string) $attributes['heading'] : '';
		$intro        = isset( $attributes['intro'] ) ? (string) $attributes['intro'] : '';
		$items        = isset( $attributes['items'] ) && is_array( $attributes['items'] ) ? $attributes['items'] : array();
		$display_mode = isset( $attributes['displayMode'] ) ? (string) $attributes['displayMode'] : 'accordion';
		$style        = isset( $attributes['stylePreset'] ) ? (string) $attributes['stylePreset'] : 'accordion-cards';
		$color        = isset( $attributes['colorPreset'] ) ? (string) $attributes['colorPreset'] : 'blue';
		$density      = isset( $attributes['density'] ) ? (string) $attributes['density'] : 'comfortable';
		$open_first   = ! empty( $attributes['openFirstItem'] );
		$show_numbers = ! empty( $attributes['showQuestionNumbers'] );
		$sharp        = ! empty( $attributes['sharpCorners'] );
		$version      = isset( $attributes['schemaVersion'] ) ? (int) $attributes['schemaVersion'] : 1;

		$classes = array(
			'wp-block-seovault-faq',
			'seovault-faq-block',
			'seovault-faq-block--' . sanitize_html_class( $display_mode ),
			'seovault-faq-block--style-' . sanitize_html_class( $style ),
			'seovault-faq-block--color-' . sanitize_html_class( $color ),
			'seovault-faq-block--density-' . sanitize_html_class( $density ),
		);
		if ( $sharp ) {
			$classes[] = 'seovault-faq-block--sharp';
		}

		$index     = 0;
		$items_html = '';
		foreach ( $items as $item ) {
			if ( ! is_array( $item ) ) {
				continue;
			}
			$question = isset( $item['question'] ) ? trim( (string) $item['question'] ) : '';
			$answer   = isset( $item['answer'] ) ? (string) $item['answer'] : '';
			$plain    = isset( $item['answer_plain'] ) ? (string) $item['answer_plain'] : FaqBlockAttributeSanitizer::normalize_plain_answer( $answer );
			if ( '' === $question && '' === $plain ) {
				continue;
			}
			++$index;
			$label      = $show_numbers ? ( (string) $index . '. ' . $question ) : $question;
			$item_id    = isset( $item['id'] ) ? (string) $item['id'] : '';
			$id_attr    = '' !== $item_id ? ' data-faq-item-id="' . esc_attr( $item_id ) . '"' : '';
			$answer_html = wp_kses( $answer, FaqBlockAttributeSanitizer::answer_kses_allowed() );

			if ( 'static' === $display_mode ) {
				$items_html .= '<div class="seovault-faq-block__item"' . $id_attr . '><h3 class="seovault-faq-block__question">' . esc_html( $label ) . '</h3><div class="seovault-faq-block__answer">' . $answer_html . '</div></div>';
			} else {
				$open_attr   = ( $open_first && 1 === $index ) ? ' open' : '';
				$items_html .= '<details class="seovault-faq-block__item"' . $id_attr . $open_attr . '><summary class="seovault-faq-block__question">' . esc_html( $label ) . '</summary><div class="seovault-faq-block__answer">' . $answer_html . '</div></details>';
			}
		}

		$heading_html = '' !== $heading ? '<h2 class="seovault-faq-block__heading">' . esc_html( $heading ) . '</h2>' : '';
		$intro_html   = '' !== $intro ? '<p class="seovault-faq-block__intro">' . esc_html( $intro ) . '</p>' : '';

		return '<section class="' . esc_attr( implode( ' ', $classes ) ) . '" data-seovault-block="faq" data-schema-version="' . esc_attr( (string) $version ) . '">' . $heading_html . $intro_html . '<div class="seovault-faq-block__items">' . $items_html . '</div></section>';
	}
}
