/**
 * @module Subscription_V1 witness class
 *
 * this is where you will find the witness class that contains behavior
 * for the Subscription ibgib.
 *
 * A subscription can do two things from the caller's side:
 *
 * 1. unsubscribe
 * 2. give a boolean on whether it has been unsubscribed or not
 *
 * A subscription is created from a call to subscribe on an observable. Since
 * the observable is also a witness, we can have this subscription visit the
 * observable via `observable.witness(subscription)`. IOW, the subscription
 * witness ibgib is the argument to `witness` function.
 *
 * The observable will know that if a subscription visits it, then the
 * observable needs to release/unsubscribe that subscription.
 *
 * So since this is a light witness, we can just have two primitives be the
 * incoming argument ibgibs: unsubscribe^gib and unsubscribed^gib.
 */
import { extractErrorMsg, getUUID, pretty, clone, getIdPool, } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
import { DEFAULT_DATA_PATH_DELIMITER } from '@ibgib/helper-gib/dist/constants.mjs';
import { Factory_V1, } from '@ibgib/ts-gib/dist/V1/factory.mjs';
import { ROOT } from '@ibgib/ts-gib/dist/V1/constants.mjs';
import { GLOBAL_LOG_A_LOT } from '../../../core-constants.mjs';
import { argy_, resulty_ } from '../../../witness/witness-helper.mjs';
import { isError } from '../../error/error-helper.mjs';
import { DynamicFormFactoryBase } from '../../../witness/factory/dynamic-form-factory-base.mjs';
import { SubscriptionCmd, DEFAULT_SUBSCRIPTION_DATA_V1, DEFAULT_SUBSCRIPTION_REL8NS_V1, } from './subscription-types.mjs';
import { SubscriptionFormBuilder, getSubscriptionIb } from './subscription-helper.mjs';
import { DEFAULT_DESCRIPTION_SUBSCRIPTION } from './subscription-constants.mjs';
import { LightWitnessBase_V1 } from '../../../witness/light-witness-base-v1.mjs';
import { FALSE_GIB, TRUE_GIB } from '../../other/ibgib-constants.mjs';
import { getGib } from '@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs';
import { constantIbGib } from '../../other/ibgib-helper.mjs';
import { isObservable } from '../observable/observable-helper.mjs';
/**
 * for logging. import this constant from your project.
 */
const logalot = GLOBAL_LOG_A_LOT; // change this when you want to turn off verbose logging
/**
 * sketching...
 * under construction...
 */
