import { buildRefsPath } from '../../batch/path.js';
import { Checker } from '../../data/index.js';
import { ObjectsPath, ReferencesPath } from '../../data/path.js';
import { Deserialize } from '../deserialize/index.js';
import { referenceToBeacons } from '../references/utils.js';
import { DataGuards, Serialize } from '../serialize/index.js';
const addContext = (builder, consistencyLevel, tenant) => {
    if (consistencyLevel) {
        builder = builder.withConsistencyLevel(consistencyLevel);
    }
    if (tenant) {
        builder = builder.withTenant(tenant);
    }
    return builder;
};
const data = (connection, name, dbVersionSupport, consistencyLevel, tenant) => {
    const objectsPath = new ObjectsPath(dbVersionSupport);
    const referencesPath = new ReferencesPath(dbVersionSupport);
    const parseObject = async (object) => {
        if (!object) {
            return {};
        }
        const obj = {
            id: object.id,
            properties: object.properties
                ? Serialize.restProperties(object.properties, object.references)
                : undefined,
        };
        // as any required below because server uses swagger object as interface{} in Go to perform type switching
        // actual types are []number and [][]number but unions don't work in go-swagger
        if (Array.isArray(object.vectors)) {
            const requiresNamedVectorsInsertFix = await dbVersionSupport.requiresNamedVectorsInsertFix();
            if (requiresNamedVectorsInsertFix.supports) {
                obj.vector = object.vectors;
                obj.vectors = { default: object.vectors };
            }
            else {
                obj.vector = object.vectors;
            }
        }
        else if (object.vectors) {
            obj.vectors = object.vectors;
        }
        return obj;
    };
    return {
        deleteById: (id) => objectsPath
            .buildDelete(id, name, consistencyLevel, tenant)
            .then((path) => connection.delete(path, undefined, false))
            .then(() => true),
        deleteMany: (where, opts) => connection
            .batch(name, consistencyLevel, tenant)
            .then((batch) => batch.withDelete({
            filters: Serialize.filtersGRPC(where),
            dryRun: opts?.dryRun,
            verbose: opts?.verbose,
        }))
            .then((reply) => Deserialize.deleteMany(reply, opts?.verbose)),
        exists: (id) => addContext(new Checker(connection, objectsPath).withId(id).withClassName(name), consistencyLevel, tenant).do(),
        insert: (obj) => Promise.all([
            objectsPath.buildCreate(consistencyLevel),
            parseObject(obj ? (DataGuards.isDataObject(obj) ? obj : { properties: obj }) : obj),
        ]).then(([path, object]) => connection
            .postReturn(path, {
            class: name,
            tenant: tenant,
            ...object,
        })
            .then((obj) => obj.id)),
        insertMany: (objects) => connection.batch(name, consistencyLevel).then(async (batch) => {
            const requiresNamedVectorsInsertFix = await dbVersionSupport.requiresNamedVectorsInsertFix();
            const serialized = await Serialize.batchObjects(name, objects, requiresNamedVectorsInsertFix.supports, tenant);
            const start = Date.now();
            const reply = await batch.withObjects({ objects: serialized.mapped });
            const end = Date.now();
            const elapsedSeconds = (end - start) / 1000;
            return Deserialize.batchObjects(reply, serialized.batch, serialized.mapped, elapsedSeconds);
        }),
        referenceAdd: (args) => referencesPath
            .build(args.fromUuid, name, args.fromProperty, consistencyLevel, tenant)
            .then((path) => Promise.all(referenceToBeacons(args.to).map((beacon) => connection.postEmpty(path, beacon))))
            .then(() => { }),
        referenceAddMany: (refs) => {
            const path = buildRefsPath(new URLSearchParams(consistencyLevel ? { consistency_level: consistencyLevel } : {}));
            const references = [];
            refs.forEach((ref) => {
                referenceToBeacons(ref.to).forEach((beacon) => {
                    references.push({
                        from: `weaviate://localhost/${name}/${ref.fromUuid}/${ref.fromProperty}`,
                        to: beacon.beacon,
                        tenant: tenant,
                    });
                });
            });
            const start = Date.now();
            return connection
                .postReturn(path, references)
                .then((res) => {
                const end = Date.now();
                const errors = {};
                res.forEach((entry, idx) => {
                    if (entry.result?.status === 'FAILED') {
                        errors[idx] = {
                            message: entry.result?.errors?.error?.[0].message
                                ? entry.result?.errors?.error?.[0].message
                                : 'unknown error',
                            reference: references[idx],
                        };
                    }
                });
                return {
                    elapsedSeconds: (end - start) / 1000,
                    errors: errors,
                    hasErrors: Object.keys(errors).length > 0,
                };
            });
        },
        referenceDelete: (args) => referencesPath
            .build(args.fromUuid, name, args.fromProperty, consistencyLevel, tenant)
            .then((path) => Promise.all(referenceToBeacons(args.to).map((beacon) => connection.delete(path, beacon, false))))
            .then(() => { }),
        referenceReplace: (args) => referencesPath
            .build(args.fromUuid, name, args.fromProperty, consistencyLevel, tenant)
            .then((path) => connection.put(path, referenceToBeacons(args.to), false)),
        replace: (obj) => Promise.all([objectsPath.buildUpdate(obj.id, name, consistencyLevel), parseObject(obj)]).then(([path, object]) => connection.put(path, {
            class: name,
            tenant: tenant,
            ...object,
        })),
        update: (obj) => Promise.all([objectsPath.buildUpdate(obj.id, name, consistencyLevel), parseObject(obj)]).then(([path, object]) => connection.patch(path, {
            class: name,
            tenant: tenant,
            ...object,
        })),
    };
};
export default data;
