<?php

namespace App\Services\Keywords;

use Illuminate\Support\Str;

class HeuristicKeywordMatcher
{
    /**
     * @param  list<array{term: string, avg_monthly_searches: int, competition: string, low_top_of_page_bid_micros?: int}>  $keywords
     * @return array{term: string, avg_monthly_searches: int, competition: string, low_top_of_page_bid_micros?: int}|null
     */
    public function match(string $productName, array $keywords): ?array
    {
        $productTokens = $this->tokens($productName);

        if ($productTokens === []) {
            return null;
        }

        $bestKeyword = null;
        $bestScore = 0;

        foreach ($keywords as $keyword) {
            $term = (string) ($keyword['term'] ?? '');

            if ($term === '') {
                continue;
            }

            $score = $this->scoreMatch($productTokens, $term);

            if ($score > $bestScore) {
                $bestScore = $score;
                $bestKeyword = $keyword;
            }
        }

        return $bestScore > 0 ? $bestKeyword : null;
    }

    /**
     * @param  list<string>  $productTokens
     */
    private function scoreMatch(array $productTokens, string $term): int
    {
        $termLower = Str::lower($term);
        $productLower = implode(' ', $productTokens);
        $score = 0;

        if (Str::contains($productLower, $termLower) || Str::contains($termLower, $productLower)) {
            $score += 10;
        }

        $termTokens = $this->tokens($term);

        foreach ($termTokens as $token) {
            if (in_array($token, $productTokens, true)) {
                $score += 3;
            }
        }

        return $score;
    }

    /**
     * @return list<string>
     */
    private function tokens(string $value): array
    {
        $tokens = preg_split('/[^a-z0-9]+/i', Str::lower($value)) ?: [];

        return array_values(array_filter($tokens, fn (string $token): bool => strlen($token) > 2));
    }
}