export class Subscription_V1 extends LightWitnessBase_V1 {
    /**
     * Log context for convenience with logging. (Ignore if you don't want to use this.)
     */
    lc = `[${Subscription_V1.name}]`;
    /**
     * Reference to the local ibgibs service, which is one way at getting at the
     * local user space.
     */
    metaspace;
    /**
     * this is UNDEFINED if we have not yet subscribed.
     *
     * this is only true AFTER we have subscribed.
     *
     * this is FALSE after we have subscribed AND THEN UNSUBSCRIBED.
     */
    _subscribed;
    /**
     * this is not !this._subscribed!!!
     *
     * this is only true AFTER we have subscribed and then unsubscribed.
     */
    _unsubscribed;
    /**
     * this is the observable
     */
    _srcObservable;
    constructor(initialData, initialRel8ns) {
        super(initialData, initialRel8ns);
    }
    async initialize() {
        const lc = `${this.lc}[${this.initialize.name}]`;
        try {
            if (logalot) {
                console.log(`${lc} starting... (I: 8097612cda5f43e5b38249d057b65b1e)`);
            }
            await super.initialize();
            if (!this.data) {
                throw new Error(`this.data required (E: 1af73af2d17a48ada42596ad395fd99c)`);
            }
            this.data.uuid = this.instanceId;
            this.ib = getSubscriptionIb({ data: this.data, classname: 'Subscription_V1' });
            this.gib = await getGib({ ibGib: this });
        }
        catch (error) {
            console.error(`${lc} ${error.message}`);
            throw error;
        }
        finally {
            if (logalot) {
                console.log(`${lc} complete.`);
            }
        }
    }
    /**
     *
     * @param arg
     * @returns
     */
    async witness(arg) {
        const lc = `${this.lc}[${this.witness.name}]`;
        try {
            if (!this._isInitialized) {
                await this.initialized;
            }
            const { ib } = arg;
            if (!ib) {
                throw new Error(`arg.ib required (E: 1fd9aebef07b4bdaacc0cd323c8ddf76)`);
            }
            if (logalot) {
                console.log(`${lc} ib: ${ib} (I: 4de7771f477dc5b36933343b694ce223)`);
            }
            if (ib === SubscriptionCmd.unsubscribe) {
                if (logalot) {
                    console.log(`${lc} routing unsubscribe cmd (I: 5633b86d467af2aa67388abf559a3c23)`);
                }
                return this.witness_unsubscribe();
            }
            else if (ib === SubscriptionCmd.unsubscribed) {
                if (logalot) {
                    console.log(`${lc} routing unsubscribed query (I: 6e0ae61d90f1974d76bcdbb47970e723)`);
                }
                return this.witness_unsubscribed();
            }
            else if (isObservable({ ibGib: arg })) {
                if (logalot) {
                    console.log(`${lc} routing observerable (I: 165b422cda9ec3052312243dc965cf23)`);
                }
                return this.witness_observable({ ibGib: arg });
            }
            else {
                throw new Error(`unknown arg. expect unsubscribe/unsubscribed primitive ibgib or Observable ibgib. (E: 132dc7cd8f7c4afc9ec4651e2c559c74)`);
            }
        }
        catch (error) {
            console.error(`${lc} ${extractErrorMsg(error) ?? 'unknown error (E: b0bbaab21833404a9420c3d273df7f21)'}`);
            throw error;
        }
    }
    async witness_unsubscribe() {
        const lc = `${this.lc}[${this.witness_unsubscribe.name}]`;
        try {
            if (logalot) {
                console.log(`${lc} starting... (I: a3d549220ef24604956aaa2c66fff09b)`);
            }
            if (!this._subscribed) {
                console.warn(`${lc} tried to unsubscribe but this._subscribed is ${this._subscribed} (W: cdc94f59447d4011b4b4cc12ef9ec074)`);
                return ROOT; /* <<<< returns early */
            }
            if (this._unsubscribed) {
                console.warn(`{lc} this._unsubscribed already true. (W: fa287d76cec040e0978a8add5c758093)`);
                return ROOT;
            }
            if (!this._srcObservable) {
                throw new Error(`(UNEXPECTED) this._subscribed but this._srcObservable falsy? (E: 83687993b7bea299b4ba2438b530a123)`);
            }
            if (logalot) {
                console.log(`${lc} inspecting srcObservable before src.witness... (I: f15a33d10f1cae3cec6fdbdb32596823)`);
                console.dir(this._srcObservable);
            }
            await this._srcObservable.witness(this);
            if (logalot) {
                console.log(`${lc} inspecting srcObservable after src.witness... (I: f15a33d10f1cae3cec6fdbdb32596823)`);
                console.dir(this._srcObservable);
            }
            // hmm maybe being too picky here with two different 3-state flags.
            this._subscribed = false;
            this._unsubscribed = true;
            delete this._srcObservable;
            if (logalot) {
                console.log(`${lc} this._ (I: 7b1bc418a3d32757772391cad7a49523)`);
            }
            return ROOT;
        }
        catch (error) {
            console.error(`${lc} ${error.message}`);
            throw error;
        }
        finally {
            if (logalot) {
                console.log(`${lc} complete.`);
            }
        }
    }
    async witness_observable({ ibGib }) {
        const lc = `${this.lc}[${this.witness_observable.name}]`;
        try {
            if (logalot) {
                console.log(`${lc} starting... (I: faf248f372fc44a7bb0e70492e8284f9)`);
            }
            if (this._subscribed !== undefined) {
                throw new Error(`(UNEXPECTED) this._subscribed (${this._subscribed} is not undefined? (E: 09a0e926357676c2c93a173ccbe93923)`);
            }
            if (this._srcObservable) {
                throw new Error(`(UNEXPECTED) this subscription isn't subscribed but this._observable is truthy? (E: acdf5198fb2ff3c8b2db95450bd55b23)`);
            }
            // store the observable reference as this subscription's observable
            this._srcObservable = ibGib;
            if (logalot) {
                console.log(`${lc} this._srcObservable set to ibgib with ib: ${ibGib.ib} (I: 4b81d17243f60dfdcc2163fbb55e9223)`);
            }
            // we are now subscribed
            this._subscribed = true;
            // like an ack/ok/void/whatever
            return ROOT;
        }
        catch (error) {
            console.error(`${lc} ${error.message}`);
            throw error;
        }
        finally {
            if (logalot) {
                console.log(`${lc} complete.`);
            }
        }
    }
    async witness_unsubscribed() {
        const lc = `${this.lc}[${this.witness_unsubscribed.name}]`;
        try {
            if (logalot) {
                console.log(`${lc} starting... (I: 1beac551253e4c5694b63e0d36d5d5c0)`);
            }
            if (this._subscribed == undefined) {
                throw new Error(`subscription never subscribed in the first place (this._subscribed === undefined) (E: b2c1b21a0e876a4908339a976924b923)`);
            }
            if (this._subscribed && this._unsubscribed) {
                throw new Error(`(UNEXPECTED) both this._subscribed and this._unsubscribed true (E: a967e49de657a43929a74703c656ad23)`);
            }
            const result = this._unsubscribed === true ?
                TRUE_GIB : // still subscribed, so UNsubscribed is false
                FALSE_GIB; // subscribed is false, so UNsubscribed is true
            if (logalot) {
                console.log(`${lc} result: ${pretty(result)} (I: b488cf36ce39e006a81d3ad3db5b4a23)`);
            }
            return result;
        }
        catch (error) {
            console.error(`${lc} ${error.message}`);
            throw error;
        }
        finally {
            if (logalot) {
                console.log(`${lc} complete.`);
            }
        }
    }
    // #region public api
    /**
     * unsubscribe a subscriber of an observable via this subscription.
     */
    async unsubscribe() {
        const lc = `${this.lc}[${this.unsubscribe.name}]`;
        try {
            if (logalot) {
                console.log(`${lc} starting... (I: ef7f3f4787a74fcca65d761880f44594)`);
            }
            const result = await this.witness(Factory_V1.primitive({ ib: SubscriptionCmd.unsubscribe }));
            // do we really care about the result other than checking for error?
            if (!result) {
                throw new Error(`(UNEXPECTED) unsubscribe returned undefined? (E: 7013193490b94b6880f1049c36577d6f)`);
            }
            if (isError({ ibGib: result })) {
                const errorIbGib = result;
                // should we just return the error?
                throw new Error(`${errorIbGib.data.raw} (E: fca1200dccb14f84968b2462f1b4c29e)`);
            }
        }
        catch (error) {
            console.error(`${lc} ${error.message}`);
            throw error;
        }
        finally {
            if (logalot) {
                console.log(`${lc} complete.`);
            }
        }
    }
    /**
     * (async) flag to indicate if a subscription is still active or not.
     *
     * @returns `true` if we have unsubscribed/deactivated this subscription, `false` if this subscription is still active
     */
    async unsubscribed() {
        const lc = `${this.lc}[${this.unsubscribed.name}]`;
        try {
            if (logalot) {
                console.log(`${lc} starting... (I: 8ee9a93be61647d6aef6a4fa03e96dad)`);
            }
            const resRaw = await this.witness(Factory_V1.primitive({ ib: SubscriptionCmd.unsubscribed }));
            if (!resRaw) {
                throw new Error(`(UNEXPECTED) unsubscribe returned undefined? (E: 5051d7ff4a4d4bf7864c520b53f028d5)`);
            }
            const resUnsubscribed = resRaw.ib === TRUE_GIB.ib;
            if (logalot) {
                console.log(`${lc} resUnsubscribed: ${resUnsubscribed} (I: b4800166c58d5ba2ca1dccaa53de2323)`);
            }
            return resUnsubscribed;
        }
        catch (error) {
            console.error(`${lc} ${error.message}`);
            throw error;
        }
        finally {
            if (logalot) {
                console.log(`${lc} complete.`);
            }
        }
    }
    // #endregion public api
    /**
     * validates against common witness qualities.
     *
     * Override this with a call to `super.validateThis` for custom validation
     * for descending witness classes.
     *
     * @returns validation errors common to all subscription witnesses, if any errors exist.
     */
    async validateThis() {
        const lc = `${this.lc}[${this.validateThis.name}]`;
        try {
            if (logalot) {
                console.log(`${lc} starting...`);
            }
            if (!this.data) {
            }
            const errors = [
            // ...await super.validateThis(),
            // ...validateCommonSubscriptionData({ data: this.data }),
            ];
            return errors;
        }
        catch (error) {
            console.error(`${lc} ${extractErrorMsg(error)}`);
            throw error;
        }
        finally {
            if (logalot) {
                console.log(`${lc} complete.`);
            }
        }
    }
    /**
     * 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, if indeed a result ibgib is required.
     *
     * This is only useful in witnesses that have more structured
     * inputs/outputs. For those that simply accept any ibgib incoming and
     * return a primitive like ib^gib or whatever, then this is unnecessary.
     *
     * wrapper convenience to avoid long generic calls.
     */
    async resulty({ resultData, ibGibs, }) {
        const result = await resulty_({
            // ibMetadata: getSubscriptionResultMetadata({space: this}),
            resultData,
        });
        if (ibGibs) {
            result.ibGibs = ibGibs;
        }
        return result;
    }
}
/**
 * factory for random Subscription witness.
 *
 * @see {@link DynamicFormFactoryBase}
 */
