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 Debug from 'debug';
import { generateQuery, generateQueryData, generateSerial, insertMutation } from './gql/index.js';
const debug = Debug('deeplinks:promise');
const log = debug.extend('log');
const error = debug.extend('error');
export const delay = (time) => new Promise(res => setTimeout(() => res(null), time));
export function awaitPromise(options) {
    const id = options.id;
    const timeout = options.timeout || 1000;
    const client = options.client;
    return new Promise((res, rej) => __awaiter(this, void 0, void 0, function* () {
        var _a, _b, _c, _d;
        log('promise', { id });
        if (!id) {
            log('!id', { id: options.id });
            return rej('options.id must be defined!');
        }
        let promises = null;
        try {
            while (true) {
                const result = yield client.query(generateQuery({
                    queries: [generateQueryData({ tableName: 'links', returning: `
          id type { id value } from_id to_id
          `, variables: {
                                where: {
                                    _or: [
                                        {
                                            from_id: { _eq: id },
                                            type_id: { _eq: options.Then },
                                            to: {
                                                _not: {
                                                    out: {
                                                        type_id: { _in: [options.Resolved, options.Rejected] }
                                                    }
                                                }
                                            }
                                        },
                                        {
                                            type_id: { _eq: options.Promise },
                                            in: { type_id: { _eq: options.Then }, from_id: { _eq: id } },
                                        },
                                        {
                                            from: {
                                                type_id: { _eq: options.Promise },
                                                in: { type_id: { _eq: options.Then }, from_id: { _eq: id } },
                                            },
                                            type_id: { _in: [options.Resolved, options.Rejected] },
                                        },
                                    ],
                                },
                            } })],
                    name: 'PROMISE',
                }));
                log('result', JSON.stringify(result, null, 2));
                try {
                    if (result === null || result === void 0 ? void 0 : result.errors) {
                        log('error', result === null || result === void 0 ? void 0 : result.errors);
                        return rej(result === null || result === void 0 ? void 0 : result.errors);
                    }
                    else if (result === null || result === void 0 ? void 0 : result.data) {
                        const links = (_a = result.data) === null || _a === void 0 ? void 0 : _a.q0;
                        log('data', JSON.stringify(links, null, 2));
                        if (promises === null) {
                            promises = {};
                            for (let l = 0; l < links.length; l++) {
                                const link = links[l];
                                if (((_b = link === null || link === void 0 ? void 0 : link.type) === null || _b === void 0 ? void 0 : _b.id) === options.Then)
                                    promises[link === null || link === void 0 ? void 0 : link.to_id] = true;
                            }
                        }
                        let promiseResolves = {};
                        let promiseRejects = {};
                        for (let l = 0; l < links.length; l++) {
                            const link = links[l];
                            if (((_c = link === null || link === void 0 ? void 0 : link.type) === null || _c === void 0 ? void 0 : _c.id) === options.Resolved)
                                promiseResolves[link === null || link === void 0 ? void 0 : link.from_id] = true;
                            else if (((_d = link === null || link === void 0 ? void 0 : link.type) === null || _d === void 0 ? void 0 : _d.id) === options.Rejected)
                                promiseRejects[link === null || link === void 0 ? void 0 : link.from_id] = true;
                        }
                        let promisesCount = Object.keys(promises).length;
                        let thenExists = promisesCount > 0;
                        log('analized', { thenExists });
                        if (thenExists) {
                            let thenResolvedByPromise = [];
                            let thenRejectedByPromise = [];
                            for (let key in promises) {
                                thenResolvedByPromise.push(!!promiseResolves[key]);
                                thenRejectedByPromise.push(!!promiseRejects[key]);
                            }
                            const thenResolved = thenResolvedByPromise.some(r => r);
                            const thenRejected = thenRejectedByPromise.some(r => r);
                            log('analized', { thenResolved, thenRejected });
                            const filteredLinks = links.filter(l => {
                                var _a, _b, _c, _d;
                                return (l === null || l === void 0 ? void 0 : l.id) === id ||
                                    ((_a = l === null || l === void 0 ? void 0 : l.type) === null || _a === void 0 ? void 0 : _a.id) === options.Then ||
                                    (((_b = l === null || l === void 0 ? void 0 : l.type) === null || _b === void 0 ? void 0 : _b.id) === options.Promise && promises[l === null || l === void 0 ? void 0 : l.id]) ||
                                    (((_c = l === null || l === void 0 ? void 0 : l.type) === null || _c === void 0 ? void 0 : _c.id) === options.Resolved && promises[l === null || l === void 0 ? void 0 : l.from_id]) ||
                                    (((_d = l === null || l === void 0 ? void 0 : l.type) === null || _d === void 0 ? void 0 : _d.id) === options.Rejected && promises[l === null || l === void 0 ? void 0 : l.from_id]);
                            });
                            log('filteredLinks', JSON.stringify(filteredLinks, null, 2));
                            if (thenResolved && !thenRejected) {
                                log('resolved');
                                return res(options.Results ? filteredLinks : true);
                            }
                            else if (thenRejected) {
                                log('rejected');
                                return res(options.Results ? filteredLinks : false);
                            }
                            else {
                                log('waiting');
                            }
                        }
                        else {
                            log('!then');
                            return res(options.Results ? [] : true);
                        }
                    }
                }
                catch (e) {
                    error('error', e);
                    return rej(options.Results ? e : false);
                }
                yield delay(timeout);
            }
        }
        catch (e) {
            error('error', e);
            return rej(options.Results ? e : false);
        }
    }));
}
;
export function findPromiseLink(options) {
    var _a, _b;
    return __awaiter(this, void 0, void 0, function* () {
        const result = yield options.client.query(generateQuery({
            queries: [
                generateQueryData({ tableName: 'links', returning: `id`, variables: { where: {
                            type_id: { _eq: options.Promise },
                            in: {
                                type_id: { _eq: options.Then },
                                from_id: { _eq: options.id },
                                to: {
                                    _not: {
                                        out: {
                                            type_id: { _in: [options.Resolved, options.Rejected] }
                                        }
                                    }
                                }
                            },
                        } } }),
            ],
            name: 'FIND_PROMISE',
        }));
        return (_b = (_a = result === null || result === void 0 ? void 0 : result.data) === null || _a === void 0 ? void 0 : _a.q0) === null || _b === void 0 ? void 0 : _b[0];
    });
}
export function reject(options) {
    return __awaiter(this, void 0, void 0, function* () {
        log('reject', options.id);
        const promise = yield findPromiseLink(options);
        if (promise) {
            log('rejected', yield options.client.mutate(generateSerial({
                actions: [insertMutation('links', { objects: { type_id: options.Rejected, from_id: promise.id, to_id: promise.id } })],
                name: 'REJECT',
            })));
            return true;
        }
        return false;
    });
}
export function resolve(options) {
    return __awaiter(this, void 0, void 0, function* () {
        log('resolve', options.id);
        const promise = yield findPromiseLink(options);
        if (promise) {
            log('resolved', yield options.client.mutate(generateSerial({
                actions: [insertMutation('links', { objects: { type_id: options.Resolved, from_id: promise.id, to_id: promise.id } })],
                name: 'RESOLVE',
            })));
            return true;
        }
        return false;
    });
}
//# sourceMappingURL=promise.js.map