import { UmbModalToken } from '../token/modal-token.js';
import { UMB_ROUTE_CONTEXT } from '../../router/route/route.context-token.js';
import { UmbControllerBase } from '../../../../libs/class-api/index.js';
import { umbDeepMerge } from '../../utils/index.js';
import { UmbId } from '../../id/index.js';
import { UmbObjectState, UmbStringState } from '../../../../libs/observable-api/index.js';
import { UmbViewController } from '../../view/index.js';
// TODO: consider splitting this into two separate handlers
export class UmbModalContext extends UmbControllerBase {
    //
    // TODO: Come up with a better name:
    #submitIsGood;
    #submitRejectReason;
    #submitIsResolved;
    #submitPromise;
    #submitResolver;
    #submitRejecter;
    #markAsResolved() {
        this.#submitResolver = undefined;
        this.#submitRejecter = undefined;
        this.#submitIsResolved = true;
    }
    #value;
    #size;
    constructor(host, modalAlias, args) {
        super(host);
        this.type = 'dialog';
        this.router = null;
        this.#size = new UmbStringState('small');
        this.size = this.#size.asObservable();
        this.key = args.modal?.key || UmbId.new();
        this.router = args.router ?? null;
        this.alias = modalAlias;
        this.view = new UmbViewController(this, modalAlias.toString());
        let title = undefined;
        let size = 'small';
        if (this.alias instanceof UmbModalToken) {
            this.type = this.alias.getDefaultModal()?.type || this.type;
            size = this.alias.getDefaultModal()?.size ?? size;
            this.element = this.alias.getDefaultModal()?.element || this.element;
            this.backdropBackground = this.alias.getDefaultModal()?.backdropBackground || this.backdropBackground;
            title = this.alias.getDefaultModal()?.title ?? undefined;
        }
        this.view.setTitle(title);
        this.type = args.modal?.type || this.type;
        size = args.modal?.size ?? size;
        this.element = args.modal?.element || this.element;
        this.backdropBackground = args.modal?.backdropBackground || this.backdropBackground;
        this.#size.setValue(size);
        const defaultData = this.alias instanceof UmbModalToken ? this.alias.getDefaultData() : undefined;
        this.data = Object.freeze(
        // If we have both data and defaultData perform a deep merge
        args.data && defaultData
            ? umbDeepMerge(args.data, defaultData)
            : // otherwise pick one of them:
                (args.data ?? defaultData));
        const initValue = args.value ?? (this.alias instanceof UmbModalToken ? this.alias.getDefaultValue() : undefined);
        this.#value = new UmbObjectState(initValue);
        this.value = this.#value.asObservable();
        // TODO: Consider if its right to use Promises, or use another event based system? Would we need to be able to cancel an event, to then prevent the closing..?
        this.#submitPromise = new Promise((resolve, reject) => {
            this.#submitResolver = resolve;
            this.#submitRejecter = reject;
        });
    }
    #activeModalPath;
    // eslint-disable-next-line @typescript-eslint/naming-convention
    _internal_setCurrentModalPath(path) {
        this.#activeModalPath = path;
    }
    // eslint-disable-next-line @typescript-eslint/naming-convention
    async _internal_removeCurrentModal() {
        const routeContext = await this.getContext(UMB_ROUTE_CONTEXT);
        routeContext?._internal_removeModalPath(this.#activeModalPath);
    }
    forceResolve() {
        // THIS should close the element no matter what.
        if (this.#submitIsGood) {
            const resolver = this.#submitResolver;
            this.#markAsResolved();
            resolver?.(this.getValue());
        }
        else {
            // We might store the reason from reject and use it here?, but I'm not sure what the real need is for this reason. [NL]
            const resolver = this.#submitRejecter;
            this.#markAsResolved();
            resolver?.(this.#submitRejectReason ?? { type: 'close' });
        }
    }
    isResolved() {
        return this.#submitIsResolved === true;
    }
    // note, this methods is private  argument is not defined correctly here, but requires to be fix by appending the OptionalSubmitArgumentIfUndefined type when newing up this class.
    /**
     * Submits this modal, returning with a value to the initiator of the modal.
     * @public
     * @memberof UmbModalContext
     */
    submit() {
        if (this.#submitIsResolved)
            return;
        if (this.router) {
            // Do not resolve jet, lets try to change the URL.
            this.#submitIsGood = true;
            this._internal_removeCurrentModal();
            return;
        }
        const resolver = this.#submitResolver;
        this.#markAsResolved();
        resolver?.(this.getValue());
        // TODO: Could we clean up this class here? (Example destroy the value state, and other things?)
    }
    /**
     * Closes this modal
     * @param reason
     * @public
     * @memberof UmbModalContext
     */
    reject(reason) {
        if (this.#submitIsResolved)
            return;
        if (this.router) {
            // Do not reject jet, lets try to change the URL.
            this.#submitIsGood = false;
            this.#submitRejectReason = reason;
            this._internal_removeCurrentModal();
            return;
        }
        const resolver = this.#submitRejecter;
        this.#markAsResolved();
        resolver?.(reason);
        // TODO: Could we clean up this class here? (Example destroy the value state, and other things?)
    }
    /**
     * Gives a Promise which will be resolved when this modal is submitted.
     * @returns {Promise<ModalValue>}
     * @public
     * @memberof UmbModalContext
     */
    async onSubmit() {
        return await this.#submitPromise;
    }
    /**
     * Gives the current value of this modal.
     * @returns {ModalValue}
     * @public
     * @memberof UmbModalContext
     */
    getValue() {
        return this.#value.getValue();
    }
    /**
     * Sets the current value of this modal.
     * @param value
     * @public
     * @memberof UmbModalContext
     */
    setValue(value) {
        this.#value.setValue(value);
    }
    /**
     * Updates the current value of this modal.
     * @param partialValue
     * @public
     * @memberof UmbModalContext
     */
    updateValue(partialValue) {
        this.#value.update(partialValue);
    }
    /**
     * Updates the size this modal.
     * @param size
     * @public
     * @memberof UmbModalContext
     */
    setModalSize(size) {
        this.#size.setValue(size);
    }
    destroy() {
        this.dispatchEvent(new CustomEvent('umb:destroy'));
        this.#value.destroy();
        this.router = null;
        this.data = undefined;
        super.destroy();
    }
}