export class Subscription_V1_Factory extends DynamicFormFactoryBase {
    lc = `[${Subscription_V1_Factory.name}]`;
    getName() { return Subscription_V1.name; }
    async newUp({ data, rel8ns, }) {
        const lc = `${this.lc}[${this.newUp.name}]`;
        try {
            if (logalot) {
                console.log(`${lc} starting...`);
            }
            data ??= clone(DEFAULT_SUBSCRIPTION_DATA_V1);
            data = data;
            rel8ns = rel8ns ?? DEFAULT_SUBSCRIPTION_REL8NS_V1 ? clone(DEFAULT_SUBSCRIPTION_REL8NS_V1) : undefined;
            data.uuid ||= await getUUID();
            let { classname } = data;
            const ib = getSubscriptionIb({ data });
            // subscription is a constant, so we dont need to create a
            // dependency graph or timeline or anything like that. it's just a
            // stone.
            const subscriptionIbGibDto = await constantIbGib({
                parentPrimitiveIb: `witness ${classname}`,
                ib, data, rel8ns,
            });
            // replace the newIbGib which is just ib,gib,data,rel8ns with loaded
            // witness class (that has the witness function on it)
            const witnessIbGib = new Subscription_V1(undefined, undefined);
            await witnessIbGib.loadIbGibDto(subscriptionIbGibDto);
            if (logalot) {
                console.log(`${lc} witnessDto: ${pretty(subscriptionIbGibDto)} (I: 5e41337176d1441f9a5c7b43664d43ef)`);
            }
            return { newIbGib: witnessIbGib };
        }
        catch (error) {
            console.error(`${lc} ${extractErrorMsg({ error })}`);
            throw error;
        }
        finally {
            if (logalot) {
                console.log(`${lc} complete.`);
            }
        }
    }
    async witnessToForm({ witness }) {
        const lc = `${this.lc}[${this.witnessToForm.name}]`;
        try {
            if (logalot) {
                console.log(`${lc} starting...`);
            }
            let { data } = witness;
            if (!data) {
                throw new Error(`(UNEXPECTED) witness.data falsy? (E: ef44497ff97a4e5bac004d52c4ff40eb)`);
            }
            if (logalot) {
                console.log(`${lc} data: ${pretty(data)} (I: c3f4c3d85386461aaefbe33524ff9cbd)`);
            }
            // be careful of order here because of TS type inference
            const idPool = await getIdPool({ n: 100 });
            const form = new SubscriptionFormBuilder()
                .with({ idPool })
                .name({ of: data.name, required: false, })
                .description({ of: data.description ?? DEFAULT_DESCRIPTION_SUBSCRIPTION })
                .and()
                .and()
                .uuid({ of: data.uuid, required: true })
                .classname({ of: data.classname })
                .and()
                .commonWitnessFields({ data })
                .outputForm({
                formName: 'form',
                label: 'subscription',
            });
            return Promise.resolve(form);
        }
        catch (error) {
            console.error(`${lc} ${extractErrorMsg({ error })}`);
            throw error;
        }
        finally {
            if (logalot) {
                console.log(`${lc} complete.`);
            }
        }
    }
    async formToWitness({ form }) {
        let data = clone(DEFAULT_SUBSCRIPTION_DATA_V1);
        this.patchDataFromItems({ data, items: form.items, pathDelimiter: DEFAULT_DATA_PATH_DELIMITER });
        let resWitnessIbGib = await this.newUp({ data });
        return resWitnessIbGib;
    }
}
//# sourceMappingURL=subscription-v1.mjs.map