<?php

/**
 * Schema Validator
 *
 * Validates and tests schema markup for Google Rich Results compatibility.
 *
 * @package ProRank\SEO\Core\Schema
 * @since   2.0.0
 */

declare(strict_types=1);

namespace ProRank\SEO\Core\Schema;

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


/**
 * SchemaValidator class
 *
 * Provides validation and testing for schema.org structured data
 */


class SchemaValidator {
    
    /**
     * Google Rich Results Test API endpoint
     *
     * @var string
     */
    private const GOOGLE_TEST_API = 'https://searchconsole.googleapis.com/v1/urlTestingTools/richResults:test';
    
    /**
     * Schema.org validator endpoint
     *
     * @var string
     */
    private const SCHEMA_VALIDATOR_API = 'https://validator.schema.org/api/validate';
    
    /**
     * Validate schema markup
     *
     * @param array $schema Schema data to validate.
     * @return array Validation results.
     */
    public function validate(array $schema): array {
        $results = [
            'valid' => true,
            'errors' => [],
            'warnings' => [],
            'info' => []
        ];

        // Basic structural validation
        $structural = $this->validate_structure($schema);
        if (!$structural['valid']) {
            $results['valid'] = false;
            $results['errors'] = array_merge($results['errors'], $structural['errors']);
        }

        // Product-specific validation
        if ($this->is_product_schema($schema)) {
            $product_validation = $this->validate_product_schema($schema);
            if (!$product_validation['valid']) {
                $results['valid'] = false;
                $results['errors'] = array_merge($results['errors'], $product_validation['errors']);
            }
            $results['warnings'] = array_merge($results['warnings'], $product_validation['warnings']);
            $results['info'] = array_merge($results['info'], $product_validation['info']);
        }

        // 2025 Schema Types validation
        $schema_type = $this->get_schema_type($schema);

        if ($schema_type === 'DiscussionForumPosting') {
            $discussion_validation = $this->validate_discussion_forum_posting($schema);
            $results = $this->merge_validation_results($results, $discussion_validation);
        }

        if ($schema_type === 'ProfilePage') {
            $profile_validation = $this->validate_profile_page($schema);
            $results = $this->merge_validation_results($results, $profile_validation);
        }

        return $results;
    }
    
    /**
     * Validate schema structure
     *
     * @param array $schema Schema data.
     * @return array Validation results.
     */
    private function validate_structure(array $schema): array {
        $errors = [];
        
        // Check for @context
        if (!isset($schema['@context']) && !isset($schema['@graph'])) {
            $errors[] = 'Missing @context property. Schema must include "https://schema.org" context.';
        }
        
        // Check for @type or @graph
        if (!isset($schema['@type']) && !isset($schema['@graph'])) {
            $errors[] = 'Missing @type or @graph property. Schema must specify a type.';
        }
        
        // If using @graph, validate it's an array
        if (isset($schema['@graph']) && !is_array($schema['@graph'])) {
            $errors[] = '@graph must be an array of schema objects.';
        }
        
        return [
            'valid' => empty($errors),
            'errors' => $errors
        ];
    }
    
    /**
     * Check if schema is a product schema
     *
     * @param array $schema Schema data.
     * @return bool
     */
    private function is_product_schema(array $schema): bool {
        if (isset($schema['@type']) && $schema['@type'] === 'Product') {
            return true;
        }
        
        if (isset($schema['@graph'])) {
            foreach ($schema['@graph'] as $item) {
                if (isset($item['@type']) && $item['@type'] === 'Product') {
                    return true;
                }
            }
        }
        
        return false;
    }
    
