"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const bson_1 = require("bson");
const index_1 = require("./index");
class DocumentSnapshot {
    constructor(data, requestId) {
        this._data = data;
        this.requestId = requestId;
    }
    data() {
        return this._data;
    }
}
const START = 'database.startTransaction';
const COMMIT = 'database.commitTransaction';
const ABORT = 'database.abortTransaction';
const GET_DOC = 'database.getInTransaction';
const UPDATE_DOC = 'database.updateDocInTransaction';
const DELETE_DOC = 'database.deleteDocInTransaction';
class Transaction {
    constructor(db) {
        this._db = db;
        this._request = new index_1.Db.reqClass(this._db.config);
    }
    async init() {
        const res = await this._request.send(START, {});
        if (res.code) {
            throw res;
        }
        this._id = res.transactionId;
    }
    async get(documentRef) {
        const param = {
            collectionName: documentRef._coll,
            transactionId: this._id,
            _id: documentRef.id
        };
        const res = await this._request.send(GET_DOC, param);
        if (res.code)
            throw res;
        return new DocumentSnapshot(bson_1.EJSON.parse(res.data), res.requestId);
    }
    async set(documentRef, data) {
        const param = {
            collectionName: documentRef._coll,
            transactionId: this._id,
            _id: documentRef.id,
            data: bson_1.EJSON.stringify(data, { relaxed: false }),
            upsert: true
        };
        const res = await this._request.send(UPDATE_DOC, param);
        if (res.code)
            throw res;
        return Object.assign(Object.assign({}, res), { updated: bson_1.EJSON.parse(res.updated), upserted: res.upserted
                ? JSON.parse(res.upserted)
                : null });
    }
    async update(documentRef, data) {
        const param = {
            collectionName: documentRef._coll,
            transactionId: this._id,
            _id: documentRef.id,
            data: bson_1.EJSON.stringify({
                $set: data
            }, {
                relaxed: false
            })
        };
        const res = await this._request.send(UPDATE_DOC, param);
        if (res.code)
            throw res;
        return Object.assign(Object.assign({}, res), { updated: bson_1.EJSON.parse(res.updated) });
    }
    async delete(documentRef) {
        const param = {
            collectionName: documentRef._coll,
            transactionId: this._id,
            _id: documentRef.id
        };
        const res = await this._request.send(DELETE_DOC, param);
        if (res.code)
            throw res;
        return Object.assign(Object.assign({}, res), { deleted: bson_1.EJSON.parse(res.deleted) });
    }
    async commit() {
        const param = {
            transactionId: this._id
        };
        const res = await this._request.send(COMMIT, param);
        if (res.code)
            throw res;
        return res;
    }
    async rollback() {
        const param = {
            transactionId: this._id
        };
        const res = await this._request.send(ABORT, param);
        if (res.code)
            throw res;
        return res;
    }
}
exports.Transaction = Transaction;
async function startTransaction() {
    const transaction = new Transaction(this);
    await transaction.init();
    return transaction;
}
exports.startTransaction = startTransaction;
async function runTransaction(callback, times = 3) {
    if (times <= 0) {
        throw new Error('Transaction failed');
    }
    try {
        const transaction = new Transaction(this);
        await transaction.init();
        await callback(transaction);
        await transaction.commit();
    }
    catch (error) {
        console.log(error);
        return runTransaction.bind(this)(callback, --times);
    }
}
exports.runTransaction = runTransaction;
