/*---------------------------------------------------------------------------------------------
 *  Copyright (c) IBM Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
import { SchemaDraft } from '../../jsonLanguageTypes';
import { isNumber } from '../../utils/objects';
import * as l10n from '@vscode/l10n';
import { DiagnosticSeverity } from 'vscode-languageserver-types';
import { Draft2019Validator } from './draft2019Validator';
import { ValidationResult, asSchema } from './baseValidator';
export class Draft2020Validator extends Draft2019Validator {
    getCurrentSchemaDraft() {
        return SchemaDraft.v2020_12;
    }
    /**
     * Keyword: prefixItems + items
     */
    validateArrayNode(node, schema, originalSchema, validationResult, matchingSchemas, options) {
        const items = (node.items ?? []);
        // prefixItems/items/contains contribute to evaluatedItems
        const evaluatedItems = validationResult.getEvaluatedItems(node);
        const prefixItems = schema.prefixItems;
        // validate prefixItems
        if (Array.isArray(prefixItems)) {
            const limit = Math.min(prefixItems.length, items.length);
            for (let i = 0; i < limit; i++) {
                const subSchema = asSchema(prefixItems[i]);
                if (!subSchema) {
                    evaluatedItems.add(i);
                    continue;
                }
                const itemValidationResult = new ValidationResult(options.isKubernetes);
                this.validateNode(items[i], subSchema, schema, itemValidationResult, matchingSchemas, options);
                validationResult.mergePropertyMatch(itemValidationResult, false);
                validationResult.mergeEnumValues(itemValidationResult);
                // mark as evaluated even if invalid (avoids duplicate unevaluatedItems noise)
                evaluatedItems.add(i);
            }
        }
        // validate remaining items against items
        const itemsKeyword = schema.items;
        const prefixLen = Array.isArray(prefixItems) ? prefixItems.length : 0;
        if (items.length > prefixLen) {
            if (itemsKeyword === false) {
                // "items": false => no items allowed beyond prefixItems
                validationResult.problems.push({
                    location: { offset: node.offset, length: node.length },
                    severity: DiagnosticSeverity.Warning,
                    message: l10n.t('Array has too many items according to schema. Expected {0} or fewer.', prefixLen),
                    source: this.getSchemaSource(schema, originalSchema),
                    schemaUri: this.getSchemaUri(schema, originalSchema),
                });
                // mark these as evaluated by "items": false (so unevaluatedItems doesn't also complain)
                for (let i = prefixLen; i < items.length; i++) {
                    evaluatedItems.add(i);
                }
            }
            else {
                const tailSchema = asSchema(itemsKeyword);
                // if items is undefined, there's no constraint for remaining items and they remain unevaluated
                if (tailSchema) {
                    for (let i = prefixLen; i < items.length; i++) {
                        const itemValidationResult = new ValidationResult(options.isKubernetes);
                        this.validateNode(items[i], tailSchema, schema, itemValidationResult, matchingSchemas, options);
                        validationResult.mergePropertyMatch(itemValidationResult, false);
                        validationResult.mergeEnumValues(itemValidationResult);
                        // mark as evaluated even if invalid (avoids duplicate unevaluatedItems noise)
                        evaluatedItems.add(i);
                    }
                }
            }
        }
        // contains enforces min/max and marks matching indices as evaluated
        this.applyContains(node, schema, originalSchema, validationResult, matchingSchemas, options);
        // generic array keywords
        this.applyArrayLength(node, schema, originalSchema, validationResult, options);
        this.applyUniqueItems(node, schema, originalSchema, validationResult);
    }
    /**
     * Draft 2020-12: contains keyword affects the unevaluatedItems keyword
     */
    applyContains(node, schema, originalSchema, validationResult, _matchingSchemas, options) {
        const containsSchema = asSchema(schema.contains);
        if (!containsSchema)
            return;
        const items = (node.items ?? []);
        const minContainsRaw = schema.minContains;
        const maxContainsRaw = schema.maxContains;
        const minContains = isNumber(minContainsRaw) ? minContainsRaw : 1;
        const maxContains = isNumber(maxContainsRaw) ? maxContainsRaw : undefined;
        let matchCount = 0;
        // ensure evaluatedItems exists
        const evaluatedItems = validationResult.getEvaluatedItems(node);
        for (let i = 0; i < items.length; i++) {
            const itemValidationResult = new ValidationResult(options.isKubernetes);
            this.validateNode(items[i], containsSchema, schema, itemValidationResult, this.getNoOpCollector(), options);
            if (!itemValidationResult.hasProblems()) {
                // items that match contains are considered evaluated
                evaluatedItems.add(i);
                matchCount++;
                if (maxContains !== undefined && matchCount > maxContains) {
                    break;
                }
            }
        }
        if (matchCount < minContains) {
            validationResult.problems.push({
                location: { offset: node.offset, length: node.length },
                severity: DiagnosticSeverity.Warning,
                message: schema.errorMessage || l10n.t('Array has too few items matching "contains". Expected {0} or more.', minContains),
                source: this.getSchemaSource(schema, originalSchema),
                schemaUri: this.getSchemaUri(schema, originalSchema),
            });
        }
        if (maxContains !== undefined && matchCount > maxContains) {
            validationResult.problems.push({
                location: { offset: node.offset, length: node.length },
                severity: DiagnosticSeverity.Warning,
                message: schema.errorMessage || l10n.t('Array has too many items matching "contains". Expected {0} or fewer.', maxContains),
                source: this.getSchemaSource(schema, originalSchema),
                schemaUri: this.getSchemaUri(schema, originalSchema),
            });
        }
    }
}
//# sourceMappingURL=draft2020Validator.js.map