<?php
/**
 * Editor “Add schema” picker policy and shell.
 *
 * Opening the picker never emits schema or mutates post data.
 *
 * @package SEOvaultSEO\Admin
 */

namespace SEOvaultSEO\Admin;

use SEOvaultSEO\Compatibility\Detector;
use SEOvaultSEO\Core\Options;
use SEOvaultSEO\Schema\SchemaArchitectureSettings;
use SEOvaultSEO\Schema\SchemaGuidedFormSchema;
use SEOvaultSEO\Schema\SchemaTypePolicyRegistry;
use SEOvaultSEO\Schema\Validation\PostSchemaValidationService;
use SEOvaultSEO\WooCommerce\WooCommerce;

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

/**
 * Builds filtered Add schema picker groups from the type-policy registry.
 */
class SchemaAddPicker {

	const GROUP_BLOCKS  = 'blocks';
	const GROUP_GUIDED  = 'guided';
	const GROUP_ADVANCED = 'advanced';

	/**
	 * Preferred block types.
	 *
	 * FAQ and HowTo insert/focus their Gutenberg blocks.
	 *
	 * @var string[]
	 */
	const BLOCK_TYPES = array( 'FAQPage', 'HowTo' );

	/**
	 * Gutenberg block names keyed by schema type.
	 *
	 * @var array<string,string>
	 */
	const BLOCK_NAMES = array(
		'FAQPage' => 'seovault/faq',
		'HowTo'   => 'seovault/howto',
	);

	/**
	 * Preferred guided form types.
	 *
	 * @var string[]
	 */
	const GUIDED_TYPES = array(
		'Event',
		'Recipe',
		'Course',
		'JobPosting',
		'SoftwareApplication',
		'Review',
	);

	/**
	 * Build picker context for a post.
	 *
	 * @param int $post_id Post ID.
	 * @return array
	 */
	public static function build_context( $post_id ) {
		$post_id = absint( $post_id );
		$post    = $post_id ? get_post( $post_id ) : null;
		$post_type = ( $post instanceof \WP_Post ) ? (string) $post->post_type : '';

		$catalogue = SchemaArchitectureSettings::get_catalogue_presentation();
		$show_ext  = ! isset( $catalogue['show_extension_types'] ) || ! empty( $catalogue['show_extension_types'] );
		$excluded  = apply_filters( 'seovaultseo_excluded_post_types', array( 'attachment' ) );
		if ( ! is_array( $excluded ) ) {
			$excluded = array( 'attachment' );
		}
		$supported = '' !== $post_type
			&& PostSchemaValidationService::is_supported_post_type( $post_type )
			&& ! in_array( $post_type, $excluded, true );

		return array(
			'post_id'             => $post_id,
			'post_type'           => $post_type,
			'can_edit'            => $post_id > 0 && current_user_can( 'edit_post', $post_id ),
			'woo_active'          => WooCommerce::is_woocommerce_active(),
			'seo_output_enabled'  => Detector::should_output_seo(),
			'schema_enabled'      => (bool) Options::is_schema_enabled(),
			'show_extensions'     => (bool) $show_ext,
			'supported_post_type' => $supported,
		);
	}

	/**
	 * Whether the picker may be shown for this context.
	 *
	 * @param array $context Context from build_context().
	 * @return bool
	 */
	public static function is_available( array $context ) {
		return ! empty( $context['can_edit'] )
			&& ! empty( $context['seo_output_enabled'] )
			&& ! empty( $context['supported_post_type'] );
	}

