import { isEmpty, sum } from "lodash";
import matchTextWithArray from "./matchTextWithArray.js";

/**
 * Matches forms of words in the keyphrase against a given text.
 *
 * @param {Array}       keywordForms    The array with word forms of all (content) words from the keyphrase in a shape
 *                                      [ [ form1, form2, ... ], [ form1, form2, ... ] ].
 * @param {string}      text            The string to match the word forms against.
 * @param {string}      locale          The locale of the paper.
 * @param {function}    matchWordCustomHelper The helper function to match word in text.
 *
 * @returns {Object} The number and the percentage of the keyphrase words that were matched in the text by at least one form,
 * and the lowest number of positions of the matches.
 */
const findWordFormsInString = function( keywordForms, text, locale, matchWordCustomHelper ) {
	const wordNumber = keywordForms.length;
	const foundWords = Array( wordNumber );
	let positions = [];
	let matches = [];

	for ( let i = 0; i < wordNumber; i++ ) {
		const matchedKeyphrase = matchTextWithArray( text, keywordForms[ i ], locale, matchWordCustomHelper );
		foundWords[ i ] = matchedKeyphrase.count > 0 ? 1 : 0;
		positions.push( matchedKeyphrase.position );
		matches = matches.concat( matchedKeyphrase.matches );
	}
	const foundNumberOfWords = sum( foundWords );
	const result = {
		countWordMatches: foundNumberOfWords,
		percentWordMatches: 0,
		matches: matches,
	};

	if ( wordNumber > 0 ) {
		result.percentWordMatches = Math.round( foundNumberOfWords / wordNumber * 100 );
	}
	// Filtered out negative number, i.e. -1.
	positions = positions.filter( position => position >= 0 );
	result.position = positions.length === 0 ? -1 : Math.min( ...positions );

	return result;
};

/**
 * Matches forms of words in the keyphrase and in the synonyms against a given text.
 *
 * @param {Object}      topicForms       The object with word forms of all (content) words from the keyphrase and eventually synonyms,
 * comes in a shape {
 *                     keyphraseForms: [[ form1, form2, ... ], [ form1, form2, ... ]],
 *                     synonymsForms: [
 *                          [[ form1, form2, ... ], [ form1, form2, ... ]],
 *                          [[ form1, form2, ... ], [ form1, form2, ... ]],
 *                          [[ form1, form2, ... ], [ form1, form2, ... ]],
 *                     ],
 *                  }
 * @param {string}      text                    The string to match the word forms against.
 * @param {boolean}     useSynonyms             Whether to use synonyms as if it was keyphrase or not (depends on the assessment).
 * @param {string}      locale                  The locale of the paper.
 * @param {function}    matchWordCustomHelper   The language-specific helper function to match word in text.
 *
 * @returns {Object} The number and the percentage for the keyphrase words or synonyms that were matched in the text by at least one form,
 * and whether the keyphrase or a synonym was matched.
 */
const findTopicFormsInString = function( topicForms, text, useSynonyms, locale, matchWordCustomHelper ) {
	// First check if the keyword is found in the text
	let result = findWordFormsInString( topicForms.keyphraseForms, text, locale, matchWordCustomHelper );
	result.keyphraseOrSynonym = "keyphrase";

	// If a full match found with the keyword or if no synonyms are supplied or supposed to be used, return the keyphrase search result.
	if ( result.percentWordMatches === 100 || useSynonyms === false || isEmpty( topicForms.synonymsForms ) ) {
		return result;
	}

	// Collect results of matching of every synonym with the text.
	const resultsSynonyms = [];
	for ( let i = 0; i < topicForms.synonymsForms.length; i++ ) {
		const synonym = topicForms.synonymsForms[ i ];
		resultsSynonyms[ i ] = findWordFormsInString( synonym, text, locale, matchWordCustomHelper );
	}

	// Find which synonym occurred most fully.
	const foundSynonyms = resultsSynonyms.map( resultSynonyms => resultSynonyms.percentWordMatches );
	const bestSynonymIndex = foundSynonyms.indexOf( Math.max( ...foundSynonyms ) );

	// If the best synonym showed lower results than the keyword, return the keyword.
	if ( result.percentWordMatches >= resultsSynonyms[ bestSynonymIndex ].percentWordMatches ) {
		return result;
	}

	// If the best synonym showed better results than the keyword, return the synonym.
	result = resultsSynonyms[ bestSynonymIndex ];
	result.keyphraseOrSynonym = "synonym";

	return result;
};

export {
	findWordFormsInString,
	findTopicFormsInString,
};
