<?php
declare(strict_types=1);

namespace Ekesto\CacheInvalidator\Integrations\Caching;

use Ekesto\CacheInvalidator\Support\Debug;

/**
 * Request-level recursion and duplicate-signal guard for adapter signals.
 */
final class AdapterSignalGuard
{
    /**
     * @var int Suppression depth for adapter-initiated purge operations.
     */
    private static int $suppressionDepth = 0;

    /**
     * @var array<string,bool> Request-local dedupe map.
     */
    private static array $signalMap = [];

    /**
     * Run a callback while suppressing adapter signal processing.
     *
     * @template T
     * @param callable():T $callback
     * @return T
     */
    public static function runWithSuppressedSignals(callable $callback)
    {
        ++self::$suppressionDepth;

        try {
            return $callback();
        } finally {
            self::$suppressionDepth = max(0, self::$suppressionDepth - 1);
        }
    }

    /**
     * Returns whether signals are currently suppressed in this request.
     *
     * @return bool
     */
    public static function isSuppressed(): bool
    {
        return self::$suppressionDepth > 0;
    }

    /**
     * Returns whether a signal should be processed in this request.
     *
     * @param string            $adapter Adapter key.
     * @param string            $hook Hook name.
     * @param string            $signalType Signal type label.
     * @param array<int,string> $urls URL payload for dedupe identity.
     * @return bool
     */
    public static function shouldProcessSignal(
        string $adapter,
        string $hook,
        string $signalType,
        array $urls = []
    ): bool {
        $adapter = sanitize_key($adapter);
        $hook = (string) $hook;
        $signalType = sanitize_key($signalType);
        $urls = self::normalizeUrls($urls);

        if (self::isSuppressed()) {
            Debug::log('eci_signal_suppressed_recursion', [
                'adapter' => $adapter,
                'hook' => $hook,
                'signal_type' => $signalType,
                'url_count' => count($urls),
            ]);
            return false;
        }

        $urlHashInput = wp_json_encode($urls);
        $urlHash = is_string($urlHashInput) ? md5($urlHashInput) : 'nohash';
        $dedupeKey = $adapter . '|' . $hook . '|' . $signalType . '|' . $urlHash;

        if (isset(self::$signalMap[$dedupeKey])) {
            Debug::log('eci_signal_deduped', [
                'adapter' => $adapter,
                'hook' => $hook,
                'signal_type' => $signalType,
                'url_count' => count($urls),
            ]);
            return false;
        }

        self::$signalMap[$dedupeKey] = true;

        Debug::log('eci_signal_ingested', [
            'adapter' => $adapter,
            'hook' => $hook,
            'signal_type' => $signalType,
            'url_count' => count($urls),
        ]);

        return true;
    }

    /**
     * Normalize URL arrays for stable dedupe.
     *
     * @param array<int,string> $urls Raw URLs.
     * @return array<int,string>
     */
    private static function normalizeUrls(array $urls): array
    {
        $out = [];
        $seen = [];

        foreach ($urls as $url) {
            $normalized = trim((string) $url);
            if ($normalized === '' || isset($seen[$normalized])) {
                continue;
            }

            $seen[$normalized] = true;
            $out[] = $normalized;
        }

        return $out;
    }
}