	/**
	 * Build categorized picker actions (read-only; no emission side effects).
	 *
	 * @param array $context Picker context.
	 * @return array{available:bool,reason:string,groups:array}
	 */
	public static function get_groups( array $context ) {
		if ( empty( $context['can_edit'] ) ) {
			return self::empty_result( __( 'You do not have permission to add schema on this post.', 'seovault-ai' ) );
		}
		if ( empty( $context['seo_output_enabled'] ) ) {
			return self::empty_result( __( 'SEOvault SEO output is disabled by compatibility mode.', 'seovault-ai' ) );
		}
		if ( empty( $context['supported_post_type'] ) ) {
			return self::empty_result( __( 'Schema cannot be added for this post type.', 'seovault-ai' ) );
		}

		$blocks   = array();
		$guided   = array();
		$advanced = array();
		$listed   = array();

		foreach ( self::BLOCK_TYPES as $type ) {
			$entry = SchemaTypePolicyRegistry::get_entry( $type );
			if ( ! is_array( $entry ) ) {
				continue;
			}
			$blocks[]         = self::present_block_action( $entry );
			$listed[ $type ] = true;
		}

		foreach ( self::GUIDED_TYPES as $type ) {
			$entry = SchemaTypePolicyRegistry::get_entry( $type );
			if ( ! is_array( $entry ) ) {
				continue;
			}
			$guided[]         = self::present_guided_action( $entry, $context );
			$listed[ $type ] = true;
		}

		// Manual Product only when Woo does not own product pages.
		if ( empty( $context['woo_active'] ) ) {
			$product = SchemaTypePolicyRegistry::get_entry( 'Product' );
			if ( is_array( $product ) ) {
				$action                 = self::present_guided_action( $product, $context );
				$action['description']  = __( 'Manual Product markup is available because no commerce integration owns product pages.', 'seovault-ai' );
				$action['fallback']     = true;
				$guided[]               = $action;
				$listed['Product']      = true;
			}
		} elseif ( 'product' === (string) ( $context['post_type'] ?? '' ) ) {
			$guided[] = array(
				'id'           => 'product-integration',
				'type'         => 'Product',
				'label'        => __( 'Product', 'seovault-ai' ),
				'group'        => self::GROUP_GUIDED,
				'action'       => 'integration_owned',
				'enabled'      => false,
				'status'       => 'integration_owned',
				'status_label' => __( 'Automatic for WooCommerce', 'seovault-ai' ),
				'description'  => __( 'Product schema is owned by the WooCommerce integration on product pages.', 'seovault-ai' ),
				'extension'    => false,
				'fallback'     => false,
			);
			$listed['Product'] = true;
		}

		if ( ! empty( $context['show_extensions'] ) ) {
			foreach ( SchemaTypePolicyRegistry::get_entries() as $type => $entry ) {
				if ( isset( $listed[ $type ] ) || ! is_array( $entry ) ) {
					continue;
				}
				if ( ! SchemaTypePolicyRegistry::is_specialized( $type ) ) {
					continue;
				}
				if ( 'Product' === $type && ! empty( $context['woo_active'] ) ) {
					continue;
				}
				$method = (string) ( $entry['editor_method'] ?? '' );
				if ( 'block' === $method ) {
					$action              = self::present_block_action( $entry );
					$action['extension'] = true;
					$blocks[]            = $action;
				} elseif ( 'guided_form' === $method ) {
					$action              = self::present_guided_action( $entry, $context );
					$action['extension'] = true;
					$guided[]            = $action;
				}
			}
		}

		$advanced[] = array(
			'id'           => 'custom-json',
			'type'         => 'CustomJSON',
			'label'        => __( 'Custom JSON-LD', 'seovault-ai' ),
			'group'        => self::GROUP_ADVANCED,
			'action'       => 'focus_custom_json',
			'enabled'      => true,
			'status'       => 'available',
			'status_label' => __( 'Advanced', 'seovault-ai' ),
			'description'  => __( 'Edit the advanced custom JSON-LD field. This does not change schema until you save the post.', 'seovault-ai' ),
			'extension'    => false,
			'fallback'     => false,
		);

		return array(
			'available' => true,
			'reason'    => '',
			'groups'    => array(
				array(
					'id'          => self::GROUP_BLOCKS,
					'title'       => __( 'Visible content blocks', 'seovault-ai' ),
					'description' => __( 'Insert a visible FAQ or How-to block. Schema follows the same content.', 'seovault-ai' ),
					'actions'     => $blocks,
				),
				array(
					'id'          => self::GROUP_GUIDED,
					'title'       => __( 'Guided schema forms', 'seovault-ai' ),
					'description' => __( 'Add specialized schema only when that content really exists.', 'seovault-ai' ),
					'actions'     => $guided,
				),
				array(
					'id'          => self::GROUP_ADVANCED,
					'title'       => __( 'Advanced', 'seovault-ai' ),
					'description' => __( 'Escape hatch for custom JSON-LD. Prefer blocks and guided forms when possible.', 'seovault-ai' ),
					'actions'     => $advanced,
				),
			),
		);
	}

