import {FunctionComponent, Dispatch, SetStateAction} from 'react';
import {Subject} from 'rxjs';
import {Popover, PopoverProps} from './popover';
import {PopoverRef} from './popover-ref';
import {PopoverContentComponent} from './popover-content-component';

export class PopoverManager {
    constructor(
        private _list: PopoverRef[],
        private setList: Dispatch<SetStateAction<PopoverRef[]>>
    ) {}

    get list(): PopoverRef[] {
        return this._list;
    }

    set list(val: PopoverRef[]) {
        this._list = val;
        this.setList(val);
    }

    open<In extends Record<string, unknown>, Out>(
        ContentComponent: PopoverContentComponent<In, Out>,
        contentProps: In,
        popoverOptions: Omit<PopoverProps, 'onClose'>
    ) : PopoverRef<Out | unknown> {
        const {
            nestedGroupId = '',
            nestedLayerIdx = 0
        } = popoverOptions;

        // the same nested group should not have multiple popovers on the same layer
        if (nestedGroupId) {
            // close the existing popovers if they're on/above the same layer
            this.list.
                filter(elm => elm.nestedGroupId === nestedGroupId && elm.nestedLayerIdx >= nestedLayerIdx)
                .forEach(elm => elm.close())
        }

        const closeEmitter = new Subject<Out | unknown>();

        const closeHandler = (res?: Out) => {
            // close from internal function
            closeEmitter.next(res);

            // close nested popovers against specified layer
            if (nestedGroupId) {
                this.list
                    .filter(elm => elm.nestedGroupId === nestedGroupId && elm.nestedLayerIdx > nestedLayerIdx)
                    .forEach(elm => elm?.close())
            }
        }

        const PopoverComponent: FunctionComponent = () => (
            <Popover<Out>
                {...popoverOptions}
                onClose={closeHandler}
            >
                <ContentComponent
                    {...contentProps}
                    closePopover={closeHandler}
                />
            </Popover>
        );

        const ref = new PopoverRef(closeEmitter, PopoverComponent);
        // nested grouping assign
        ref.nestedGroupId = nestedGroupId;
        ref.nestedLayerIdx = nestedLayerIdx;

        const sub = closeEmitter.subscribe(() => {
            this.list = this.list.filter(elm => elm !== ref);
            sub.unsubscribe();
        });
        this.list = [...this.list, ref];

        return ref;
    }

    close<Out>(result?: Out, ref?: PopoverRef<Out>) {
        if (ref) {
            if (ref.nestedGroupId) {
                const id = ref.nestedGroupId;
                const idx = ref.nestedLayerIdx;
                this.list.filter(elm => elm.nestedGroupId === id && elm.nestedLayerIdx > idx).forEach(elm => elm?.close(result))
            } else {
                ref?.close(result);
            }
        } else {
            // default to pop the last popover instance
            [...this.list].pop()?.close(result);
        }
    }
}
