import { assert } from "../../assert.js";
import { min3 } from "../../math/min3.js";
import { string_jaro_distance } from "./string_jaro_distance.js";

/**
 * Calculate the Jaro-Winkler distance between two strings.
 * Useful for sorting and fuzzy matching.
 *
 * @param {string} first The string to compare
 * @param {string} second The string to compare with
 * @returns {number} similarity score, higher value means strings are more similar, between 0 and 1
 *
 * @example
 *      string_jaro_winkler("Hello", "Hello") === 1
 *      string_jaro_winkler("apple", "orange") === 0.58
 *      string_jaro_winkler("how are you?", "are you good?") === 0.74
 */
export function string_jaro_winkler(first, second) {
    assert.isString(first, 'first');
    assert.isString(second, 'second');

    const l1 = first.length;
    const l2 = second.length;

    if (l1 === 0 && l2 === 0) {
        // special case for empty string
        return 1;
    }

    // Calculate the Jaro distance:
    const similarity = string_jaro_distance(first, second, l1, l2);

    if (similarity === 0) {
        // no similarity at all
        return 0;
    }

    // Transform to Jaro-Winkler
    // Prefix scale gives more favorable ratings to strings that share common prefixes

    const prefix_scale = 0.1;
    const prefix = getPrefix(first, second, min3(l1, l2, 4));

    return similarity + prefix * prefix_scale * (1 - similarity);
}

/**
 * Counts the number of common characters at the beginning of each word up to a maximum of {@link character_limit}
 * @param {string} a1 The first string to compare
 * @param {string} a2 The second string to compare
 * @param {number} character_limit
 * @returns {number} number of matching characters from the start of the string, up to limit
 */
function getPrefix(a1, a2, character_limit) {

    let p = 0;

    for (; p < character_limit; p++) {
        if (a1.charAt(p) !== a2.charAt(p)) {
            return p;
        }
    }

    return ++p;
}