<?php

declare(strict_types=1);

/**
 * FAQ Schema Service
 *
 * Single source of truth for building and rendering FAQPage JSON-LD.
 * Used by:
 *   - Premium Gutenberg block (AdvancedSchemaModule::render_faq_schema_block)
 *   - Elementor FAQ widget (FAQWidget::output_faq_schema)
 *   - Premium Page-Targeted FAQ Schema module (TargetedFaqSchemaModule)
 *
 * Provides a request-level emitted flag so multiple producers on the same
 * page don't duplicate FAQPage schema in the rendered HTML.
 *
 * @package ProRank\SEO\Core\Services
 * @since 1.1.4
 */

namespace ProRank\SEO\Core\Services;

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


class FaqSchemaService {

    /**
     * Request-level flag — true once any FAQ schema has been emitted on this request.
     *
     * @var bool
     */
    private static bool $emitted = false;

    /**
     * Normalize raw items into [{question, answer}] keeping only valid entries.
     *
     * Accepts items with either ['question'/'answer'] or ['q'/'a'] keys for
     * convenience — both shapes appear in the codebase.
     *
     * @param array $items Raw items.
     * @return array Normalized items, [] when none are valid.
     */
    public static function normalize_items( array $items ): array {
        $out = [];

        foreach ( $items as $item ) {
            if ( ! is_array( $item ) ) {
                continue;
            }

            $question = trim( (string) ( $item['question'] ?? $item['q'] ?? '' ) );
            $answer = trim( (string) ( $item['answer'] ?? $item['a'] ?? '' ) );

            if ( $question === '' || $answer === '' ) {
                continue;
            }

            $out[] = [
                'question' => $question,
                'answer' => $answer,
            ];
        }

        return $out;
    }

    /**
     * Build the FAQPage JSON-LD array.
     *
     * Schema text fields are stripped of HTML per Google FAQ guidance —
     * callers are responsible for any input-time sanitization.
     *
     * @param array $items Raw items.
     * @return array|null FAQPage schema array, or null when no valid items.
     */
    public static function build_schema( array $items ): ?array {
        $valid = self::normalize_items( $items );

        if ( empty( $valid ) ) {
            return null;
        }

        $main_entity = [];

        foreach ( $valid as $item ) {
            $main_entity[] = [
                '@type' => 'Question',
                'name' => wp_strip_all_tags( $item['question'] ),
                'acceptedAnswer' => [
                    '@type' => 'Answer',
                    'text' => wp_strip_all_tags( $item['answer'] ),
                ],
            ];
        }

        return [
            '@context' => 'https://schema.org',
            '@type' => 'FAQPage',
            'mainEntity' => $main_entity,
        ];
    }

    /**
     * Render a complete <script type="application/ld+json"> tag.
     *
     * Returns empty string when no valid items so callers can safely echo it.
     *
     * @param array $items Raw items.
     * @param string $class_attr Optional class attribute for the script tag.
     * @return string Script tag HTML, or '' when there's nothing to emit.
     */
    public static function render_script( array $items, string $class_attr = '' ): string {
        $schema = self::build_schema( $items );

        if ( $schema === null ) {
            return '';
        }

        $class = $class_attr !== ''
            ? ' class="' . esc_attr( $class_attr ) . '"'
            : '';

        return '<script type="application/ld+json"' . $class . '>'
            . wp_json_encode( $schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE )
            . '</script>';
    }

    /**
     * Mark FAQ schema as emitted for this request.
     *
     * @return void
     */
    public static function mark_emitted(): void {
        self::$emitted = true;
    }

    /**
     * Whether any FAQ schema has been emitted this request.
     *
     * @return bool
     */
    public static function was_emitted(): bool {
        return self::$emitted;
    }

    /**
     * Reset the emitted flag — exposed for tests, not used in production code.
     *
     * @return void
     */
    public static function reset_for_tests(): void {
        self::$emitted = false;
    }
}
