/**
 * @module SyncPeer_V1 witness class for base class plumbing of sync peers.
 *
 * You are expected to implement concrete peer classes for concrete architecture.
 */
import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
import { GLOBAL_LOG_A_LOT } from '../../core-constants.mjs';
import { LightWitnessBase_V1 } from '../../witness/light-witness-base-v1.mjs';
import { newupSubject } from '../../common/pubsub/subject/subject-helper.mjs';
import { authenticateContext, authorizeContext, validateContextAndSagaFrame } from '../sync-saga-context/sync-saga-context-helpers.mjs';
import { getFromSpace } from '../../witness/space/space-helper.mjs';
const logalot = GLOBAL_LOG_A_LOT;
const logalotControlDomain = false;
const lcControlDomain = '[ControlDomain]';
/**
 * Abstract witness for talking to a Sync Peer (e.g. Remote Node or Local Simulator).
 *
 * Takes a SyncSagaContext (Request) and returns a SyncSagaContext (Response).
 *
 * Implements the core "Sync Protocol Transport" logic:
 * 1. Push Context & Dependencies (Sender -> Receiver)
 * 2. Send Request (RPC)
 * 3. Pull Response & Dependencies (Receiver -> Sender)
 */
export class SyncPeer_V1 extends LightWitnessBase_V1 {
    lc = `[${SyncPeer_V1.name}]`;
    opts;
    payloadIbGibsDomainReceived$;
    get classname() {
        if (!this.data) {
            throw new Error(`(UNEXPECTED) this.data falsy? (E: 1ab1841e9338b54f3aa615fa37024826)`);
        }
        if (!this.data.classname) {
            throw new Error(`invalid peer. this.data.classname is falsy (E: b0ee28a0abb84a06588d9de7afcef826)`);
        }
        return this.data.classname;
    }
    constructor(initialData, initialRel8ns) {
        super(initialData, initialRel8ns);
    }
    async initialize() {
        this.payloadIbGibsDomainReceived$ = await newupSubject();
    }
    /**
     * base implementation just sets the opts property.
     *
     * override this in concrete classes for further initialization code
     * specific to the sender.
     *
     * @see {@link SyncPeerWitness.opts}
     */
    async initializeSender(opts) {
        const lc = `${this.lc}[${this.initializeSender.name}]`;
        try {
            if (logalot) {
                console.log(`${lc} starting... (I: 31bd5fda37c89fa37fbaf14daf5fe726)`);
            }
            this.opts = opts;
        }
        catch (error) {
            console.error(`${lc} ${extractErrorMsg(error)}`);
            throw error;
        }
        finally {
            if (logalot) {
                console.log(`${lc} complete.`);
            }
        }
    }
    setOptionalOpts(arg) {
        const lc = `${this.lc}[${this.setOptionalOpts.name}]`;
        try {
            if (logalot) {
                console.log(`${lc} starting... (I: ae5ddbd3577db267f84743175a736626)`);
            }
            if (!this.opts) {
                throw new Error(`(UNEXPECTED) this.opts falsy? (E: 0dd4d6080a6e2f31d86cf4d86e11b826)`);
            }
            if (logalot) {
                console.log(`${lc} updating opts. arg keys: ${Object.keys(arg)} (I: 452d78a558f8fc3468c8e2e68c70b226)`);
            }
            this.opts = { ...this.opts, ...arg };
        }
        catch (error) {
            console.error(`${lc} ${extractErrorMsg(error)}`);
            throw error;
        }
        finally {
            if (logalot) {
                console.log(`${lc} complete.`);
            }
        }
    }
    /**
     * Witness the synchronization context (Send/Receive).
     *
     * At this point in code, we are sending the context OUTGOING. The {@link context} should
     * have a fully populated {@link SyncSagaContextIbGib_V1.sagaFrame}
     *
     * @param arg The OUTGOING context (Request).
     * @returns The INCOMING context (Response), or undefined if failed/empty.
     */
    async witness(context) {
        const lc = `${this.lc}[${this.witness.name}]`;
        try {
            if (logalot) {
                console.log(`${lc} starting...`);
            }
            if (!this.opts) {
                throw new Error(`(UNEXPECTED) this.opts falsy? Concrete class should have initialized sender opts by now. (E: 0b9e28287318fdf8bf9f5a6886a24826)`);
            }
            // NOTE: There are two basic types of peers:
            // * local-only (peer)
            //   * this peer is both sender/receiver peer
            //   * for local merges and relatively fast spaces
            // * proxy to remote space
            //   * this peer is both sender/receiver peer
            //   * works directly with remote/outerspaces
            //   * Less efficient over-the-wire xfer due to chatiness
            // * symmetric node sender/receiver peers
            //   * separate sender/receiver classes
            //     * e.g., SyncPeerClient[Substrate]Sender_V1,
            //       SyncPeerClient[Substrate]Receiver_V1
            //   * More efficient over-the-wire xfer
            //   * Most difficult to mentally model
            //   * sender.witness does the pushing out
            //     * what the sender/initiator's coordinator has a reference to
            //   * receiver.witness does the coming in
            //     * lives on a server/remote instance with its own coordinator
            //       instance
            //     * must do its own validation/authn/authz for possible MITM
            //       issues
            // The following is a sketch where some of these may be blurred
            // persist the context, sagaframe, identity(s) ONLY in the receiver's durable space
            // if sending domain ibgibs...
            //   verify the domain ibgibs intrinsically.
            //   persist the domain ibgibs in the receiver's temp space (push all addrs?)
            //   await until all domain ibgibs are in receiver's temp space
            // process the context with the receiver's coordinator to get a new response context
            // analyze the response context
            //   validate context intrinsically
            //   validate return identity if present
            // persist the response context's dependency graph (context, saga frame and msg(s), identities)
            // if response has payload domain ibgibs,
            //   spin off...
            //     pull domain ibgibs to local temp space
            //     publish domain ibgibs to observable
            //     when all domain ibgibs are pulled, publish complete to the observable
            // return resulting context ibgib (which the caller should get BEFORE the domain ibgibs observable completes)
            // validate, authenticate, and authorize the context, sagaFrame, and identity(s)
            const validationErrors = await validateContextAndSagaFrame({ context });
            if (validationErrors.length > 0) {
                throw new Error(`invalid context received. validationErrors: ${validationErrors} (E: 8b34c875c968af29bc433138e57a7826)`);
            }
            const authenticationErrors = await authenticateContext({ context });
            if (authenticationErrors.length > 0) {
                throw new Error(`invalid context authentication. authenticationErrors: ${authenticationErrors} (E: da89da5ee1269aeb78952d475d607526)`);
            }
            const authorizationErrors = await authorizeContext({ context });
            if (authorizationErrors.length > 0) {
                throw new Error(`invalid context authorization. authorizationErrors: ${authorizationErrors} (E: 8ddc284a758cf10ba829334c1babb826)`);
            }
            // at this point, we have a valid, authenticated, authorized context
            await this.ensureReceiverTempSpace();
            // execute the transfer of control domain ibgibs & outgoing payload
            // ibgibs...
            const response = await this.sendContextRequest(context);
            if (response) {
                // at this point, all outgoing payload domain ibgibs have been sent.
                // we have received the response context, wherein the sync saga
                // frame/msg stone(s) and any keystones should be evolved. Depending
                // on the concrete implementation, if there are domain ibgibs to
                // receive, they may still be transferring. These will be published
                // to this.payloadIbGibsDomainReceived$
                return response;
            }
            else {
                // response falsy. we could be done, or this could be an error.
                if (logalot) {
                    console.log(`${lc} No response from peer. this may be normal if we just sent the very last commit frame. (I: 1855b7728c8898e1f9c001f8170ba826)`);
                }
                return undefined;
            }
        }
        catch (error) {
            console.error(`${lc} ${extractErrorMsg(error)}`);
            throw error;
        }
        finally {
            if (logalot) {
                console.log(`${lc} complete.`);
            }
        }
    }
    /**
     * Abstract accessor to get an IbGib from the Local space/store.
     * Needed for inspecting rela8ns during the graph walk.
     *
     * Default implementation simply looks in senderSpace
     */
    async getLocalIbGib(addr) {
        const lc = `${this.lc}[${this.getLocalIbGib.name}]`;
        try {
            if (logalot) {
                console.log(`${lc} starting... (I: 27b248cb9801eeb2386b71485389a826)`);
            }
            console.warn(`${lc} possibly a bottleneck here, getLocalIbGib only gets a single ibgib... (W: 2fd448a435480e6b128f6b8bcbef4826)`);
            if (!this.opts) {
                throw new Error(`(UNEXPECTED) this.opts falsy? (E: cfb9431f2fb851f24a6c88e80c3b3326)`);
            }
            const resGet = await getFromSpace({ space: this.opts.senderSpace, addr });
            if (resGet.success && resGet.ibGibs && resGet.ibGibs.length === 1) {
                const ibGib = resGet.ibGibs[0];
                return ibGib;
            }
            else {
                throw new Error(`couldn't get addr (${addr}) from local space ${this.opts.senderSpace}. reason: ${resGet.errorMsg ?? 'unknown error (E: 926ef8bf4fcc299ab89dba34ea691a26)'} (E: d8a89807e471d3f8b938ab21df44cb26)`);
            }
        }
        catch (error) {
            console.error(`${lc} ${extractErrorMsg(error)}`);
            throw error;
        }
        finally {
            if (logalot) {
                console.log(`${lc} complete.`);
            }
        }
    }
}
//# sourceMappingURL=sync-peer-v1.mjs.map