package org.dicio.skill.skill

import org.dicio.skill.context.SkillContext

/**
 * A skill is the component that scores input and generates output.
 *
 * @param InputData the type the data extracted from the input in [score], that [generateOutput] may
 * need to generate output
 * @param correspondingSkillInfo the [SkillInfo] this [Skill] belongs to
 * @param specificity how specific this skill is in recognizing input, see [Specificity]
 */
abstract class Skill<InputData>(
    val correspondingSkillInfo: SkillInfo,
    val specificity: Specificity,
) {

    /**
     * Matches the user input, calculates a [Score] object and extracts data from the input.
     *
     * @param ctx the [SkillContext] object to use to access resources, query information from
     * the environment (e.g. get the current locale), read settings, parse numbers, ...
     * @param input raw input from the user
     * @return a [Score] representing how closely this skill matched the user input,
     * along with any data about the input that [generateOutput] may need to generate output (will
     * actually be used only in case this skill turns out to be the best one)
     */
    abstract fun score(
        ctx: SkillContext,
        input: String,
    ): Pair<Score, InputData>

    /**
     * This will be called if this skill was deemed as the best one which could provide output for
     * what the user requested, based on its score and specificity. This method processes the input
     * data previously provided by [Skill.score] (and stored in [inputData]), and generates output
     * to speak or to show to the user. This will be called in a background thread, so if you need
     * to run something on the main thread make sure to use coroutines or other means.
     *
     * @param ctx the [SkillContext] object to use to access resources, query information from
     * the environment (e.g. get user contacts), read settings, parse numbers, ...
     * @param inputData the data previously generated by [score], that contains information about
     * the input
     */
    abstract suspend fun generateOutput(
        ctx: SkillContext,
        inputData: InputData,
    ): SkillOutput
}
