var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { arrayMapSync, iterableFind, iterableFirst, iterableSome, setFilterMap, withDefault, } from "collection-utils";
import { descriptionTypeAttributeKind } from "../attributes/Description.js";
import { makeNamesTypeAttributes } from "../attributes/TypeNames.js";
import { languageNamed } from "../language/All.js";
import { messageError } from "../Messages.js";
import { defined, errorMessage, panic } from "../support/Support.js";
import { CompressedJSONFromString, } from "./CompressedJSON.js";
import { TypeInference } from "./Inference.js";
function messageParseError(name, description, e) {
    return messageError("MiscJSONParseError", {
        description: withDefault(description, "input"),
        address: name,
        message: errorMessage(e),
    });
}
export class JSONInput {
    constructor(_compressedJSON) {
        this._compressedJSON = _compressedJSON;
        this.kind = "json";
        this.needIR = true;
        this.needSchemaProcessing = false;
        this._topLevels = new Map();
    }
    addSample(topLevelName, sample) {
        let topLevel = this._topLevels.get(topLevelName);
        if (topLevel === undefined) {
            topLevel = { samples: [], description: undefined };
            this._topLevels.set(topLevelName, topLevel);
        }
        topLevel.samples.push(sample);
    }
    setDescription(topLevelName, description) {
        const topLevel = this._topLevels.get(topLevelName);
        if (topLevel === undefined) {
            panic("Trying to set description for a top-level that doesn't exist");
        }
        topLevel.description = description;
    }
    addSamples(name, values, description) {
        for (const value of values) {
            this.addSample(name, value);
            if (description !== undefined) {
                this.setDescription(name, description);
            }
        }
    }
    addSource(source) {
        return __awaiter(this, void 0, void 0, function* () {
            const { name, samples, description } = source;
            try {
                const values = yield arrayMapSync(samples, (s) => __awaiter(this, void 0, void 0, function* () { return yield this._compressedJSON.parse(s); }));
                this.addSamples(name, values, description);
            }
            catch (e) {
                return messageParseError(name, description, e);
            }
        });
    }
    addSourceSync(source) {
        const { name, samples, description } = source;
        try {
            const values = samples.map((s) => this._compressedJSON.parseSync(s));
            this.addSamples(name, values, description);
        }
        catch (e) {
            messageParseError(name, description, e);
        }
    }
    singleStringSchemaSource() {
        return undefined;
    }
    addTypes(ctx, typeBuilder, inferMaps, inferEnums, fixedTopLevels) {
        return __awaiter(this, void 0, void 0, function* () {
            this.addTypesSync(ctx, typeBuilder, inferMaps, inferEnums, fixedTopLevels);
        });
    }
    addTypesSync(_ctx, typeBuilder, inferMaps, inferEnums, fixedTopLevels) {
        const inference = new TypeInference(this._compressedJSON, typeBuilder, inferMaps, inferEnums);
        for (const [name, { samples, description }] of this._topLevels) {
            const tref = inference.inferTopLevelType(makeNamesTypeAttributes(name, false), samples, fixedTopLevels);
            typeBuilder.addTopLevel(name, tref);
            if (description !== undefined) {
                const attributes = descriptionTypeAttributeKind.makeAttributes(new Set([description]));
                typeBuilder.addAttributes(tref, attributes);
            }
        }
    }
}
export function jsonInputForTargetLanguage(targetLanguage, languages, handleJSONRefs = false) {
    if (typeof targetLanguage === "string") {
        targetLanguage = defined(languageNamed(targetLanguage, languages));
    }
    const compressedJSON = new CompressedJSONFromString(targetLanguage.dateTimeRecognizer, handleJSONRefs);
    return new JSONInput(compressedJSON);
}
export class InputData {
    constructor() {
        // FIXME: Make into a Map, indexed by kind.
        // biome-ignore lint/suspicious/noExplicitAny: <explanation>
        this._inputs = new Set();
    }
    addInput(input) {
        this._inputs = this._inputs.add(input);
    }
    getOrAddInput(kind, makeInput) {
        let input = iterableFind(this._inputs, (i) => i.kind === kind);
        if (input === undefined) {
            input = makeInput();
            this.addInput(input);
        }
        return input;
    }
    addSource(kind, source, makeInput) {
        return __awaiter(this, void 0, void 0, function* () {
            const input = this.getOrAddInput(kind, makeInput);
            yield input.addSource(source);
        });
    }
    addSourceSync(kind, source, makeInput) {
        const input = this.getOrAddInput(kind, makeInput);
        input.addSourceSync(source);
    }
    addTypes(ctx, typeBuilder, inferMaps, inferEnums, fixedTopLevels) {
        return __awaiter(this, void 0, void 0, function* () {
            for (const input of this._inputs) {
                yield input.addTypes(ctx, typeBuilder, inferMaps, inferEnums, fixedTopLevels);
            }
        });
    }
    addTypesSync(ctx, typeBuilder, inferMaps, inferEnums, fixedTopLevels) {
        for (const input of this._inputs) {
            input.addTypesSync(ctx, typeBuilder, inferMaps, inferEnums, fixedTopLevels);
        }
    }
    get needIR() {
        return iterableSome(this._inputs, (i) => i.needIR);
    }
    get needSchemaProcessing() {
        return iterableSome(this._inputs, (i) => i.needSchemaProcessing);
    }
    singleStringSchemaSource() {
        const schemaStrings = setFilterMap(this._inputs, (i) => i.singleStringSchemaSource());
        if (schemaStrings.size > 1) {
            return panic("We have more than one input with a string schema source");
        }
        return iterableFirst(schemaStrings);
    }
}
