<?php

declare(strict_types=1);

namespace Numinix\SeekmodoWordPress\Events;

use Numinix\SeekmodoWordPress\Commerce\SearchSwap as CommerceSearchSwap;

/**
 * Stamps `data-seekmodo-*` attributes on SERP result links so the
 * bundled `seekmodo-beacon.js` can mirror clicks for LTR labels.
 *
 * WooCommerce product loops use split open/close anchor hooks; the
 * stock `woocommerce_template_loop_product_link_open()` echo does
 * not expose attribute hooks — we replace those callbacks only while
 * a Seekmodo commerce swap is active on the main query.
 */
final class SerpAttributes
{
    /** @var int 1-based position within the current product loop. */
    private static int $commerceLoopPos = 0;

    public static function register(): void
    {
        if (!function_exists('add_action')) {
            return;
        }

        add_action('woocommerce_before_shop_loop', [self::class, 'resetCommerceLoopPos'], 1);
        add_action('woocommerce_before_shop_loop_item', [self::class, 'armCommerceLinkHooks'], 9);
        add_action('woocommerce_before_shop_loop_item', [self::class, 'commerceLoopProductLinkOpen'], 10);
        add_action('woocommerce_after_shop_loop_item', [self::class, 'commerceLoopProductLinkClose'], 5);
    }

    public static function resetCommerceLoopPos(): void
    {
        self::$commerceLoopPos = 0;
    }

    public static function armCommerceLinkHooks(): void
    {
        if (!self::commerceSwapActive()) {
            return;
        }
        remove_action('woocommerce_before_shop_loop_item', 'woocommerce_template_loop_product_link_open', 10);
        remove_action('woocommerce_after_shop_loop_item', 'woocommerce_template_loop_product_link_close', 5);
    }

    public static function commerceLoopProductLinkOpen(): void
    {
        if (!self::commerceSwapActive()) {
            return;
        }

        global $product;
        if (!($product instanceof \WC_Product)) {
            return;
        }

        self::$commerceLoopPos++;
        $object_id = (int) $product->get_id();
        $link      = apply_filters('woocommerce_loop_product_link', get_the_permalink(), $product);

        $open = '<a href="' . esc_url((string) $link) . '"'
            . ' class="woocommerce-LoopProduct-link woocommerce-loop-product__link"'
            . self::attrHtml($object_id, self::$commerceLoopPos, 'serp')
            . '>';
        echo $open; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- href via esc_url; attrHtml uses ints + esc_attr
    }

    public static function commerceLoopProductLinkClose(): void
    {
        if (!self::commerceSwapActive()) {
            return;
        }

        echo '</a>';
    }

    private static function attrHtml(int $object_id, int $position, string $surface): string
    {
        return sprintf(
            ' data-seekmodo-id="%d" data-seekmodo-pos="%d" data-seekmodo-surface="%s"%s',
            $object_id,
            max(1, $position),
            esc_attr($surface),
            self::searchEventAttr()
        );
    }

    private static function searchEventAttr(): string
    {
        if (!class_exists(CommerceSearchSwap::class)) {
            return '';
        }
        $seid = (int) CommerceSearchSwap::lastSearchEventId();
        if ($seid <= 0) {
            return '';
        }

        return sprintf(' data-seekmodo-seid="%d"', $seid);
    }

    private static function commerceSwapActive(): bool
    {
        if (!function_exists('numinix_seekmodo_enabled') || !numinix_seekmodo_enabled()) {
            return false;
        }
        if (!self::hasSearchKeyword()) {
            return false;
        }
        if (CommerceSearchSwap::serpClickTrackingActive()) {
            return true;
        }

        // Product site-search (`/?s=…&post_type=product`) may clear
        // `s` from the query before the loop renders; fall back to the
        // request params the beacon uses for keyword attribution.
        $post_type = $_GET['post_type'] ?? '';
        if ($post_type === 'product' || (is_array($post_type) && in_array('product', $post_type, true))) {
            return true;
        }

        return function_exists('is_search') && is_search();
    }

    private static function hasSearchKeyword(): bool
    {
        if (function_exists('get_search_query')) {
            $kw = trim((string) get_search_query(false));
            if ($kw !== '') {
                return true;
            }
        }
        if (isset($_GET['s']) && is_string($_GET['s'])) {
            return trim($_GET['s']) !== '';
        }

        return false;
    }
}