    /**
     * Validate product-specific schema
     *
     * @param array $schema Schema data.
     * @return array Validation results.
     */
    private function validate_product_schema(array $schema): array {
        $errors = [];
        $warnings = [];
        $info = [];
        
        // Find product object
        $product = null;
        if (isset($schema['@type']) && $schema['@type'] === 'Product') {
            $product = $schema;
        } elseif (isset($schema['@graph'])) {
            foreach ($schema['@graph'] as $item) {
                if (isset($item['@type']) && $item['@type'] === 'Product') {
                    $product = $item;
                    break;
                }
            }
        }
        
        if (!$product) {
            return ['valid' => true, 'errors' => [], 'warnings' => [], 'info' => []];
        }
        
        // Required fields for Google Rich Results
        if (!isset($product['name']) || empty($product['name'])) {
            $errors[] = 'Product name is required for rich results.';
        }
        
        if (!isset($product['image']) && !isset($product['images'])) {
            $warnings[] = 'Product image is recommended for better rich results.';
        }
        
        // Validate offers
        if (!isset($product['offers'])) {
            $errors[] = 'Product offers are required for price display in search results.';
        } else {
            $offers_validation = $this->validate_offers($product['offers']);
            $errors = array_merge($errors, $offers_validation['errors']);
            $warnings = array_merge($warnings, $offers_validation['warnings']);
        }
        
        // Recommended fields for enhanced results
        if (!isset($product['description'])) {
            $warnings[] = 'Product description is recommended for better search visibility.';
        }
        
        if (!isset($product['brand'])) {
            $warnings[] = 'Brand information enhances product rich results.';
        }
        
        if (!isset($product['sku'])) {
            $info[] = 'Consider adding SKU for better product identification.';
        }
        
        if (!isset($product['gtin']) && !isset($product['mpn']) && !isset($product['isbn'])) {
            $info[] = 'Global identifiers (GTIN/MPN/ISBN) improve product matching in Google Shopping.';
        }
        
        if (!isset($product['aggregateRating']) && !isset($product['review'])) {
            $info[] = 'Product ratings and reviews can increase click-through rates.';
        }
        
        return [
            'valid' => empty($errors),
            'errors' => $errors,
            'warnings' => $warnings,
            'info' => $info
        ];
    }
    
    /**
     * Validate offers schema
     *
     * @param mixed $offers Offers data.
     * @return array Validation results.
     */
    private function validate_offers($offers): array {
        $errors = [];
        $warnings = [];
        
        // Handle both single Offer and AggregateOffer
        if (is_array($offers)) {
            if (isset($offers['@type'])) {
                // Single offer object
                $offers_to_validate = [$offers];
            } else {
                // Array of offers
                $offers_to_validate = $offers;
            }
            
            foreach ($offers_to_validate as $offer) {
                if (!isset($offer['@type'])) {
                    $errors[] = 'Offer must have @type property.';
                    continue;
                }
                
                if ($offer['@type'] === 'Offer') {
                    // Validate single offer
                    if (!isset($offer['price']) && !isset($offer['priceSpecification'])) {
                        $errors[] = 'Offer must include price information.';
                    }
                    
                    if (!isset($offer['priceCurrency'])) {
                        $errors[] = 'Offer must specify priceCurrency (e.g., "USD").';
                    }
                    
                    if (!isset($offer['availability'])) {
                        $warnings[] = 'Offer should include availability status.';
                    }
                } elseif ($offer['@type'] === 'AggregateOffer') {
                    // Validate aggregate offer
                    if (!isset($offer['lowPrice']) || !isset($offer['highPrice'])) {
                        $errors[] = 'AggregateOffer must include lowPrice and highPrice.';
                    }
                    
                    if (!isset($offer['priceCurrency'])) {
                        $errors[] = 'AggregateOffer must specify priceCurrency.';
                    }
                }
                
                // Validate price validity period
                if (isset($offer['priceValidUntil'])) {
                    $valid_until = strtotime($offer['priceValidUntil']);
                    if ($valid_until && $valid_until < time()) {
                        $warnings[] = 'Price validity period has expired. Update priceValidUntil.';
                    }
                }
            }
        }
        
        return [
            'errors' => $errors,
            'warnings' => $warnings
        ];
    }
    
    /**
     * Test schema with Google Rich Results Test
     *
     * @param string $url URL to test.
     * @param string $api_key Google API key (optional).
     * @return array Test results.
     */
    public function test_with_google(string $url, string $api_key = ''): array {
        // This would require a Google API key and proper authentication
        // For now, return a placeholder response
        return [
            'status' => 'requires_api_key',
            'message' => 'Google Rich Results testing requires API configuration.',
            'test_url' => 'https://search.google.com/test/rich-results?url=' . urlencode($url)
        ];
    }
    
