<?PHP
declare(strict_types = 1);

/**
 * Main tool to check if a set of strings has listed concordances towards md's controlled lists.
 */
final class ConcordanceChecker {

    // Concordance lists that are importer-specific / don't implement the expected interface.
    public const DISABLED_CONCORDANCE_LISTS = [
        'MDConcFieldRelatedWorksType',
    ];

    /** @var class-string */
    public readonly string $class;

    /**
     * Returns a list of concordance lists that can be used.
     *
     * @return array<string>
     */
    public static function getLists():array {

        $output = [];
        $files = MD_STD::scandir(__DIR__ . '/../dependencies/MDImporterConcordanceLists/src/');

        foreach ($files as $file) {

            $className = pathinfo($file, PATHINFO_FILENAME);

            // Filter out invalid options
            if (empty($className)) continue;
            if (!is_file(__DIR__ . '/../dependencies/MDImporterConcordanceLists/src/' . $className . '.php')) {
                continue;
            }
            if (in_array($className, ConcordanceChecker::DISABLED_CONCORDANCE_LISTS, true)) {
                continue;
            }

            $output[] = $className;

        }

        return $output;

    }

    /**
     * Transforms a list of array{int, string} vales into the format array<array{id: int|string, name: string}>.
     *
     * @param array<int|string, string> $keyValueList Key value list to transform.
     *
     * @return array<array{id: int|string, name: string}>
     */
    private static function _transformKeyValueListToIdNameList(array $keyValueList):array {

        $output = [];
        foreach ($keyValueList as $key => $value) {
            $output[] = [
                'id' => $key,
                'name' => $value,
            ];
        }
        return $output;

    }

    /**
     * Transforms a list of array{int, string} vales into the format array<array{id: int|string, name: string}>.
     *
     * @param array<int|string, bool> $keyValueList Key value list to transform.
     *
     * @return array<array{id: int|string, name: string}>
     */
    private static function _transformKeyValueListToIdBoolList(array $keyValueList):array {

        $output = [];
        foreach ($keyValueList as $key => $value) {
            $output[] = [
                'id' => $key,
                'name' => (string)$value,
            ];
        }
        return $output;

    }

