<?php

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

class AIEO_Ratings_Schema {

    
    const DEFAULT_MIN_COUNT = 1;

    public static function init() {
        if ( is_admin() ) { return; }
        if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { return; }

        
        
        
        
        
        
        
        
        
        add_filter( 'aioseo_schema_output',                            array( __CLASS__, 'inject_aioseo' ),    20, 1 );
        add_filter( 'wpseo_schema_product',                            array( __CLASS__, 'inject_yoast' ),     20, 2 );
        add_filter( 'rank_math/snippet/rich_snippet_product_entity',   array( __CLASS__, 'inject_rankmath' ),  20, 1 );

        
        
        
        add_action( 'wp_head', array( __CLASS__, 'maybe_emit_fallback_schema' ), 99 );
    }

    
    public static function build_aggregate_rating( $product_id = null ) {
        if ( $product_id === null ) {
            if ( ! function_exists( 'is_product' ) || ! is_product() ) { return null; }
            
            
            
            
            $product_id = (int) get_queried_object_id();
        }
        $product_id = (int) $product_id;
        if ( $product_id <= 0 ) { return null; }
        if ( ! class_exists( 'AIEO_Ratings' ) ) { return null; }

        
        
        
        
        $settings = AIEO_Ratings::get_settings();
        if ( empty( $settings['seo_schema_enabled'] ) ) { return null; }

        
        
        
        
        
        $count = 0;
        $avg   = 0.0;
        global $wpdb;
        $vitals = $wpdb->prefix . 'aieo_core_product_vitals';
        static $has_vcols = null;
        if ( $has_vcols === null ) {
            $has_vcols = ( (int) $wpdb->get_var( $wpdb->prepare(
                "SELECT COUNT(*) FROM information_schema.COLUMNS
                 WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s AND COLUMN_NAME = 'rating_value'",
                $vitals
            ) ) ) > 0;
        }
        
        $vrow = $has_vcols ? $wpdb->get_row( $wpdb->prepare(
            "SELECT rating_value, rating_count FROM `{$vitals}` WHERE product_id = %d AND rating_value IS NOT NULL LIMIT 1",
            $product_id
        ), ARRAY_A ) : null;
        if ( is_array( $vrow ) ) {
            $count = (int) $vrow['rating_count'];
            $avg   = (float) $vrow['rating_value'];
        } else {
            $stats = AIEO_Ratings::instance()->get_stats( $product_id, 'product' );
            if ( is_array( $stats ) ) {
                $count = isset( $stats['count'] ) ? (int) $stats['count'] : 0;
                $avg   = isset( $stats['avg'] )   ? (float) $stats['avg'] : 0.0;
            }
        }

        $min_count = (int) apply_filters( 'aieo_ratings_schema_min_count', self::DEFAULT_MIN_COUNT, $product_id );
        if ( $count < $min_count || $avg <= 0.0 ) { return null; }

        return array(
            '@type'       => 'AggregateRating',
            'ratingValue' => number_format( $avg, 1, '.', '' ),
            
            
            
            
            'ratingCount' => $count,
            'bestRating'  => '5',
            'worstRating' => '1',
        );
    }

    
    public static function build_reviews( $product_id, $agg = null ) {
        $product_id = (int) $product_id;
        if ( $product_id <= 0 ) { return array(); }
        $reviews = array();

        
        if ( 'yes' === get_option( 'woocommerce_enable_review_rating', 'yes' ) && function_exists( 'get_comments' ) ) {
            $comments = get_comments( array(
                'post_id'      => $product_id,
                'status'       => 'approve',
                'number'       => 5,
                'meta_key'     => 'rating',     
                'meta_compare' => 'EXISTS',
                'orderby'      => 'comment_date_gmt',
                'order'        => 'DESC',
            ) );
            foreach ( (array) $comments as $c ) {
                $r = (int) get_comment_meta( $c->comment_ID, 'rating', true );
                if ( $r < 1 || $r > 5 ) { continue; }
                $body = trim( wp_strip_all_tags( (string) $c->comment_content ) );
                if ( '' === $body ) { continue; }
                $reviews[] = array(
                    '@type'         => 'Review',
                    'reviewRating'  => array( '@type' => 'Rating', 'ratingValue' => (string) $r, 'bestRating' => '5', 'worstRating' => '1' ),
                    'author'        => array( '@type' => 'Person', 'name' => ( '' !== $c->comment_author ? $c->comment_author : __( 'Customer', 'ai-eshop-optimizer' ) ) ),
                    'reviewBody'    => self::truncate_review( $body, 500 ),
                    'datePublished' => mysql2date( 'Y-m-d', $c->comment_date_gmt ),
                );
            }
        }

        
        $seo = class_exists( 'AIEO_DMM_Settings' ) ? (array) AIEO_DMM_Settings::get( 'seo', array() ) : array();
        if ( ! empty( $seo['rating_review_enabled'] ) ) {
            if ( $agg === null ) { $agg = self::build_aggregate_rating( $product_id ); }
            if ( is_array( $agg ) && ! empty( $agg['ratingValue'] ) ) {
                $star     = max( 1, min( 5, (int) round( (float) $agg['ratingValue'] ) ) );
                $means    = ( isset( $seo['rating_star_meanings'] ) && is_array( $seo['rating_star_meanings'] ) ) ? $seo['rating_star_meanings'] : array();
                $defaults = array(
                    1 => __( 'Our customers rate this poorly.', 'ai-eshop-optimizer' ),
                    2 => __( 'Our customers find this below average.', 'ai-eshop-optimizer' ),
                    3 => __( 'Our customers find this average.', 'ai-eshop-optimizer' ),
                    4 => __( 'Our customers find this very good.', 'ai-eshop-optimizer' ),
                    5 => __( 'Our customers find this excellent.', 'ai-eshop-optimizer' ),
                );
                $body = ( isset( $means[ $star ] ) && '' !== trim( (string) $means[ $star ] ) ) ? (string) $means[ $star ] : $defaults[ $star ];
                $org  = (string) get_bloginfo( 'name' );
                $reviews[] = array(
                    '@type'        => 'Review',
                    'reviewRating' => array( '@type' => 'Rating', 'ratingValue' => $agg['ratingValue'], 'bestRating' => '5', 'worstRating' => '1' ),
                    'author'       => array( '@type' => 'Organization', 'name' => ( '' !== $org ? $org : __( 'Verified customers', 'ai-eshop-optimizer' ) ) ),
                    'reviewBody'   => self::truncate_review( $body, 300 ),
                );
            }
        }

        return $reviews;
    }

    
    private static function truncate_review( $text, $limit ) {
        $text = trim( (string) $text );
        $len  = function_exists( 'mb_strlen' ) ? mb_strlen( $text ) : strlen( $text );
        if ( $len <= $limit ) { return $text; }
        $cut = function_exists( 'mb_substr' ) ? mb_substr( $text, 0, $limit - 1 ) : substr( $text, 0, $limit - 1 );
        return rtrim( $cut ) . '…';
    }

    
    
    

    
    public static function inject_aioseo( $graphs ) {
        if ( ! is_array( $graphs ) ) { return $graphs; }
        $agg = self::build_aggregate_rating();
        if ( ! $agg ) { return $graphs; }

        
        
        
        
        $product_types = array( 'Product', 'ProductGroup' );
        foreach ( $graphs as $i => $piece ) {
            if ( is_array( $piece ) ) {
                $type = isset( $piece['@type'] ) ? $piece['@type'] : '';
                $is_product = is_array( $type )
                    ? (bool) array_intersect( $product_types, $type )
                    : in_array( $type, $product_types, true );
                if ( ! $is_product ) { continue; }
                if ( ! empty( $piece['aggregateRating']['@id'] ) ) {
                    $agg['@id'] = $piece['aggregateRating']['@id'];
                }
                $graphs[ $i ]['aggregateRating'] = $agg;
            } elseif ( is_object( $piece ) ) {
                $type = isset( $piece->{'@type'} ) ? $piece->{'@type'} : '';
                $is_product = is_array( $type )
                    ? (bool) array_intersect( $product_types, $type )
                    : in_array( $type, $product_types, true );
                if ( ! $is_product ) { continue; }
                if ( ! empty( $piece->aggregateRating->{'@id'} ) ) {
                    $agg['@id'] = $piece->aggregateRating->{'@id'};
                }
                $graphs[ $i ]->aggregateRating = (object) $agg;
            }
        }
        return $graphs;
    }

    
    
    

    
    public static function inject_yoast( $data ) {
        if ( ! is_array( $data ) ) { return $data; }
        $agg = self::build_aggregate_rating();
        if ( ! $agg ) { return $data; }
        $data['aggregateRating'] = $agg;
        return $data;
    }

    
    
    

    
    public static function inject_rankmath( $entity ) {
        if ( ! is_array( $entity ) ) { return $entity; }
        $agg = self::build_aggregate_rating();
        if ( ! $agg ) { return $entity; }
        $entity['aggregateRating'] = $agg;
        return $entity;
    }

    
    
    

    
    private static function has_seo_plugin() {
        return defined( 'AIOSEO_VERSION' )
            || class_exists( 'AIOSEO\\Plugin\\Common\\Main' )
            || defined( 'WPSEO_VERSION' )
            || class_exists( 'WPSEO_Options' )
            || defined( 'RANK_MATH_VERSION' )
            || class_exists( 'RankMath' );
    }

    
    public static function maybe_emit_fallback_schema() {
        if ( ! function_exists( 'is_product' ) || ! is_product() ) { return; }
        if ( self::has_seo_plugin() ) { return; }

        
        
        
        
        
        
        
        
        
        
        if ( class_exists( 'AIEO_SEO_Store' ) && AIEO_SEO_Store::is_active_provider()
            && class_exists( 'AIEO_SEO_Head' )
            && null !== AIEO_SEO_Head::get( 'product', (int) get_queried_object_id() ) ) {
            return;
        }

        $agg = self::build_aggregate_rating();
        if ( ! $agg ) { return; }

        global $product;
        if ( ! function_exists( 'wc_get_product' ) ) { return; }
        $wc_product = is_a( $product, 'WC_Product' ) ? $product : wc_get_product( get_the_ID() );
        if ( ! $wc_product ) { return; }

        $schema = array(
            '@context'        => 'https://schema.org/',
            '@type'           => 'Product',
            'name'            => (string) $wc_product->get_name(),
            'url'             => (string) get_permalink( $wc_product->get_id() ),
            'aggregateRating' => $agg,
        );
        $sku = (string) $wc_product->get_sku();
        if ( $sku !== '' ) { $schema['sku'] = $sku; }

        echo "\n<script type=\"application/ld+json\" data-aieo-ratings-fallback=\"1\">"
            . wp_json_encode( $schema, JSON_HEX_TAG | JSON_HEX_AMP | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE )
            . "</script>\n";
    }
}

AIEO_Ratings_Schema::init();
