import { SchemaContext, SchemaGraphUtil, SchemaJsonLocater, SchemaMatchType } from "@itwin/ecschema-metadata";
import { IModelIncrementalSchemaLocater } from "../../IModelIncrementalSchemaLocater";
import { BriefcaseDb, IModelDb, StandaloneDb } from "../../IModelDb";
import { IModelHost } from "../../IModelHost";
import { KnownTestLocations } from "../KnownTestLocations";
import { OpenMode } from "@itwin/core-bentley";
import { IModelJsFs } from "../../IModelJsFs";
import { ProfileOptions } from "@itwin/core-common";
import { SchemaXmlFileLocater } from "@itwin/ecschema-locaters";
import { DOMParser, XMLSerializer } from "@xmldom/xmldom";
import * as path from "path";
export class TestContext {
    _iModel;
    _schemaLocater;
    _assetContext;
    constructor(iModel) {
        this._iModel = iModel;
        this._schemaLocater = this._iModel.schemaContext.locaters.find((locater) => {
            return locater instanceof IModelIncrementalSchemaLocater;
        });
        // Ideally we should not need a seperate context here to locate and locate the bisschemas from the
        // parent imodel context, but due to a bug in the incremental schema logic, we have to do this for now.
        // TODO: remove this when issue #1763 is fixed.
        this._assetContext = new SchemaContext();
        this._assetContext.addLocater(new SchemaJsonLocater((schemaName) => {
            return iModel.getSchemaProps(schemaName);
        }));
        const xmlAssetSchemaLocater = new SchemaXmlFileLocater();
        xmlAssetSchemaLocater.addSchemaSearchPath(path.join(KnownTestLocations.assetsDir, "IncrementalSchemaLocater"));
        this._assetContext.addLocater(xmlAssetSchemaLocater);
        iModel.schemaContext.addLocater(this._assetContext);
    }
    get iModel() {
        return this._iModel;
    }
    get schemaLocater() {
        return this._schemaLocater;
    }
    get schemaContext() {
        return this._iModel.schemaContext;
    }
    static async create(options) {
        if (!IModelHost.isValid) {
            await IModelHost.startup();
        }
        const iModel = options?.bimFile ?
            await this.loadIModelFile(options.bimFile) :
            await this.createIModel();
        const configuration = IModelHost.configuration;
        if (configuration) {
            const previousSetting = configuration.incrementalSchemaLoading;
            configuration.incrementalSchemaLoading = options ? options.incrementalSchemaLoading : "enabled";
            iModel.onBeforeClose.addOnce(() => {
                configuration.incrementalSchemaLoading = previousSetting;
            });
        }
        return new TestContext(iModel);
    }
    static async loadIModelFile(bimFile) {
        const pathToBriefCase = path.join(KnownTestLocations.assetsDir, bimFile);
        return BriefcaseDb.open({
            fileName: pathToBriefCase,
            readonly: true,
            key: "test-iModel",
        });
    }
    static async createIModel() {
        const testBimPath = path.join(KnownTestLocations.assetsDir, "IncrementalSchemaLocater", "test-bim.bim");
        if (IModelJsFs.existsSync(testBimPath)) {
            ~IModelJsFs.removeSync(testBimPath);
        }
        const localBim = StandaloneDb.createEmpty(testBimPath, {
            allowEdit: "true",
            rootSubject: {
                name: "IncrementalSchemaTestingDb"
            },
        });
        localBim.close();
        const nativeDb = IModelDb.openDgnDb({ path: testBimPath }, OpenMode.ReadWrite, { profile: ProfileOptions.Upgrade });
        nativeDb.saveChanges();
        nativeDb.closeFile();
        return StandaloneDb.openFile(testBimPath, OpenMode.ReadWrite);
    }
    async getSchemaNames() {
        const result = new Array();
        const sqlQuery = "SELECT Name, VersionMajor, VersionWrite, VersionMinor FROM meta.ECSchemaDef ORDER BY Name";
        const reader = this._iModel.createQueryReader(sqlQuery);
        while (await reader.step()) {
            const name = reader.current[0];
            const versionMajor = reader.current[1];
            const versionWrite = reader.current[2];
            const versionMinor = reader.current[3];
            result.push(`${name}.${versionMajor}.${versionWrite}.${versionMinor}`);
        }
        return result;
    }
    async importAssetSchema(schemaKey) {
        // If schema is already in the iModel, return it.
        if (undefined !== this.iModel.querySchemaVersion(schemaKey.name))
            return await this.schemaContext.getSchema(schemaKey);
        // Locate the schema from the assets using the schema contexts asset locaters.
        const testSchema = await this._assetContext.getSchema(schemaKey, SchemaMatchType.Exact);
        if (undefined === testSchema)
            throw new Error(`The schema '${schemaKey.name}' could not be found in the assets folder.`);
        const schemaXml = await getOrderedSchemaStrings(testSchema);
        await this._iModel.importSchemaStrings(schemaXml);
        this._iModel.saveChanges();
        if (this.iModel.isBriefcaseDb() && !this.iModel.isReadonly) {
            await this.iModel.pushChanges({ description: "import test schema" });
        }
        const schema = await this.schemaContext.getSchema(schemaKey);
        if (undefined === schema)
            throw new Error(`The schema '${schemaKey.name}' could not be found after import.`);
        if (schema.loadingController && !schema.loadingController.isComplete) {
            await schema.loadingController.wait();
        }
        return schema;
    }
    async [Symbol.asyncDispose]() {
        return this._iModel.close();
    }
}
async function getOrderedSchemaStrings(insertSchema) {
    const schemas = SchemaGraphUtil.buildDependencyOrderedSchemaList(insertSchema);
    const schemaStrings = await Promise.all(schemas.map(async (schema) => getSchemaString(schema)));
    return schemaStrings;
}
async function getSchemaString(schema) {
    // Serialize schema to the document object
    const xmlDocument = new DOMParser().parseFromString(`<?xml version="1.0" encoding="UTF-8"?>`, "application/xml");
    await schema.toXml(xmlDocument);
    const serializer = new XMLSerializer();
    const xml = serializer.serializeToString(xmlDocument);
    return xml;
}
//# sourceMappingURL=TestContext.js.map