    /**
     * Get schema validation report
     *
     * @param array $schema Schema data.
     * @return string HTML report.
     */
    public function get_validation_report(array $schema): string {
        $validation = $this->validate($schema);
        
        $html = '<div class="prorank-schema-validation-report">';
        
        // Status
        $status_class = $validation['valid'] ? 'success' : 'error';
        $status_text = $validation['valid'] ? 'Valid' : 'Invalid';
        $html .= '<div class="validation-status ' . $status_class . '">';
        $html .= '<strong>Schema Status:</strong> ' . $status_text;
        $html .= '</div>';
        
        // Errors
        if (!empty($validation['errors'])) {
            $html .= '<div class="validation-errors">';
            $html .= '<h4>Errors (' . count($validation['errors']) . ')</h4>';
            $html .= '<ul>';
            foreach ($validation['errors'] as $error) {
                $html .= '<li class="error-item">' . esc_html($error) . '</li>';
            }
            $html .= '</ul>';
            $html .= '</div>';
        }
        
        // Warnings
        if (!empty($validation['warnings'])) {
            $html .= '<div class="validation-warnings">';
            $html .= '<h4>Warnings (' . count($validation['warnings']) . ')</h4>';
            $html .= '<ul>';
            foreach ($validation['warnings'] as $warning) {
                $html .= '<li class="warning-item">' . esc_html($warning) . '</li>';
            }
            $html .= '</ul>';
            $html .= '</div>';
        }
        
        // Info
        if (!empty($validation['info'])) {
            $html .= '<div class="validation-info">';
            $html .= '<h4>Recommendations (' . count($validation['info']) . ')</h4>';
            $html .= '<ul>';
            foreach ($validation['info'] as $info) {
                $html .= '<li class="info-item">' . esc_html($info) . '</li>';
            }
            $html .= '</ul>';
            $html .= '</div>';
        }
        
        // Test link
        $html .= '<div class="validation-actions">';
        $html .= '<a href="https://search.google.com/test/rich-results" target="_blank" class="button">Test with Google Rich Results</a>';
        $html .= '</div>';
        
        $html .= '</div>';

        return $html;
    }

    /**
     * Get schema type from schema data
     *
     * @param array $schema Schema data.
     * @return string|null Schema type.
     */
    private function get_schema_type(array $schema): ?string {
        if (isset($schema['@type'])) {
            return $schema['@type'];
        }

        if (isset($schema['@graph']) && is_array($schema['@graph'])) {
            foreach ($schema['@graph'] as $item) {
                if (isset($item['@type'])) {
                    return $item['@type'];
                }
            }
        }

        return null;
    }

    /**
     * Merge validation results
     *
     * @param array $results1 First validation results.
     * @param array $results2 Second validation results.
     * @return array Merged results.
     */
    private function merge_validation_results(array $results1, array $results2): array {
        return [
            'valid' => $results1['valid'] && $results2['valid'],
            'errors' => array_merge($results1['errors'], $results2['errors']),
            'warnings' => array_merge($results1['warnings'], $results2['warnings']),
            'info' => array_merge($results1['info'], $results2['info'])
        ];
    }

    /**
     * Validate DiscussionForumPosting schema (2025)
     *
     * @param array $schema Schema data.
     * @return array Validation results.
     */
    private function validate_discussion_forum_posting(array $schema): array {
        $errors = [];
        $warnings = [];
        $info = [];

        $posting = $this->extract_schema_object($schema, 'DiscussionForumPosting');

        if (!$posting) {
            return ['valid' => true, 'errors' => [], 'warnings' => [], 'info' => []];
        }

        // Required properties
        if (!isset($posting['headline']) || empty($posting['headline'])) {
            $errors[] = 'DiscussionForumPosting requires a headline property.';
        }

        if (!isset($posting['author'])) {
            $errors[] = 'DiscussionForumPosting requires an author property.';
        }

        // Recommended 2025 properties
        if (!isset($posting['interactionStatistic'])) {
            $info[] = 'Consider adding interactionStatistic for better engagement metrics (2025 enhancement).';
        }

        if (!isset($posting['upvoteCount']) && !isset($posting['downvoteCount'])) {
            $info[] = 'Adding upvoteCount/downvoteCount can enhance forum post schema (2025 feature).';
        }

        if (!isset($posting['text'])) {
            $warnings[] = 'Text content is recommended for DiscussionForumPosting.';
        }

        return [
            'valid' => empty($errors),
            'errors' => $errors,
            'warnings' => $warnings,
            'info' => $info
        ];
    }