	/**
	 * Empty dialog shell (survives Schema Health panel replace).
	 *
	 * @param array|null $context Optional context; built from current post when null.
	 * @return string
	 */
	public static function render_shell( $context = null ) {
		if ( ! is_array( $context ) ) {
			$context = self::build_context( get_the_ID() );
		}
		$matrix = self::get_groups( $context );

		$buffer_level = ob_get_level();
		ob_start();
		try {
			?>
		<dialog
			id="seovaultseo-schema-add-picker"
			class="seovaultseo-schema-add-picker"
			aria-labelledby="seovaultseo-schema-add-picker-title"
			aria-modal="true"
		>
			<div class="seovaultseo-schema-add-picker-chrome">
				<header class="seovaultseo-schema-add-picker-header">
					<h2 id="seovaultseo-schema-add-picker-title"><?php esc_html_e( 'Add schema', 'seovault-ai' ); ?></h2>
					<button
						type="button"
						class="button-link seovaultseo-schema-add-picker-close"
						aria-label="<?php esc_attr_e( 'Close add schema picker', 'seovault-ai' ); ?>"
					>
						<span aria-hidden="true">&times;</span>
					</button>
				</header>
				<div class="seovaultseo-schema-add-picker-body" id="seovaultseo-schema-add-picker-body" tabindex="-1">
					<?php echo self::render_body( $matrix, $context ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Method escapes. ?>
				</div>
				<footer class="seovaultseo-schema-add-picker-footer">
					<button type="button" class="button button-primary seovaultseo-schema-add-picker-close">
						<?php esc_html_e( 'Close', 'seovault-ai' ); ?>
					</button>
				</footer>
			</div>
		</dialog>

			<?php echo SchemaGuidedFormRenderer::render_shell( (int) ( $context['post_id'] ?? 0 ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Renderer escapes. ?>
			<?php

			return (string) ob_get_clean();
		} finally {
			while ( ob_get_level() > $buffer_level ) {
				ob_end_clean();
			}
		}
	}

	/**
	 * Render picker body groups.
	 *
	 * @param array $matrix  Result from get_groups().
	 * @param array $context Picker context.
	 * @return string
	 */
	public static function render_body( array $matrix, array $context = array() ) {
		$buffer_level = ob_get_level();
		ob_start();
		try {
			?>
		<div class="seovaultseo-schema-add-picker-content" data-schema-add-picker="1">
			<?php if ( empty( $matrix['available'] ) ) : ?>
				<p class="seovaultseo-schema-add-picker-unavailable" role="status">
					<?php echo esc_html( (string) ( $matrix['reason'] ?? '' ) ); ?>
				</p>
			<?php else : ?>
				<p class="seovaultseo-schema-add-picker-intro">
					<?php esc_html_e( 'Choose how to add optional schema. Availability here does not publish schema by itself.', 'seovault-ai' ); ?>
				</p>
				<?php if ( empty( $context['schema_enabled'] ) ) : ?>
					<p class="seovaultseo-schema-add-picker-note" role="status">
						<?php esc_html_e( 'Schema output is currently disabled in settings. You can still prepare content, but nothing is published until schema output is enabled.', 'seovault-ai' ); ?>
					</p>
				<?php endif; ?>
				<?php foreach ( (array) ( $matrix['groups'] ?? array() ) as $group ) : ?>
					<?php if ( ! is_array( $group ) ) { continue; } ?>
					<section
						class="seovaultseo-schema-add-picker-group seovaultseo-schema-add-picker-group-<?php echo esc_attr( (string) ( $group['id'] ?? '' ) ); ?>"
						aria-labelledby="seovaultseo-schema-add-group-<?php echo esc_attr( (string) ( $group['id'] ?? '' ) ); ?>"
					>
						<h3 id="seovaultseo-schema-add-group-<?php echo esc_attr( (string) ( $group['id'] ?? '' ) ); ?>">
							<?php echo esc_html( (string) ( $group['title'] ?? '' ) ); ?>
						</h3>
						<?php if ( ! empty( $group['description'] ) ) : ?>
							<p class="seovaultseo-schema-add-picker-muted"><?php echo esc_html( (string) $group['description'] ); ?></p>
						<?php endif; ?>
						<ul class="seovaultseo-schema-add-picker-actions" role="list">
							<?php foreach ( (array) ( $group['actions'] ?? array() ) as $action ) : ?>
								<?php if ( ! is_array( $action ) ) { continue; } ?>
								<li>
									<button
										type="button"
										class="button button-secondary seovaultseo-schema-add-action"
										data-schema-add-id="<?php echo esc_attr( (string) ( $action['id'] ?? '' ) ); ?>"
										data-schema-add-type="<?php echo esc_attr( (string) ( $action['type'] ?? '' ) ); ?>"
										data-schema-add-action="<?php echo esc_attr( (string) ( $action['action'] ?? '' ) ); ?>"
										data-schema-add-block="<?php echo esc_attr( (string) ( $action['block_name'] ?? '' ) ); ?>"
										<?php disabled( empty( $action['enabled'] ) ); ?>
										<?php echo empty( $action['enabled'] ) ? 'aria-disabled="true"' : ''; ?>
									>
										<span class="seovaultseo-schema-add-action-label"><?php echo esc_html( (string) ( $action['label'] ?? '' ) ); ?></span>
										<?php if ( ! empty( $action['status_label'] ) ) : ?>
											<span class="seovaultseo-schema-add-action-status"><?php echo esc_html( (string) $action['status_label'] ); ?></span>
										<?php endif; ?>
									</button>
									<?php if ( ! empty( $action['description'] ) ) : ?>
										<p class="seovaultseo-schema-add-picker-muted"><?php echo esc_html( (string) $action['description'] ); ?></p>
									<?php endif; ?>
								</li>
							<?php endforeach; ?>
						</ul>
					</section>
				<?php endforeach; ?>
			<?php endif; ?>
		</div>
			<?php

			return (string) ob_get_clean();
		} finally {
			while ( ob_get_level() > $buffer_level ) {
				ob_end_clean();
			}
		}
	}

	/**
	 * @param array $entry Policy entry.
	 * @return array
	 */
	private static function present_block_action( array $entry ) {
		$type       = (string) ( $entry['type'] ?? '' );
		$label      = (string) ( $entry['label'] ?? $type );
		$block_name = isset( self::BLOCK_NAMES[ $type ] ) ? self::BLOCK_NAMES[ $type ] : '';
		$available  = '' !== $block_name && in_array( $type, array( 'FAQPage', 'HowTo' ), true );

		if ( $available && 'HowTo' === $type ) {
			return array(
				'id'           => 'block-' . strtolower( $type ),
				'type'         => $type,
				'label'        => $label,
				'group'        => self::GROUP_BLOCKS,
				'action'       => 'insert_block',
				'block_name'   => $block_name,
				'enabled'      => true,
				'status'       => 'available',
				'status_label' => __( 'Insert How-to block', 'seovault-ai' ),
				'description'  => __(
					'Inserts or focuses the SEOvault How-to block. Fill a name and ordered steps — schema is published separately when the how-to is complete.',
					'seovault-ai'
				),
				'extension'    => false,
				'fallback'     => false,
			);
		}

		if ( $available && 'FAQPage' === $type ) {
			return array(
				'id'           => 'block-' . strtolower( $type ),
				'type'         => $type,
				'label'        => $label,
				'group'        => self::GROUP_BLOCKS,
				'action'       => 'insert_block',
				'block_name'   => $block_name,
				'enabled'      => true,
				'status'       => 'available',
				'status_label' => __( 'Insert FAQ block', 'seovault-ai' ),
				'description'  => __(
					'Inserts or focuses the SEOvault FAQ block. Fill visible questions and answers — schema is published separately when pairs are complete.',
					'seovault-ai'
				),
				'extension'    => false,
				'fallback'     => false,
			);
		}

		return array(
			'id'           => 'block-' . strtolower( $type ),
			'type'         => $type,
			'label'        => $label,
			'group'        => self::GROUP_BLOCKS,
			'action'       => 'insert_block',
			'block_name'   => $block_name,
			'enabled'      => false,
			'status'       => 'coming_soon',
			'status_label' => __( 'Coming in a later update', 'seovault-ai' ),
			'description'  => sprintf(
				/* translators: %s: schema type label. */
				__( '%s block insertion will be available when the SEOvault block ships. This action does not change the post yet.', 'seovault-ai' ),
				$label
			),
			'extension'    => false,
			'fallback'     => false,
		);
	}

	/**
	 * @param array $entry   Policy entry.
	 * @param array $context Picker context.
	 * @return array
	 */
	private static function present_guided_action( array $entry, array $context ) {
		$type  = (string) ( $entry['type'] ?? '' );
		$label = (string) ( $entry['label'] ?? $type );
		$help  = SchemaGuidedFormSchema::type_help( $type );

		return array(
			'id'           => 'guided-' . strtolower( $type ),
			'type'         => $type,
			'label'        => $label,
			'group'        => self::GROUP_GUIDED,
			'action'       => 'open_guided_form',
			'enabled'      => true,
			'status'       => 'available',
			'status_label' => __( 'Guided form', 'seovault-ai' ),
			'description'  => $help['picker_description'],
			'extension'    => false,
			'fallback'     => false,
			'post_type'    => (string) ( $context['post_type'] ?? '' ),
		);
	}

	/**
	 * @param string $reason Unavailable reason.
	 * @return array
	 */
	private static function empty_result( $reason ) {
		return array(
			'available' => false,
			'reason'    => (string) $reason,
			'groups'    => array(),
		);
	}
}
