<?php

namespace WPSN\Modules\Audit;

defined('ABSPATH') || exit;

/**
 * Lightweight signal helpers for rendered SEO meta and template sanity checks.
 *
 * Why this exists:
 * - Accurate audit should treat separator-only / placeholder-only rendered meta as effectively blank.
 * - Duplicate-key normalization should ignore broken blank-like values.
 * - SQL template profiling should share the same separator-only template heuristic.
 */
final class AuditMetaSignal
{
    public const REASON_OK = 'ok';
    public const REASON_BLANK = 'blank';
    public const REASON_SEPARATOR_ONLY = 'separator_only';
    public const REASON_PLACEHOLDER_ONLY = 'placeholder_only';

    public const LENGTH_SKIP = 'skip';
    public const LENGTH_OK = 'ok';
    public const LENGTH_TOO_SHORT = 'too_short';
    public const LENGTH_TOO_LONG = 'too_long';

    /**
     * @return array{normalized:string,blank:bool,reason:string}
     */
    public static function analyzeRenderedMeta(string $value): array
    {
        $normalized = self::normalizeText($value);
        if ($normalized === '') {
            return [
                'normalized' => '',
                'blank' => true,
                'reason' => self::REASON_BLANK,
            ];
        }

        $withoutPlaceholders = self::stripRenderedPlaceholders($normalized);
        $signal = self::stripNoise($withoutPlaceholders, true);

        if ($signal === '') {
            $reason = $withoutPlaceholders !== $normalized
                ? self::REASON_PLACEHOLDER_ONLY
                : self::REASON_SEPARATOR_ONLY;

            return [
                'normalized' => $normalized,
                'blank' => true,
                'reason' => $reason,
            ];
        }

        return [
            'normalized' => $normalized,
            'blank' => false,
            'reason' => self::REASON_OK,
        ];
    }

    public static function isEffectivelyBlankRenderedMeta(string $value): bool
    {
        $info = self::analyzeRenderedMeta($value);
        return !empty($info['blank']);
    }

    public static function hasUnresolvedRenderedPlaceholder(string $value): bool
    {
        $value = self::normalizeText($value);
        if ($value === '') {
            return false;
        }

        return self::containsPlaceholderSyntax($value);
    }

    /**
     * @return array{normalized:string,length:int,status:string,has_placeholder:bool,blank:bool,reason:string}
     */
    public static function classifyRenderedLength(string $value, int $min, int $max): array
    {
        $info = self::analyzeRenderedMeta($value);
        $normalized = (string) ($info['normalized'] ?? '');
        $blank = !empty($info['blank']);
        $reason = (string) ($info['reason'] ?? self::REASON_OK);
        $hasPlaceholder = self::containsPlaceholderSyntax($normalized);
        $length = self::textLength($normalized);

        if ($blank || $hasPlaceholder) {
            return [
                'normalized' => $normalized,
                'length' => $length,
                'status' => self::LENGTH_SKIP,
                'has_placeholder' => $hasPlaceholder,
                'blank' => $blank,
                'reason' => $reason,
            ];
        }

        $status = self::LENGTH_OK;
        if ($min > 0 && $length < $min) {
            $status = self::LENGTH_TOO_SHORT;
        } elseif ($max > 0 && $length > $max) {
            $status = self::LENGTH_TOO_LONG;
        }

        return [
            'normalized' => $normalized,
            'length' => $length,
            'status' => $status,
            'has_placeholder' => false,
            'blank' => false,
            'reason' => $reason,
        ];
    }

    /**
     * Normalize a rendered meta value for duplicate detection.
     *
     * Blank-like values return an empty string so they do not create noisy duplicate groups.
     */
    public static function normalizeRenderedDuplicateKey(string $value, int $limit = 191): string
    {
        $info = self::analyzeRenderedMeta($value);
        if (!empty($info['blank'])) {
            return '';
        }

        $key = (string) ($info['normalized'] ?? '');
        if (function_exists('mb_strtolower')) {
            $key = (string) mb_strtolower($key, 'UTF-8');
        } else {
            $key = strtolower($key);
        }

        if ($limit > 0) {
            if (function_exists('mb_substr')) {
                $key = (string) mb_substr($key, 0, $limit, 'UTF-8');
            } else {
                $key = substr($key, 0, $limit);
            }
        }

        return $key;
    }

    /**
     * SQL template mode keeps real data-bearing tokens (for example %title%)
     * but ignores non-content tokens like %sep% / %page% and punctuation.
     */
    public static function isEffectivelyBlankTemplate(string $template): bool
    {
        $template = self::normalizeText($template);
        if ($template === '') {
            return true;
        }

        $template = preg_replace('/%sep%|%page%/iu', ' ', $template) ?? $template;
        $template = self::stripNoise($template, false);

        return $template === '';
    }

    private static function normalizeText(string $value): string
    {
        $value = trim(wp_strip_all_tags($value));
        $value = preg_replace('/\x{00A0}|&nbsp;/u', ' ', $value) ?? $value;
        $value = preg_replace('/\s+/u', ' ', $value) ?? $value;

        return trim($value);
    }

    private static function stripRenderedPlaceholders(string $value): string
    {
        $value = preg_replace('/%[^%\r\n]{1,200}%/u', ' ', $value) ?? $value;
        $value = preg_replace('/\{\{[^{}\r\n]{1,200}\}\}/u', ' ', $value) ?? $value;
        $value = preg_replace('/\s+/u', ' ', $value) ?? $value;

        return trim($value);
    }

    private static function stripNoise(string $value, bool $removePercent = false): string
    {
        $pattern = $removePercent
            ? '/[\s\|\-—–:»«\/\\\\·•,.;+~_=%*&^$#@!?(){}\[\]<>"\'\x{2018}\x{2019}\x{201C}\x{201D}`]+/u'
            : '/[\s\|\-—–:»«\/\\\\·•,.;+~_=*&^$#@!?(){}\[\]<>"\'\x{2018}\x{2019}\x{201C}\x{201D}`]+/u';
        $value = preg_replace($pattern, '', $value) ?? $value;

        return trim($value);
    }

    private static function containsPlaceholderSyntax(string $value): bool
    {
        if ($value === '') {
            return false;
        }

        return (bool) preg_match('/%[^%\r\n]{1,200}%/u', $value)
            || (bool) preg_match('/\{\{[^{}\r\n]{1,200}\}\}/u', $value);
    }

    private static function textLength(string $value): int
    {
        if ($value === '') {
            return 0;
        }

        if (function_exists('mb_strlen')) {
            return (int) mb_strlen($value, 'UTF-8');
        }

        return strlen($value);
    }
}