    /**
     * Validate ProfilePage schema (2025)
     *
     * @param array $schema Schema data.
     * @return array Validation results.
     */
    private function validate_profile_page(array $schema): array {
        $errors = [];
        $warnings = [];
        $info = [];

        $profile = $this->extract_schema_object($schema, 'ProfilePage');

        if (!$profile) {
            return ['valid' => true, 'errors' => [], 'warnings' => [], 'info' => []];
        }

        // Required properties
        if (!isset($profile['mainEntity'])) {
            $errors[] = 'ProfilePage requires a mainEntity property (Person).';
        } else {
            $person = $profile['mainEntity'];

            // Validate Person
            if (!isset($person['@type']) || $person['@type'] !== 'Person') {
                $errors[] = 'ProfilePage mainEntity must be of type Person.';
            }

            if (!isset($person['name']) || empty($person['name'])) {
                $errors[] = 'Person in ProfilePage must have a name.';
            }

            // Recommended properties
            if (!isset($person['image'])) {
                $warnings[] = 'Adding an image to the Person enhances ProfilePage display.';
            }

            if (!isset($person['sameAs'])) {
                $info[] = 'Consider adding sameAs property with social media profiles (2025 enhancement).';
            }

            if (!isset($person['jobTitle'])) {
                $info[] = 'Adding jobTitle can enhance professional profiles.';
            }
        }

        if (!isset($profile['url'])) {
            $warnings[] = 'ProfilePage should include a url property.';
        }

        return [
            'valid' => empty($errors),
            'errors' => $errors,
            'warnings' => $warnings,
            'info' => $info
        ];
    }

    /**
     * Validate enhanced Product schema with 2025 properties
     *
     * @param array $product Product schema data.
     * @return array Validation results.
     */
    private function validate_2025_product_enhancements(array $product): array {
        $info = [];

        // Check for 2025 enhancements
        if (isset($product['offers'])) {
            $offers = is_array($product['offers']) && isset($product['offers'][0]) ?
                      $product['offers'] : [$product['offers']];

            foreach ($offers as $offer) {
                if (!isset($offer['shippingDetails'])) {
                    $info[] = '2025 Enhancement: Consider adding shippingDetails to offers for better product information.';
                }

                if (!isset($offer['hasMerchantReturnPolicy'])) {
                    $info[] = '2025 Enhancement: Adding hasMerchantReturnPolicy improves buyer confidence.';
                } elseif (isset($offer['hasMerchantReturnPolicy']) && !isset($offer['hasMerchantReturnPolicy']['returnPolicyCountry'])) {
                    $info[] = '2025 REQUIRED: returnPolicyCountry is now required in MerchantReturnPolicy (March 2025 Google update).';
                }
            }
        }

        if (!isset($product['additionalProperty'])) {
            $info[] = '2025 Enhancement: Use additionalProperty for sustainability and eco-friendly information.';
        }

        return ['valid' => true, 'errors' => [], 'warnings' => [], 'info' => $info];
    }

    /**
     * Extract schema object by type from schema or @graph
     *
     * @param array  $schema Schema data.
     * @param string $type   Schema type to extract.
     * @return array|null Schema object or null.
     */
    private function extract_schema_object(array $schema, string $type): ?array {
        if (isset($schema['@type']) && $schema['@type'] === $type) {
            return $schema;
        }

        if (isset($schema['@graph']) && is_array($schema['@graph'])) {
            foreach ($schema['@graph'] as $item) {
                if (isset($item['@type']) && $item['@type'] === $type) {
                    return $item;
                }
            }
        }

        return null;
    }
}