<?php
/**
 * Pro-2: JSON-LD structured data output
 *
 * Outputs Schema.org JSON-LD (AggregateRating, LocalBusiness,
 * OpeningHoursSpecification, Review) on pages where the plugin
 * shortcode or Gutenberg block is present.
 *
 * Requires: Freemius Pro license
 */
if ( ! defined( 'ABSPATH' ) ) exit;

class TMNL_JsonLD {

    /** @var TMNL_Adapter_Google */
    private $google_adapter;

    public function __construct( TMNL_Adapter_Google $google_adapter ) {
        $this->google_adapter = $google_adapter;

        // Only run for Pro users
        if ( ! kw_is_pro() ) {
            return;
        }

        // Output JSON-LD in <head>
        add_action( 'wp_head', array( $this, 'output_jsonld' ), 20 );
    }

    /**
     * Detect plugin shortcode/block in current post and extract store_id.
     * Uses $post->post_content directly to avoid wp_head/the_content timing issue.
     */
    private function detect_store_id() {
        // get_queried_object() works before the loop starts (unlike global $post)
        $queried = get_queried_object();
        if ( ! $queried instanceof WP_Post ) return null;

        $content = $queried->post_content;

        // Gutenberg block
        if ( has_block( 'kuchikomi-wall/kuchikomi-wall', $queried ) ) {
            if ( preg_match( '/"storeId"\s*:\s*"([a-z0-9_\-]+)"/i', $content, $m ) ) {
                return sanitize_key( $m[1] );
            }
            return '';
        }

        // Shortcode
        if ( has_shortcode( $content, 'tmnl_kuchikomi_wall' ) || has_shortcode( $content, 'tmnl_testimonials' ) ) {
            if ( preg_match( '/\[tmnl_kuchikomi_wall[^\]]*store_id=["\']?([a-z0-9_\-]+)["\']?/i', $content, $m ) ) {
                return sanitize_key( $m[1] );
            }
            if ( preg_match( '/\[tmnl_testimonials[^\]]*store_id=["\']?([a-z0-9_\-]+)["\']?/i', $content, $m ) ) {
                return sanitize_key( $m[1] );
            }
            return '';
        }

        return null; // plugin not on this page
    }

