<?php

/**
 * Generate and print the FAQ schema in the site header via the wp_head hook, only for pages containing FAQ sets/groups.
 *
 * @package Flexible_FAQs
 * @subpackage FAQs_Schema
 */
namespace WPGO_Plugins\Flexible_Faqs;

/**
 * FAQ schema class.
 */
class Faq_Schema
{
    /**
     * Contains the plugin root paths/dirs.
     *
     * @var object $module_roots Plugins root paths/dirs.
     */
    protected  $module_roots ;
    /**
     * Filtered Q/A (store processed sets and groups in post meta).
     *
     * @var Array $data Will be used to store the questions answers from FAQ sets and groups.
     */
    protected  $data = array(
        'questions' => array(),
        'answers'   => array(),
        'disabled'  => array(),
    ) ;
    /**
     * To keep count for loaded FAQ sets and groups and to prevent duplicate FAQs in the schema,
     * groups and sets IDs are stored in $loaded faqs as array;
     *
     * @var Array $loaded_faqs An array to contain all the IDs for FAQ sets and groups.
     */
    protected  $loaded_faqs = array(
        'groups' => array(),
        'sets'   => array(),
    ) ;
    /**
     * Class constructor.
     *
     * @param Array $module_roots Plugins root paths/dirs.
     */
    public function __construct( $module_roots )
    {
        $this->module_roots = $module_roots;
        add_action( 'wp_head', array( &$this, 'load_schema_data' ) );
        add_action( 'init', array( &$this, 'register_post_meta' ) );
        // add_action( 'add_meta_boxes', array( &$this, 'add_schema_meta_box' ) );
        // not sure this is needed as we're updating it via React?
        // add_action( 'save_post', array( &$this, 'update_meta_on_save' ), 10, 2 );
    }
    
    /**
     * Register the FAQ post meta fields.
     */
    public function register_post_meta()
    {
        register_meta( 'post', 'wpgo_faq_schema_markup', array(
            'type'    => 'string',
            'single'  => true,
            'default' => '',
        ) );
        register_meta( 'post', 'wpgo_faq_schema_generated', array(
            'type'    => 'string',
            'single'  => true,
            'default' => '0',
        ) );
        register_meta( 'post', 'wpgo_faq_disable_schema', array(
            'show_in_rest' => true,
            'type'         => 'boolean',
            'single'       => true,
            'default'      => false,
        ) );
    }
    
    /**
     * Generate FAQ schema and display in site header via wp_head hook.
     */
    public function load_schema_data()
    {
        global  $post ;
        $output_faq_schema = false;
        $disable_faq_schema = ( isset( $post->ID ) ? get_post_meta( $post->ID, 'wpgo_faq_disable_schema', true ) : false );
        $schema_markup = ( isset( $post->ID ) ? get_post_meta( $post->ID, 'wpgo_faq_schema_markup', true ) : '' );
        $schema_generated_date = ( isset( $post->ID ) ? get_post_meta( $post->ID, 'wpgo_faq_schema_generated', true ) : '0' );
        // Return if FAQ schema is disabled in admin editor.
        if ( '1' !== $disable_faq_schema ) {
            return;
        }
        // Generate FAQ schema markup.
        $box_faqs = has_block( 'flexible-faqs/box-expand-faqs' );
        $text_faqs = has_block( 'flexible-faqs/text-expand-faqs' );
        $accordion_faqs = has_block( 'flexible-faqs/accordion-expand-faqs' );
        // Proceed only if FAQs block exists in the page.
        
        if ( $box_faqs || $text_faqs || $accordion_faqs ) {
            $output_faq_schema = true;
            // Find all FAQ blocks used in the page.
            $all_blocks = parse_blocks( $post->post_content );
            $this->find_faq_blocks( $all_blocks );
        }
        
        // Deals with the FAQs shortcodes.
        
        if ( isset( $post->post_content ) && has_shortcode( $post->post_content, 'flexible-faqs' ) ) {
            $output_faq_schema = true;
            $wpgo_shortcodes = $this->get_flexible_shortcode_attrs();
            foreach ( $wpgo_shortcodes as $shortcode_attrs ) {
                if ( array_key_exists( 'id', $shortcode_attrs ) ) {
                    $this->get_single_faqs_set( $shortcode_attrs['id'] );
                }
                if ( array_key_exists( 'group', $shortcode_attrs ) ) {
                    $this->get_grouped_faqs( $shortcode_attrs['group'] );
                }
            }
        }
        
        
        if ( $output_faq_schema ) {
            $generated_schema = $this->generate_json_schema_markup();
            echo  "\n<!-- Flexible FAQs Schema (generated) -->\n" ;
            echo  $generated_schema ;
        }
    
    }
    
