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 { generateApolloClient } from '@deep-foundation/hasura/client.js';
import React, { createContext, useContext, useEffect, useState } from "react";
import { generateQuery, generateQueryData } from './gql/index.js';
import { useMinilinks } from './minilinks.js';
import { corePckg } from './core.js';
import { debug } from './debug.js';
import { Traveler as NativeTraveler } from './traveler.js';
import { createHelia } from 'helia';
import { strings } from '@helia/strings';
import { CID } from 'multiformats/cid';
const moduleLog = debug.extend('cyberclient');
import { serializeQuery, DeepClient, useAuthNode, useDeepNamespace, } from './client.js';
import { CyberClient } from '@cybercongress/cyber-js';
import * as cyberConfig from './cyber/config';
const log = debug.extend('log');
const error = debug.extend('error');
const corePckgIds = {};
corePckg.data.filter(l => !!l.type).forEach((l, i) => {
    corePckgIds[l.id] = i + 1;
});
;
export const model = (name, fields, models = {}) => {
    const th = models[name] = {
        out: fields,
        in: {},
    };
    for (const f in fields) {
        if (models[fields[f]]) {
            models[fields[f]].in[name] = models[fields[f]].in[name] || {};
            models[fields[f]].in[name][f] = true;
        }
    }
    for (const m in models) {
        for (const f in models[m].out) {
            if (models[m].out[f] === name) {
                th.in[m] = th.in[m] || {};
                th.in[m][f] = true;
            }
        }
    }
};
export const schema = (type, from = '', to = '', schemas = {}) => {
    if (!schemas.byType)
        schemas.byType = {};
    if (!schemas.byFrom)
        schemas.byFrom = {};
    if (!schemas.byTo)
        schemas.byTo = {};
    schemas.byType[type] = { from, to };
    if (from) {
        schemas.byFrom[from] = schemas.byFrom[from] || [];
        schemas.byFrom[from].push(type);
    }
    if (to) {
        schemas.byTo[to] = schemas.byTo[to] || [];
        schemas.byTo[to].push(type);
    }
};
export const getValue = (cid, deep) => __awaiter(void 0, void 0, void 0, function* () {
    const helia = deep.helia;
    const s = strings(helia);
    const parsed = CID.parse(cid);
    const result = yield s.get(parsed);
    return result.trim();
});
;
export function generateCyberDeepClient(options) {
    return __awaiter(this, void 0, void 0, function* () {
        const cyberClient = yield CyberClient.connect(options.config.CYBER_NODE_URL_API);
        const helia = yield createHelia();
        return new CyberDeepClient({
            cyberClient,
            helia,
            config: options.config,
            minilinks: options === null || options === void 0 ? void 0 : options.minilinks,
            namespace: options === null || options === void 0 ? void 0 : options.namespace,
        });
    });
}
const deepToCyberHash = {
    from_id: 'particle_from',
    to_id: 'particle_to',
    from: 'from',
    to: 'to',
    out: 'out',
    in: 'in',
};
const convertExpIds = (exp) => {
    let ids = [];
    if (typeof (exp) === 'string')
        ids = [exp];
    else if (typeof (exp) === 'object') {
        for (let key in exp) {
            if (typeof (exp[key]) === 'string')
                ids.push(exp[key]);
            else if (typeof (exp[key]) === 'object' && Array.isArray(exp[key]))
                ids.push(...exp[key]);
        }
    }
    return ids;
};
const splitIds = (ids) => {
    const result = { cyberlinks: [], particles: [] };
    for (let i in ids) {
        const splitted = ids[i].split(':');
        if (splitted.length === 2) {
            result.cyberlinks.push(splitted);
        }
        else {
            result.particles.push(ids[i]);
        }
    }
    return result;
};
const convertExp = (exp, mem, hash) => {
    if (typeof (exp) === 'object') {
        if (Array.isArray(exp)) {
            const result = [];
            for (const key in exp) {
                result.push(convertExp(exp[key], mem, hash));
            }
            return result;
        }
        else {
            const result = {};
            if (exp._and)
                result._and = convertExp(exp._and, mem, hash);
            if (exp._or)
                result._or = convertExp(exp._or, mem, hash);
            if (exp._not)
                result._not = convertExp(exp._not, mem, hash);
            if (!mem.table) {
                if (exp.type_id === 'cyberlink')
                    mem.table = 'cyberlinks';
                else if (exp.type_id === 'particle')
                    mem.table = 'particles';
                else if (exp.from_id || exp.to_id || exp.to || exp.from)
                    mem.table = 'cyberlinks';
                else if (exp.in || exp.out)
                    mem.table = 'particles';
                else if (exp.id) {
                    const ids = splitIds(convertExpIds(exp.id));
                    if (ids.cyberlinks.length)
                        mem.table = 'cyberlinks';
                    else if (ids.particles.length)
                        mem.table = 'particles';
                }
            }
            for (const key in exp) {
                if (key === 'in' || key === 'out') {
                    if (mem.table === 'cyberlinks')
                        throw new Error(`cyberlink cant have ${key}`);
                    result[hash[key]] = convertExp(exp[key], mem.table === 'cyberlinks' ? { table: 'particles' } : { table: 'cyberlinks' }, hash);
                }
                else if (key === 'from' || key === 'to') {
                    if (mem.table === 'particles')
                        throw new Error(`particle cant have ${key}`);
                    result[hash[key]] = convertExp(exp[key], mem.table === 'cyberlinks' ? { table: 'particles' } : { table: 'cyberlinks' }, hash);
                }
                else if (key === 'from_id' || key === 'to_id') {
                    if (mem.table === 'particles')
                        throw new Error(`particle cant have ${key}`);
                    result[hash[key]] = exp[key];
                }
                else {
                    if (mem.table === 'cyberlinks') {
                        if (key === 'id') {
                            const ids = splitIds(convertExpIds(exp.id));
                            if (ids.particles.length)
                                throw new Error(`cyberlink id cant be: ${ids.particles.join(' ')}`);
                            if (!result._and)
                                result._and = [];
                            result._and.push(...ids.cyberlinks.map(pair => ({ [hash.from_id]: pair[0], [hash.to_id]: pair[1] })));
                        }
                    }
                    if (mem.table === 'particles') {
                        if (key === 'id') {
                            const ids = splitIds(convertExpIds(exp.id));
                            if (ids.cyberlinks.length)
                                throw new Error(`particle id cant be: ${ids.cyberlinks.map(pair => pair.join(':')).join(' ')}`);
                            result.particle = exp.id;
                        }
                    }
                }
            }
            return result;
        }
    }
    else
        return exp;
};
const rewriteGettedPseudoLinksToLinks = (links, exp, deep) => __awaiter(void 0, void 0, void 0, function* () {
    var _a;
    console.log('_generateResult', 'rewrite', links, exp);
    for (let l in links) {
        const link = links[l];
        if (link.__typename === 'cyberlinks') {
            link.id = `${link.from_id}:${link.to_id}`;
            link.type_id = 'cyberlink';
        }
        else {
            link.type_id = 'particle';
            if (!deep._loadedValues[link.id]) {
                console.log('_generateResult', 'rewrite', 'getValue', link.id);
                deep._loadedValues[link.id] = yield getValue(link.id, deep);
                console.log('_generateResult', 'rewrite', 'gettedValue', link.id, deep._loadedValues[link.id]);
            }
            link.value = {
                id: link.id,
                link_id: link.id,
                value: (_a = deep._loadedValues) === null || _a === void 0 ? void 0 : _a[link.id],
            };
        }
        for (let r in ((exp === null || exp === void 0 ? void 0 : exp.return) || [])) {
            yield rewriteGettedPseudoLinksToLinks(link[r], exp === null || exp === void 0 ? void 0 : exp.return[r], deep);
        }
    }
});
export class CyberDeepClient extends DeepClient {
    constructor(options) {
        super(Object.assign(Object.assign({}, options), { apolloClient: generateApolloClient({
                path: options.config.CYBER_INDEX_HTTPS.slice(8),
                ssl: true,
                token: '',
                ws: true,
            }) }));
        this._byId = {};
        this.useDeep = useCyberDeep;
        this.DeepProvider = CyberDeepProvider;
        this.DeepContext = CyberDeepContext;
        this._loadedValues = {};
        this.cyberClient = options.cyberClient;
        this.config = options.config;
        this.helia = options.helia;
        this.accountPrefix = this.config.BECH32_PREFIX_ACC_ADDR_CYBER;
        this._generateHooks(this);
    }
    _generateQuery(exp, options) {
        const tableNamePostfix = options === null || options === void 0 ? void 0 : options.tableNamePostfix;
        const aggregate = options === null || options === void 0 ? void 0 : options.aggregate;
        const variables = options === null || options === void 0 ? void 0 : options.variables;
        const name = (options === null || options === void 0 ? void 0 : options.name) || this.defaultSelectName;
        const query = serializeQuery(exp, (options === null || options === void 0 ? void 0 : options.table) || 'links');
        const mutableOptions = { table: undefined };
        const cyberExp = convertExp(query.where, mutableOptions, deepToCyberHash);
        const queryData = generateQueryData({
            tableName: mutableOptions.table,
            tableNamePostfix: tableNamePostfix || aggregate ? '_aggregate' : '',
            returning: aggregate ? `aggregate { ${aggregate} }` : (tableName) => (tableName === 'cyberlinks' ? `from_id: ${deepToCyberHash['from_id']} to_id: ${deepToCyberHash['to_id']}` :
                tableName === 'particles' ? `id: particle` :
                    'id'),
            variables: Object.assign(Object.assign(Object.assign({}, variables), query), { where: cyberExp }),
        });
        const generatedQuery = generateQuery({
            operation: (options === null || options === void 0 ? void 0 : options.subscription) ? 'subscription' : 'query',
            queries: [
                queryData,
            ],
            name: name,
        });
        return {
            query: generatedQuery,
            queryData,
        };
    }
    _generateResult(exp, options, data) {
        return __awaiter(this, void 0, void 0, function* () {
            yield rewriteGettedPseudoLinksToLinks(data, exp, this);
            console.log('_generateResult', data, exp);
            return data;
        });
    }
    select(exp, options) {
        const _super = Object.create(null, {
            select: { get: () => super.select }
        });
        return __awaiter(this, void 0, void 0, function* () {
            return _super.select.call(this, exp, options);
        });
    }
    insert(objects, options) {
        return __awaiter(this, void 0, void 0, function* () {
            throw new Error('not implemented');
        });
    }
    ;
    update(exp, value, options) {
        return __awaiter(this, void 0, void 0, function* () {
            throw new Error('not implemented');
        });
    }
    ;
    delete(exp, options) {
        return __awaiter(this, void 0, void 0, function* () {
            throw new Error('not implemented');
        });
    }
    ;
    serial({ name, operations, returning, silent }) {
        return __awaiter(this, void 0, void 0, function* () {
            throw new Error('not implemented');
        });
    }
    ;
    reserve(count) {
        throw new Error('not implemented');
    }
    ;
    await(id, options = { results: false }) {
        return __awaiter(this, void 0, void 0, function* () {
            throw new Error('not implemented');
        });
    }
    ;
    guest(options = {}) {
        return __awaiter(this, void 0, void 0, function* () {
            throw new Error('not implemented');
        });
    }
    ;
    jwt(options) {
        return __awaiter(this, void 0, void 0, function* () {
            throw new Error('not implemented');
        });
    }
    ;
    whoami() {
        return __awaiter(this, void 0, void 0, function* () {
            throw new Error('not implemented');
        });
    }
    login(options) {
        return __awaiter(this, void 0, void 0, function* () {
            throw new Error('not implemented');
        });
    }
    ;
    logout() {
        return __awaiter(this, void 0, void 0, function* () {
            throw new Error('not implemented');
        });
    }
    ;
    can(objectIds, subjectIds, actionIds, userIds = this.linkId) {
        return __awaiter(this, void 0, void 0, function* () {
            throw new Error('not implemented');
        });
    }
    Traveler(links) {
        return new NativeTraveler(this, links);
    }
    ;
}
export function useCyberDeepGenerator({ minilinks, namespace, }) {
    const log = debug.extend(useCyberDeepGenerator.name);
    const [deep, setDeep] = useState();
    const [linkId, setLinkId] = useAuthNode();
    log({ linkId, setLinkId });
    useEffect(() => {
        const cyber = cyberConfig.CYBER;
        console.log(cyber);
        generateCyberDeepClient({
            config: cyberConfig.CYBER,
            minilinks,
            namespace,
        }).then(d => setDeep(d));
    }, []);
    log({ deep });
    return deep;
}
const CyberDeepContext = createContext(undefined);
function useCyberDeep() {
    return useContext(CyberDeepContext);
}
export function CyberDeepProvider({ minilinks: inputMinilinks, namespace, children, }) {
    const providedMinilinks = useMinilinks();
    const deep = useCyberDeepGenerator({
        minilinks: inputMinilinks || providedMinilinks,
        namespace,
    });
    useDeepNamespace(namespace, deep);
    return React.createElement(CyberDeepContext.Provider, { value: deep }, children);
}
//# sourceMappingURL=cyber.js.map