    /**
     * Returns the valid targets for matching to the currently selected concordance list.
     *
     * @param string $lang Language.
     *
     * @return array<array{id: int|string, name: string}>
     */
    public function getMatchOptions(string $lang = "en"):array {

        $tlLoader = new MDTlLoader("concordance_checker", $lang);

        switch ($this->class) {
        case "MDConcActor":
            return self::_transformKeyValueListToIdNameList(MDEventsSet::getSortedListActors($tlLoader));
            break;
        case "MDConcCheckTypes":
            return self::_transformKeyValueListToIdNameList(MDObjectCheckType::getSortedList($tlLoader));
            break;
        case "MDConcCloserLocationTypes":
            return self::_transformKeyValueListToIdNameList(MDCloserLocationTypesSet::getSortedList($tlLoader));
            break;
        case "MDConcColors":
            return self::_transformKeyValueListToIdNameList(MDColorsSet::getSortedList($tlLoader));
            break;
        case "MDConcCurrencies":
            return self::_transformKeyValueListToIdNameList(MDCurrenciesSet::getSortedList($tlLoader));
            break;
        case "MDConcDamageTypes":
            return self::_transformKeyValueListToIdNameList(MDObjectDamageType::getSortedList($tlLoader));
            break;
        case "MDConcEntryTypes":
            return self::_transformKeyValueListToIdNameList(MDEntryTypesSet::getSortedList($tlLoader));
            break;
        case "MDConcEventTypes":
            return self::_transformKeyValueListToIdNameList(MDEventsSet::getSortedList($tlLoader));
            break;
        case "MDConcLanguages":
            return self::_transformKeyValueListToIdNameList(MDLanguagesSet::AVAILABLE_LANGUAGES);
            break;
        case "MDConcLengths":
            return self::_transformKeyValueListToIdNameList(MDLengthUnit::getSortedList($tlLoader));
            break;
        case "MDConcWeights":
            return self::_transformKeyValueListToIdNameList(MDWeightUnit::getSortedList($tlLoader));
            break;
        case "MDConcLicenses":
            $output = [];
            foreach (MDLicensesSet::AVAILABLE_LICENSES as $l) $output[] = ['id' => $l, 'name' => $l];
            return $output;
            break;
        case "MDConcLoanTypes":
            return self::_transformKeyValueListToIdNameList(MDLoanTypesSet::getSortedList($tlLoader));
            break;
        case "MDConcMarkingPosition":
            return self::_transformKeyValueListToIdNameList(MDObjectPositionsSet::getSortedList($tlLoader));
            break;
        case "MDConcMarkingType":
            return self::_transformKeyValueListToIdNameList(MDMarkingTypesSet::getSortedList($tlLoader));
            break;
        case "MDConcMeasurementTypes":
            return self::_transformKeyValueListToIdNameList(MDMeasurementType::getSortedList($tlLoader));
            break;
        case "MDConcObjectForms":
            return self::_transformKeyValueListToIdNameList(MDObjectFormSet::getSortedList($tlLoader));
            break;
        case "MDConcObjectTagRelTypes":
            return self::_transformKeyValueListToIdNameList(MDTagRelationType::getSortedList($tlLoader));
            break;
        case "MDConcOwnershipStatus":
            return self::_transformKeyValueListToIdNameList(MDObjectStatusSet::getSortedList($tlLoader));
            break;
        case "MDConcPlace":
            return self::_transformKeyValueListToIdNameList(MDEventsSet::getSortedListPlaces($tlLoader));
            break;
        case "MDConcTime":
            return self::_transformKeyValueListToIdNameList(MDEventsSet::getSortedListTimes($tlLoader));
            break;
        case "MDConcSex":
            return self::_transformKeyValueListToIdNameList(MDGenderSet::getSortedList($tlLoader));
            break;
        case "MDConcSourceTypes":
            return self::_transformKeyValueListToIdNameList(MDSourceType::getSortedList($tlLoader));
            break;
        case "MDConcTitleTypes":
            return self::_transformKeyValueListToIdNameList(MDTitleTypesSet::getSortedList($tlLoader));
            break;
        case "MDConcCertainty":
            return self::_transformKeyValueListToIdBoolList(MDConcCertainty::MAPPING);
            break;
        default:
            throw new MDpageParameterNotFromListException("Undescribed concordance list selected" . $this->class);
            break;
        }

    }

    /**
     * Ensures the selected concordance list is valid.
     *
     * @param string $class Input class.
     *
     * @return class-string
     */
    private static function _validateConcordanceList(string $class):string {

        // Ensure the input string matches a loadable class at all
        if (!\class_exists($class)) {
            throw new MDpageParameterNotFromListException("Invalid concordance list selected");
        }

        // Ensure it is a concordance list.
        $implemented = class_implements($class, true);
        if (!$implemented || !in_array('MDImporterConcordanceListInterface', $implemented, true)) {
            throw new MDpageParameterNotFromListException("Invalid concordance list selected: " . $class);
        }

        return $class;

    }

    /**
     * Checks input string.
     *
     * @param array<string> $toValidate Strings to evaluate.
     *
     * @return array<passed: array<string, mixed>, not_passed: array<string>>
     */
    public function evaluate(array $toValidate):array {

        $toValidate = array_unique($toValidate);

        $passed = $not_passed = [];
        foreach ($toValidate as $entry) {

            if (empty(trim($entry))) continue;

            try {
                $target = $this->class::getConcordanceTarget($entry);
                $passed[$entry] = $target;
            }
            catch (MDImporterMissingConcordance | MDInvalidLengthUnit | MDInvalidWeightUnit $e) {
                $not_passed[] = $entry;
            }
        }
        return ['passed' => $passed, 'not_passed' => $not_passed];

    }

    /**
     * Constructor.
     *
     * @param string $class Concordance list.
     *
     * @return void
     */
    public function __construct(string $class) {

        $this->class = self::_validateConcordanceList($class);

    }
}
