"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.createTypeIndexLogic = createTypeIndexLogic;
const rdflib_1 = require("rdflib");
const debug = __importStar(require("../util/debug"));
const ns_1 = require("../util/ns");
const utils_1 = require("../util/utils");
function createTypeIndexLogic(store, authn, profileLogic, utilityLogic) {
    const ns = ns_1.ns;
    function getRegistrations(instance, theClass) {
        return store
            .each(undefined, ns.solid('instance'), instance)
            .filter((r) => {
            return store.holds(r, ns.solid('forClass'), theClass);
        });
    }
    async function loadTypeIndexesFor(user) {
        if (!user)
            throw new Error('loadTypeIndexesFor: No user given');
        const profile = await profileLogic.loadProfile(user);
        const suggestion = suggestPublicTypeIndex(user);
        let publicTypeIndex;
        try {
            publicTypeIndex = await utilityLogic.followOrCreateLink(user, ns.solid('publicTypeIndex'), suggestion, profile);
            // eslint-disable-next-line @typescript-eslint/no-unused-vars
        }
        catch (err) {
            const message = `User ${user} has no pointer in profile to publicTypeIndex file.`;
            debug.warn(message);
        }
        const publicScopes = publicTypeIndex ? [{ label: 'public', index: publicTypeIndex, agent: user }] : [];
        let preferencesFile;
        try {
            preferencesFile = await profileLogic.silencedLoadPreferences(user);
            // eslint-disable-next-line @typescript-eslint/no-unused-vars
        }
        catch (err) {
            preferencesFile = null;
        }
        let privateScopes;
        if (preferencesFile) { // watch out - can be in either as spec was not clear.  Legacy is profile.
            // If there is a legacy one linked from the profile, use that.
            // Otherwiae use or make one linked from Preferences
            const suggestedPrivateTypeIndex = suggestPrivateTypeIndex(preferencesFile);
            let privateTypeIndex;
            try {
                privateTypeIndex = store.any(user, ns.solid('privateTypeIndex'), undefined, profile) ||
                    await utilityLogic.followOrCreateLink(user, ns.solid('privateTypeIndex'), suggestedPrivateTypeIndex, preferencesFile);
                // eslint-disable-next-line @typescript-eslint/no-unused-vars
            }
            catch (err) {
                const message = `User ${user} has no pointer in preference file to privateTypeIndex file.`;
                debug.warn(message);
            }
            privateScopes = privateTypeIndex ? [{ label: 'private', index: privateTypeIndex, agent: user }] : [];
        }
        else {
            privateScopes = [];
        }
        const scopes = publicScopes.concat(privateScopes);
        if (scopes.length === 0)
            return scopes;
        const files = scopes.map(scope => scope.index);
        try {
            await store.fetcher.load(files);
        }
        catch (err) {
            debug.warn('Problems loading type index: ', err);
        }
        return scopes;
    }
    async function loadCommunityTypeIndexes(user) {
        let preferencesFile;
        try {
            preferencesFile = await profileLogic.silencedLoadPreferences(user);
            // eslint-disable-next-line @typescript-eslint/no-unused-vars
        }
        catch (err) {
            const message = `User ${user} has no pointer in profile to preferences file.`;
            debug.warn(message);
        }
        if (preferencesFile) { // For now, pick up communities as simple links from the preferences file.
            const communities = store.each(user, ns.solid('community'), undefined, preferencesFile).concat(store.each(user, ns.solid('community'), undefined, user.doc()));
            let result = [];
            for (const org of communities) {
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                result = result.concat(await loadTypeIndexesFor(org));
            }
            return result;
        }
        return []; // No communities
    }
    async function loadAllTypeIndexes(user) {
        return (await loadTypeIndexesFor(user)).concat((await loadCommunityTypeIndexes(user)).flat());
    }
    async function getScopedAppInstances(klass, user) {
        const scopes = await loadAllTypeIndexes(user);
        let scopedApps = [];
        for (const scope of scopes) {
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            const scopedApps0 = await getScopedAppsFromIndex(scope, klass);
            scopedApps = scopedApps.concat(scopedApps0);
        }
        return scopedApps;
    }
    // This is the function signature which used to be in solid-ui/logic
    // Recommended to use getScopedAppInstances instead as it provides more information.
    //
    async function getAppInstances(klass) {
        const user = authn.currentUser();
        if (!user)
            throw new Error('getAppInstances: Must be logged in to find apps.');
        const scopedAppInstances = await getScopedAppInstances(klass, user);
        return scopedAppInstances.map(scoped => scoped.instance);
    }
    function suggestPublicTypeIndex(me) {
        return (0, rdflib_1.sym)(me.doc().dir()?.uri + 'publicTypeIndex.ttl');
    }
    // Note this one is based off the pref file not the profile
    function suggestPrivateTypeIndex(preferencesFile) {
        return (0, rdflib_1.sym)(preferencesFile.doc().dir()?.uri + 'privateTypeIndex.ttl');
    }
    /*
    * Register a new app in a type index
    * used in chat in bookmark.js (solid-ui)
    * Returns the registration object if successful else null
    */
    async function registerInTypeIndex(instance, index, theClass) {
        const registration = (0, utils_1.newThing)(index);
        const ins = [
            // See https://github.com/solid/solid/blob/main/proposals/data-discovery.md
            (0, rdflib_1.st)(registration, ns.rdf('type'), ns.solid('TypeRegistration'), index),
            (0, rdflib_1.st)(registration, ns.solid('forClass'), theClass, index),
            (0, rdflib_1.st)(registration, ns.solid('instance'), instance, index)
        ];
        try {
            await store.updater.update([], ins);
        }
        catch (err) {
            const msg = `Unable to register ${instance} in index ${index}: ${err}`;
            console.warn(msg);
            return null;
        }
        return registration;
    }
    async function deleteTypeIndexRegistration(item) {
        const reg = store.the(null, ns.solid('instance'), item.instance, item.scope.index);
        if (!reg)
            throw new Error(`deleteTypeIndexRegistration: No registration found for ${item.instance}`);
        const statements = store.statementsMatching(reg, null, null, item.scope.index);
        await store.updater.update(statements, []);
    }
    async function getScopedAppsFromIndex(scope, theClass) {
        const index = scope.index;
        const results = [];
        const registrations = store.statementsMatching(null, ns.solid('instance'), null, index)
            .concat(store.statementsMatching(null, ns.solid('instanceContainer'), null, index))
            .map(st => st.subject);
        for (const reg of registrations) {
            const klass = store.any(reg, ns.solid('forClass'), null, index);
            if (!theClass || klass.sameTerm(theClass)) {
                const instances = store.each(reg, ns.solid('instance'), null, index);
                for (const instance of instances) {
                    results.push({ instance, type: klass, scope });
                }
                const containers = store.each(reg, ns.solid('instanceContainer'), null, index);
                for (const instance of containers) {
                    await store.fetcher.load(instance);
                    results.push({ instance: (0, rdflib_1.sym)(instance.value), type: klass, scope });
                }
            }
        }
        return results;
    }
    return {
        registerInTypeIndex,
        getRegistrations,
        loadTypeIndexesFor,
        loadCommunityTypeIndexes,
        loadAllTypeIndexes,
        getScopedAppInstances,
        getAppInstances,
        suggestPublicTypeIndex,
        suggestPrivateTypeIndex,
        deleteTypeIndexRegistration,
        getScopedAppsFromIndex
    };
}
//# sourceMappingURL=typeIndexLogic.js.map