    /**
     * Find all FAQ blocks.
     *
     * @param Array $blocks Array of blocks on the current page.
     */
    public function find_faq_blocks( $blocks )
    {
        // $list = array();
        foreach ( $blocks as $block ) {
            
            if ( strstr( $block['blockName'], 'flexible-faqs/' ) ) {
                // Add current item, if it's a heading block.
                // $list[] = $block;
                // Check wether the block contains FAQ group or just a set of FAQs .
                
                if ( !empty($block['attrs']['group']) ) {
                    $this->get_grouped_faqs( $block['attrs']['group'] );
                } else {
                    if ( isset( $block['attrs']['id'] ) ) {
                        $this->get_single_faqs_set( $block['attrs']['id'] );
                    }
                }
            
            } elseif ( !empty($block['innerBlocks']) ) {
                // Or call the function recursively, to find heading blocks in inner blocks.
                // $list = array_merge( $list, $this->find_faq_blocks( $block['innerBlocks'] ) );
                $this->find_faq_blocks( $block['innerBlocks'] );
            }
        
        }
        // return $list;
    }
    
    /**
     * Return a nested array of all FAQ shortcodes used on a page.
     *
     * @return Array List of attributes present in shortcode.
     */
    public function get_flexible_shortcode_attrs()
    {
        global  $post ;
        $shortcodes_attrs = array();
        preg_match_all(
            '/' . get_shortcode_regex() . '/',
            $post->post_content,
            $matches,
            PREG_SET_ORDER
        );
        foreach ( $matches as $match ) {
            // Only extract attributes if the shortcode is added by this plugin.
            if ( 'flexible-faqs' == $match[2] ) {
                $shortcodes_attrs[] = shortcode_parse_atts( $match[3] );
            }
        }
        return $shortcodes_attrs;
    }
    
    /**
     * Get question and answers from a single set of FAQs.
     *
     * @param Integer $id Custom post type ID.
     */
    public function get_single_faqs_set( $id )
    {
        if ( in_array( $id, $this->loaded_faqs['sets'] ) ) {
            return;
        }
        $questions = get_post_meta( $id, '_wpgo_faq_cpt_questions', true );
        $answers = get_post_meta( $id, '_wpgo_faq_cpt_answers', true );
        $disabled = get_post_meta( $id, '_wpgo_faq_cpt_disabled', true );
        $questions = ( is_array( $questions ) ? $questions : array() );
        $answers = ( is_array( $answers ) ? $answers : array() );
        $disabled = ( is_array( $disabled ) ? $disabled : array() );
        $this->data['questions'] = array_merge( $this->data['questions'], $questions );
        $this->data['answers'] = array_merge( $this->data['answers'], $answers );
        $this->data['disabled'] = array_merge( $this->data['disabled'], $disabled );
        $this->loaded_faqs['sets'][] = $id;
    }
    
    /**
     * Get question and answers from a grouped faqs.
     *
     * @param Integer $id Term/category ID.
     */
    public function get_grouped_faqs( $id )
    {
        // If the FAQs from this blocks are already processed, just continue. Add to array otherwise.
        if ( in_array( $id, $this->loaded_faqs['groups'] ) ) {
            return;
        }
        $faq_groups = FAQ_Templates::get_query( array(
            'group' => $id,
        ) );
        while ( $faq_groups->have_posts() ) {
            $faq_groups->the_post();
            $this->get_single_faqs_set( get_the_ID() );
        }
        $this->loaded_faqs['groups'][] = $id;
        wp_reset_postdata();
    }
    
    /**
     * This function will generate the FAQ schema markup to be outputted on the page.
     *
     * @return String The FAQs schema formatted as HTML.
     */
    public function generate_json_schema_markup()
    {
        $str = '<script type="application/ld+json">' . "\n";
        $str .= '{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [';
        foreach ( $this->data['disabled'] as $index => $item ) {
            // Only add FAQ item to schema if it's active ( Q/A both non-empty, and not disabled).
            
            if ( !empty($this->data['questions'][$index]) && !empty($this->data['answers'][$index]) && '0' === $this->data['disabled'][$index] ) {
                $str .= ( 0 === $index ? "{\n    " : " {\n    " );
                $str .= '"@type": "Question",
    "name": "' . str_replace( array( "\r\n", '"' ), array( '', "'" ), wp_strip_all_tags( strip_shortcodes( $this->data['questions'][$index] ) ) ) . '",
    "acceptedAnswer": {
      "@type": "Answer",
      "text":"' . str_replace( array( "\r\n", '"' ), array( '', "'" ), strip_shortcodes( $this->data['answers'][$index] ) ) . '"
    }
  },';
            }
        
        }
        $str = substr( $str, 0, -1 ) . "]\n}";
        $str .= "\n</script>\n";
        return $str;
    }

}
/* End class definition */