<?php

declare(strict_types=1);

namespace App\Core;

/** Scores a visitor's question against an AiKnowledge snapshot. Pure local matching — no external API. */
final class AiMatcher
{
    /** Minimum score to trust a match over falling back to Claude or the WhatsApp deflection. */
    private const ACCEPT_SCORE = 3;

    /** @param array<string, mixed> $knowledge */
    public static function answer(string $question, array $knowledge): ?string
    {
        $queryTokens = AiKnowledge::tokens($question);
        if ($queryTokens === []) {
            return null;
        }

        $best = null;
        $bestScore = 0;
        $bestRank = -1.0;
        foreach ($knowledge['entries'] ?? [] as $entry) {
            $qHits = count(array_intersect($queryTokens, $entry['qTokens'] ?? []));
            $score = 3 * $qHits + count(array_intersect($queryTokens, $entry['bTokens'] ?? []));
            if ($score < self::ACCEPT_SCORE) {
                continue;
            }
            // Tie-break toward the more specific entry: how much of *its own*
            // question the visitor actually touched on, not just raw overlap
            // (otherwise a long FAQ sharing one word can beat an exact item match).
            $coverage = $qHits / max(1, count($entry['qTokens'] ?? []));
            $rank = $score + $coverage;
            if ($rank > $bestRank) {
                $bestRank = $rank;
                $bestScore = $score;
                $best = $entry;
            }
        }

        return $bestScore >= self::ACCEPT_SCORE ? $best['answer'] : null;
    }
}
