<?php

/**
 * Lightweight Speculation Rules output
 *
 * Outputs safe prefetch rules for faster client-side navigation.
 *
 * @package ProRank\SEO\Frontend
 */

declare(strict_types=1);

namespace ProRank\SEO\Frontend;

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


/**
 * SpeculationRules
 */


class SpeculationRules {
    /**
     * Boot hooks
     */
    public function init(): void {
        if ( \ProRank\SEO\Plugin::is_elementor_runtime_request() ) {
            return;
        }

        add_action( 'wp_head', function() {
            try {
                $this->output_rules();
            } catch (\Throwable $e) {
                if (function_exists('prorank_log')) {
                    prorank_log('[ProRank SpeculationRules] wp_head output failed: ' . $e->getMessage());
                }
            }
        }, 1 );
    }

    /**
     * Output speculationrules script
     */
    public function output_rules(): void {
        static $output_done = false;
        if ($output_done) return;
        $output_done = true;
        if ( is_admin() || is_preview() || is_customize_preview() || \ProRank\SEO\Plugin::is_elementor_runtime_request() ) {
            return;
        }

        $settings = get_option('prorank_cache_settings', []);
        if (!is_array($settings)) {
            $settings = [];
        }

        $prefetch_enabled = !empty($settings['prefetch_enabled']);
        $prerender_enabled = !empty($settings['prerender_enabled']);
        if (!$prefetch_enabled && !$prerender_enabled) {
            return;
        }

        $prefetch_eagerness = (string) ($settings['prefetch_eagerness'] ?? 'moderate');
        if (!in_array($prefetch_eagerness, ['conservative', 'moderate', 'eager', 'immediate'], true)) {
            $prefetch_eagerness = 'moderate';
        }

        // Basic rules: prefetch document links without query strings; safe excludes
        $rules = [];
        if ($prefetch_enabled) {
            $rules['prefetch'] = [
                [
                    'source'    => 'document',
                    'where'     => [
                        'and' => array_values( array_filter( [
                            [ 'href_matches' => '/*' ],
                            [ 'not' => [ 'href_matches' => '/*\\?*' ] ],
                            [ 'not' => [ 'selector_matches' => '[data-no-prefetch], [rel~=nofollow]' ] ],
                            $this->woo_excludes(),
                        ] ) ),
                    ],
                    'eagerness' => $prefetch_eagerness,
                ],
            ];
        }
        if ($prerender_enabled) {
            $rules['prerender'] = [
                [
                    'source' => 'document',
                    'where' => [
                        'and' => array_values(array_filter([
                            ['href_matches' => '/*'],
                            ['not' => ['href_matches' => '/*\\?*']],
                            ['not' => ['selector_matches' => '[data-no-prerender], [rel~=nofollow]']],
                            $this->woo_excludes(),
                        ])),
                    ],
                    'eagerness' => (string) ($settings['prerender_eagerness'] ?? 'conservative'),
                ],
            ];
        }

        $json = wp_json_encode( $rules, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
        if ( empty( $json ) ) {
            return;
        }

        $this->output_deferred_rules($rules);
    }

    /**
     * Output speculation rules after the critical loading window.
     *
     * @param array $rules Speculation rules payload.
     * @return void
     */
    private function output_deferred_rules(array $rules): void {
        $payload = wp_json_encode(
            [
                'rules' => $rules,
                'delay' => 3500,
            ],
            JSON_UNESCAPED_SLASHES
        );

        if (empty($payload)) {
            return;
        }

        echo '<script id="prorank-speculation-rules-bootstrap">(function(){try{var c=' . $payload . ';var n=navigator||{};var cn=n.connection||n.mozConnection||n.webkitConnection;if(n.webdriver){return;}if(cn&&(cn.saveData||/2g/i.test(String(cn.effectiveType||"")))){return;}var done=false;var inject=function(){if(done){return;}done=true;var s=document.createElement("script");s.type="speculationrules";s.text=JSON.stringify(c.rules);document.head.appendChild(s);};var arm=function(){setTimeout(function(){if("requestIdleCallback" in window){requestIdleCallback(inject,{timeout:2500});}else{inject();}},Math.max(1000,c.delay||3500));};if(document.readyState==="complete"){arm();}else{window.addEventListener("load",arm,{once:true});}}catch(e){}})();</script>' . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- JSON is generated by wp_json_encode.
    }

    /**
     * Exclude WooCommerce carts/checkout from prefetch
     */
    private function woo_excludes() {
        if ( class_exists( 'WooCommerce' ) ) {
            return [
                'not' => [
                    'href_matches' => '/{cart,checkout,my-account}/*',
                ],
            ];
        }
        return null;
    }
}