    /**
     * Output JSON-LD in wp_head.
     */
    public function output_jsonld() {
        $store_id = $this->detect_store_id();

        // null = plugin not on this page
        if ( is_null( $store_id ) ) return;

        // store_id not specified → use first store
        if ( $store_id === '' ) {
            $stores   = $this->google_adapter->get_stores();
            $store_id = $stores[0]['id'] ?? null;
        }

        if ( empty( $store_id ) ) return;

        $meta    = $this->google_adapter->get_store_meta( $store_id );
        $reviews = $this->google_adapter->get_cached( $store_id );

        if ( empty( $meta ) || empty( $meta['rating'] ) ) return;

        $schema = $this->build_schema( $meta, $reviews );
        if ( empty( $schema ) ) return;

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

    /**
     * Build Schema.org array from place metadata and reviews.
     */
    private function build_schema( $meta, $reviews ) {
        $type = $this->map_type( $meta['primary_type'] ?? '' );

        $schema = array(
            '@context' => 'https://schema.org',
            '@type'    => $type,
            'name'     => $meta['name'] ?? '',
        );

        if ( ! empty( $meta['address'] ) ) {
            $schema['address'] = array(
                '@type'           => 'PostalAddress',
                'streetAddress'   => $meta['address'],
                'addressCountry'  => 'JP',
            );
        }

        if ( ! empty( $meta['phone'] ) ) {
            $schema['telephone'] = $meta['phone'];
        }

        if ( ! empty( $meta['website'] ) ) {
            $schema['url'] = $meta['website'];
        }

        // Opening hours
        if ( ! empty( $meta['opening_hours'] ) ) {
            $schema['openingHoursSpecification'] = $this->build_opening_hours( $meta['opening_hours'] );
        }

        // AggregateRating
        if ( ! empty( $meta['rating'] ) && ! empty( $meta['review_count'] ) ) {
            $schema['aggregateRating'] = array(
                '@type'       => 'AggregateRating',
                'ratingValue' => (string) $meta['rating'],
                'reviewCount' => (string) $meta['review_count'],
                'bestRating'  => '5',
                'worstRating' => '1',
            );
        }

        // Individual reviews (max 5 from API)
        if ( ! empty( $reviews ) ) {
            $review_items = array();
            foreach ( array_slice( $reviews, 0, 5 ) as $r ) {
                if ( empty( $r['text'] ) ) continue;
                $review_items[] = array(
                    '@type'        => 'Review',
                    'author'       => array(
                        '@type' => 'Person',
                        'name'  => $r['author_name'],
                    ),
                    'reviewRating' => array(
                        '@type'       => 'Rating',
                        'ratingValue' => (string) $r['rating'],
                        'bestRating'  => '5',
                        'worstRating' => '1',
                    ),
                    'reviewBody'      => $r['text'],
                    'datePublished'   => $r['date'],
                );
            }
            if ( ! empty( $review_items ) ) {
                $schema['review'] = $review_items;
            }
        }

        return $schema;
    }

    /**
     * Map Places API primaryType to Schema.org @type.
     */
    private function map_type( $primary_type ) {
        $map = array(
            'restaurant'          => 'Restaurant',
            'cafe'                => 'CafeOrCoffeeShop',
            'bar'                 => 'BarOrPub',
            'bakery'              => 'Bakery',
            'beauty_salon'        => 'BeautyBusiness',
            'hair_care'           => 'HairSalon',
            'spa'                 => 'DaySpa',
            'gym'                 => 'SportsActivityLocation',
            'lodging'             => 'LodgingBusiness',
            'hotel'               => 'Hotel',
            'dentist'             => 'Dentist',
            'doctor'              => 'MedicalBusiness',
            'hospital'            => 'Hospital',
            'pharmacy'            => 'Pharmacy',
            'real_estate_agency'  => 'RealEstateAgent',
            'accounting'          => 'AccountingService',
            'lawyer'              => 'LegalService',
            'car_repair'          => 'AutoRepair',
            'clothing_store'      => 'ClothingStore',
            'electronics_store'   => 'ElectronicsStore',
            'book_store'          => 'BookStore',
            'florist'             => 'Florist',
            'jewelry_store'       => 'JewelryStore',
            'pet_store'           => 'PetStore',
            'shoe_store'          => 'ShoeStore',
            'supermarket'         => 'GroceryStore',
            'convenience_store'   => 'ConvenienceStore',
            'school'              => 'School',
            'library'             => 'Library',
            'museum'              => 'Museum',
            'park'                => 'Park',
        );

        return $map[ $primary_type ] ?? 'LocalBusiness';
    }

    /**
     * Convert weekday descriptions to OpeningHoursSpecification array.
     * Places API returns strings like "月曜日: 11:00〜22:00"
     * We output a simple array using openingHours (simpler and widely supported).
     */
    private function build_opening_hours( $weekday_descriptions ) {
        if ( empty( $weekday_descriptions ) || ! is_array( $weekday_descriptions ) ) {
            return array();
        }

        $day_map = array(
            '月曜日' => 'Monday',
            '火曜日' => 'Tuesday',
            '水曜日' => 'Wednesday',
            '木曜日' => 'Thursday',
            '金曜日' => 'Friday',
            '土曜日' => 'Saturday',
            '日曜日' => 'Sunday',
        );

        $specs = array();

        foreach ( $weekday_descriptions as $desc ) {
            // e.g. "月曜日: 11:00〜22:00" or "月曜日: 定休日"
            foreach ( $day_map as $ja => $en ) {
                if ( strpos( $desc, $ja ) === false ) continue;

                // Extract times: HH:MM〜HH:MM
                if ( preg_match( '/(\d{1,2}:\d{2})[〜～\-–](\d{1,2}:\d{2})/', $desc, $m ) ) {
                    $specs[] = array(
                        '@type'     => 'OpeningHoursSpecification',
                        'dayOfWeek' => 'https://schema.org/' . $en,
                        'opens'     => $m[1],
                        'closes'    => $m[2],
                    );
                }
                // 定休日 / Closed → skip (no entry = closed)
                break;
            }
        }

        return $specs;
    }
}
