import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
import { validateIbGibIntrinsically } from '@ibgib/ts-gib/dist/V1/validate-helper.mjs';
import { GLOBAL_LOG_A_LOT } from '../../core-constants.mjs';
import { WitnessBase_V1, } from '../witness-base-v1.mjs';
import { IbGibSpaceOptionsCmd } from './space-types.mjs';
import { getSpaceResultMetadata } from './space-helper.mjs';
import { getDependencyGraph } from '../../common/other/graph-helper.mjs';
import { argy_, resulty_ } from '../../witness/witness-helper.mjs';
const logalot = GLOBAL_LOG_A_LOT;
export class SpaceBase_V1 extends WitnessBase_V1 {
    /**
     * Log context for convenience with logging. (Ignore if you don't want to use this.)
     */
    lc = `[${SpaceBase_V1.name}]`;
    /**
     * Optional cache service. hmm...
     */
    cacheSvc;
    // getSpaceIb(classname: string): string {
    //     const lc = `${this.lc}[${this.getSpaceIb.name}]`;
    //     if (!classname) {
    //         classname = this.lc?.replace('[','').replace(']','') || SpaceBase_V1.name+'_descendant';
    //         console.warn(`${lc} classname is falsy. Using ${classname}.`);
    //     }
    //     const name = this.data?.name || IBGIB_SPACE_NAME_DEFAULT;
    //     const id = this.data?.uuid || undefined;
    //     return `witness space ${classname} ${name} ${id}`;
    // }
    constructor(initialData, initialRel8ns) {
        super(initialData, initialRel8ns);
        this.initialized = this.initialize();
    }
    /**
     * In a Space, we are concerned with getting ibGibs out of and putting ibGibs into a "space".
     *
     * So in this base, we take the incoming arg and divert it multiple ways, depending on our settings.
     */
    async witnessImpl(arg) {
        const lc = `${this.lc}[${this.witnessImpl.name}]`;
        if (logalot) {
            console.log(`${lc}`);
        }
        // do the thing
        let result = await this.routeAndDoCommand({
            cmd: arg.data.cmd,
            cmdModifiers: arg.data.cmdModifiers ?? [],
            arg,
        });
        // persist the arg and result if we're configured to do so
        // if (result && this.data.persistOptsAndResultIbGibs) {
        //     try {
        //         await this.persistOptsAndResultIbGibs({arg, result});
        //     } catch (error) {
        //         const emsg = `${lc} ${error.message}`;
        //         console.error(emsg);
        //     }
        // }
        return result;
    }
    /**
     * Routes the given `cmd` to the correct handling function in the space,
     * and executes that function.
     *
     * Override this if you have custom commands to handle.
     * Check for those first, and if not among them, call this
     * via `super.doCommand(...)`. If cmd is still not found,
     * this will throw.
     */
    routeAndDoCommand({ cmd, cmdModifiers, arg, }) {
        const lc = `${this.lc}[${this.routeAndDoCommand.name}]`;
        switch (cmd) {
            case IbGibSpaceOptionsCmd.get:
                if ((cmdModifiers ?? []).length === 0) {
                    return this.get(arg);
                }
                else if (cmdModifiers.includes('can')) {
                    return this.canGet(arg);
                }
                else if (cmdModifiers.includes('latest')) {
                    if (cmdModifiers.includes('addrs')) {
                        return this.getLatestAddrs(arg);
                    }
                    else {
                        return this.getLatestIbGibs(arg);
                    }
                }
                else if (cmdModifiers.includes('tjps')) {
                    if (cmdModifiers.includes('addrs')) {
                        return this.getTjpAddrs(arg);
                    }
                    else {
                        return this.getTjpIbGibs(arg);
                    }
                }
                else if (cmdModifiers.includes('addrs')) {
                    return this.getAddrs(arg);
                }
                else if (cmdModifiers.includes('dependency-graph')) {
                    return this.getDependencyGraph(arg);
                }
                else {
                    return this.get(arg);
                }
            case IbGibSpaceOptionsCmd.put:
                if ((cmdModifiers ?? []).length === 0) {
                    return this.put(arg);
                }
                else if (cmdModifiers.includes('can')) {
                    return this.canPut(arg);
                }
                else {
                    return this.put(arg);
                }
            case IbGibSpaceOptionsCmd.delete:
                if ((cmdModifiers ?? []).length === 0) {
                    return this.delete(arg);
                }
                else if (cmdModifiers.includes('can')) {
                    return this.canDelete(arg);
                }
                else {
                    return this.delete(arg);
                }
            default:
                throw new Error(`${lc} unknown cmd: ${cmd}. cmdModifiers: ${cmdModifiers}`);
        }
    }
    get(arg) { return this.getImpl(arg); }
    put(arg) { return this.putImpl(arg); }
    delete(arg) { return this.deleteImpl(arg); }
    deleteImpl(arg) {
        const lc = `${this.lc}[${this.deleteImpl.name}]`;
        throw new Error(`${lc} not implemented in base class`);
    }
    /**
     * Get all (public?) addrs in space.
     *
     * @optional method for space.
     *
     * @returns all addresses in space (that it wants to reveal).
     */
    getAddrs(arg) { return this.getAddrsImpl(arg); }
    getAddrsImpl(arg) {
        const lc = `${this.lc}[${this.getAddrsImpl.name}]`;
        throw new Error(`${lc} not implemented in base class`);
    }
    /**
     * Get latest ibGibs for given ibGib addresses.
     *
     * Usually I pass in the tjp address(es) if I have them.
     *
     * @optional method for space.
     *
     * @returns latest ibGibs in timelines for each given ibgib address.
     */
    getLatestIbGibs(arg) { return this.getLatestIbGibsImpl(arg); }
    getLatestIbGibsImpl(arg) {
        const lc = `${this.lc}[${this.getLatestIbGibsImpl.name}]`;
        throw new Error(`${lc} not implemented in base class`);
    }
    /**
     * Get latest addrs for given ibGib(s)/address(es).
     *
     * Usually I pass in the tjp address(es) if I have them.
     *
     * @optional method for space.
     *
     * @returns latest addrs in timelines for each given ibgib address.
     */
    getLatestAddrs(arg) { return this.getLatestAddrsImpl(arg); }
    getLatestAddrsImpl(arg) {
        const lc = `${this.lc}[${this.getLatestAddrsImpl.name}]`;
        throw new Error(`${lc} not implemented in base class`);
    }
    /**
     * Get temporal junction point ibgibs for given ibgib(s) or address(es).
     *
     * @optional method for space.
     *
     * @returns tjp ibgib for each given ibgib/address.
     */
    getTjpIbGibs(arg) { return this.getTjpIbGibsImpl(arg); }
    getTjpIbGibsImpl(arg) {
        const lc = `${this.lc}[${this.getTjpIbGibsImpl.name}]`;
        throw new Error(`${lc} not implemented in base class`);
    }
    /**
     * Get temporal junction point addr(s) for given ibgib(s) or address(es).
     *
     * @optional method for space.
     *
     * @returns tjp addrs in timelines for each given ibgib/address.
     */
    getTjpAddrs(arg) { return this.getTjpAddrsImpl(arg); }
    getTjpAddrsImpl(arg) {
        const lc = `${this.lc}[${this.getTjpAddrsImpl.name}]`;
        throw new Error(`${lc} not implemented in base class`);
    }
    /**
     * Supposed to be a check on either authorization, accessibility, existence...
     *
     * @notimplementedyet
     */
    canGet(arg) { return this.canGetImpl(arg); }
    canGetImpl(arg) {
        const lc = `${this.lc}[${this.canGetImpl.name}]`;
        throw new Error(`${lc} not implemented in base class`);
    }
    /**
     * Supposed to be a check on either authorization, accessibility, existence...
     *
     * @notimplementedyet
     */
    canPut(arg) { return this.canPutImpl(arg); }
    canPutImpl(arg) {
        const lc = `${this.lc}[${this.canPutImpl.name}]`;
        throw new Error(`${lc} not implemented in base class`);
    }
    /**
     * Supposed to be a check on either authorization, accessibility, existence...
     *
     * @notimplementedyet
     */
    canDelete(arg) { return this.canPutImpl(arg); }
    canDeleteImpl(arg) {
        const lc = `${this.lc}[${this.canDeleteImpl.name}]`;
        throw new Error(`${lc} not implemented in base class`);
    }
    /**
     * gets the entire dependency graph(s) of incoming addr(s).
     *
     * NOTE: Caller will have to manually convert the result.ibGibs to a flag
     * ibgib graph to match the existing {@link getDependencyGraph} return if
     * desired. This should be a trivial conversion though, as the keys to the
     * flat graph are just the {@link getIbGibAddr} on the ibGib itself.
     *
     * @returns resulty ibgib with {@link GetDependencyGraphResultData}
     */
    getDependencyGraph(arg) { return this.getDependencyGraphImpl(arg); }
    /**
     * Default implementation of getDependencyGraph uses the helper function
     * {@link getDependencyGraph} in space-helpers.mts.
     *
     * Override this in subclasses for optimized behavior (e.g. Postgres).
     */
    async getDependencyGraphImpl(arg) {
        const lc = `${this.lc}[${this.getDependencyGraphImpl.name}]`;
        try {
            if (logalot) {
                console.log(`${lc} starting... (I: 0e5fe807a0169224781f6c2833f34826)`);
            }
            if (!arg.data) {
                throw new Error(`(UNEXPECTED) arg.data falsy? (E: 45eba816295a97a0e55f981861a03f26)`);
            }
            if ((arg.data.ibGibAddrs ?? []).length === 0) {
                throw new Error(`arg.data.ibGibAddrs is falsy/empty (E: 70adc939c7b80c9938d8fa18fd7c5826)`);
            }
            const opts = {
                space: this,
                ibGibAddrs: arg.data.ibGibAddrs,
                live: arg.data.live || true,
            };
            const graph = await getDependencyGraph(opts);
            const ibGibs = Object.values(graph);
            const resultData = {
                optsAddr: getIbGibAddr({ ibGib: arg }),
                count: ibGibs.length,
                addrs: Object.keys(graph),
            };
            return this.resulty({
                resultData: resultData,
                ibGibs,
            });
        }
        catch (error) {
            console.error(`${lc} ${extractErrorMsg(error)}`);
            throw error;
        }
        finally {
            if (logalot) {
                console.log(`${lc} complete.`);
            }
        }
    }
    /**
     * Centralized location for general argument validation for a given witness.
     *
     * In the case of spaces, this is where I usually put my common validation
     * for various cases on cmd+modifier combinations.
     *
     * @returns validation error string, empty if no errors.
     */
    async validateWitnessArg(arg) {
        const lc = `${this.lc}[${this.validateWitnessArg.name}]`;
        let errors = [];
        try {
            errors = await super.validateWitnessArg(arg);
            if (!arg.data) {
                errors.push(`arg.data required (E: 8ee544d7d88a45c6adcbc15838a283a7)`);
                return errors; // <<<< returns immediately
            }
            if (!this.data) {
                errors.push(`this.data required (E: 64f80642d7de44329355d6f907e272c8)`);
                return errors; /* <<<< returns early */
            }
            const { cmd, ibGibAddrs, } = arg.data;
            let cmdModifiers = arg.data.cmdModifiers ?? [];
            const ibGibs = arg.ibGibs;
            if (!cmd) {
                errors.push(`arg.data.cmd required (E: 72a11ee87a0d4896bcacd65a9c0284d9)`);
            }
            if (!Object.values(IbGibSpaceOptionsCmd).includes(cmd)) {
                errors.push(`unknown arg.data.cmd: ${cmd}. (E: 95282ce61e97429f8049e61ec9f14f0b)`);
            }
            const ibGibAddrsLength = ibGibAddrs?.length ?? 0;
            if (cmd === IbGibSpaceOptionsCmd.get &&
                !cmdModifiers?.includes('addrs') && // we allow get addrs to be get ALL addrs
                !cmdModifiers?.includes('latest') &&
                ibGibAddrsLength === 0) {
                errors.push(`ibGibAddrs required when cmd is ${cmd}. (E: ee55a3f60b90423cbe054f27c34ab7d5)`);
            }
            if (cmd === IbGibSpaceOptionsCmd.put) {
                if (logalot) {
                    console.log(`${lc} validate put cmd`);
                }
                const ibGibsLength = ibGibs?.length ?? 0;
                if (ibGibsLength === 0) {
                    errors.push(`ibGibs required when cmd is ${cmd}. (E: b3a422169f7344a48a1d44e7ad1ba44e)`);
                }
                else if (this.data.validateIbGibAddrsMatchIbGibs) {
                    // #region validate ibGib map to ibGibAddrs
                    if (logalot) {
                        console.log(`${lc} validateIbGibAddrsMatchIbGibs true, so doing so.`);
                    }
                    // confirm the incoming ibGibs match up with the addresses
                    // we have in `ibGibAddrs`.
                    if (ibGibsLength !== ibGibAddrsLength) {
                        errors.push(`ibGibsLength !== ibGibAddrsLength and this.data.validateIbGibAddrsMatchIbGibs is true. (E: 6c6bf824ab32443aa4d6b8bf4f8113dd)`);
                    }
                    else {
                        // lengths match, so validate ibgibs
                        if (logalot) {
                            console.log(`${lc} validateIbGibAddrsMatchIbGibs, lengths match. validating intrinsically`);
                        }
                        const ibGibAddrsCopy = ibGibAddrs.concat();
                        for (let i = 0; i < ibGibs.length; i++) {
                            const ibGib = ibGibs[i];
                            const intrinsicErrors = await validateIbGibIntrinsically({ ibGib });
                            if (intrinsicErrors?.length ?? 0 > 0) {
                                const addr = getIbGibAddr({ ibGib });
                                // for privacy it might be nice to not have the
                                // addr in the error msg, but it's too blind to
                                // only have the gib right now.
                                intrinsicErrors.forEach(x => errors.push(`(${addr}) ERROR: ${x}`));
                            }
                            else {
                                // intrinsically valid, but ensure the ibGib
                                // addr maps 1-to-1 in ibGibAddrs
                                const xAddr = getIbGibAddr({ ibGib });
                                const xIndex = ibGibAddrsCopy.indexOf(xAddr);
                                if (xIndex === -1) {
                                    errors.push(`ibGibAddrs don't map to calculated ibGib addrs. calculated Addr: (${xAddr}) (E: b21b3a7a74db43e5a8722acc97274646)`);
                                    break;
                                }
                                else {
                                    ibGibAddrsCopy.splice(xIndex, 1);
                                }
                            }
                        }
                    }
                    // #endregion validate ibGib map to ibGibAddrs
                }
            }
            return errors;
        }
        catch (error) {
            console.error(`${lc} ${extractErrorMsg(error)}`);
            throw error;
        }
        finally {
            if (errors?.length > 0) {
                console.error(`${lc} errors: ${errors}`);
            }
        }
    }
    /**
     * builds an arg ibGib.
     *
     * wrapper convenience to avoid long generic calls.
     */
    async argy({ argData, ibMetadata, noTimestamp, ibGibs, }) {
        const arg = await argy_({
            argData,
            ibMetadata,
            noTimestamp
        });
        if (ibGibs) {
            arg.ibGibs = ibGibs;
        }
        return arg;
    }
    /**
     * builds a result ibGib.
     *
     * wrapper convenience to avoid long generic calls.
     */
    async resulty({ resultData, ibGibs, }) {
        const result = await resulty_({
            ibMetadata: getSpaceResultMetadata({ space: this }),
            resultData,
        });
        if (ibGibs) {
            result.ibGibs = ibGibs;
        }
        return result;
    }
}
//# sourceMappingURL=space-base-v1.mjs.map