import React, {FC, ReactNode, useLayoutEffect, useMemo, useRef, useState} from "react";
import {PopupWindow, ScreenProps} from "./PopupWindow";
import {useLazyOffers, useLazyTestables, useScreenNavigation} from "./tree/hooks";
import {__} from "./globals";
import {Fields, FieldUpdateAction} from "./Fields";
import {
    componentCategories,
    ComponentCategory,
    ComponentRuntimeDataWithParentType,
    getDefaultRuntimeComponentData
} from "./tree/components";
import {cloneDeep, get, mapValues, set, values} from "lodash";
import {ToolTip} from "./ToolTip";
import {ContentWithSidebars} from "./ContentWithSidebars";
import {getCardRendererFromType, Note} from "./tree/cards";
import {AnimatePresence} from "motion/react";
import Fuse from "fuse.js";
import {
    CategorizedComponents,
    ComponentMeta,
    ComponentsMeta,
    ComponentType,
    getComponentMeta,
    getComponentMetaData,
    getComponentMetaDataField,
    getComponentsOrderedByCategory,
    hasImplementation
} from "./tree/ComponentsMeta";
import {QuantityMeta} from "./tree/cards/Quantity";
import {PopupWindowsState, PopupWindowStateContext, TestablesPopupWindowsAtom, TreeContextData} from "./tree/atoms";
import {ComponentDataType, TestableData, TestablePartial} from "./tree/store";
import {getNamePrefixedForCardRenderer} from "./tree/CardRenderers";
import {AnyProductsMeta} from "./tree/cards/AnyProducts";
import {FieldType} from "./tree/fields";
import {SelectOption} from "./tree/fields/MultiSelectSearch";
import {Tabs, TabsProps} from "./tree/fields/tabs";
import {IconicTab} from "./tree/IconicTab";
import {useAtomValue} from "jotai";
import {addIdToComponent} from "./tree/NodeHelpers";
import {Button} from "./tree/cards/Button";
import {Context} from "./tree/Context";
import {decodeHtmlEntities, getLabelForComponentType, withDecodedEntities} from "./helpers";
import {NonImplementedMessage} from "./tree/actions/NonImplementedMessage";
import {ProBadge} from "./ProBadge";
import {FilterTabs} from "./tree/FilterTabs";
import {SelectableList} from "./SelectableList";
import {ProductsMeta, ProductsOptions} from "./tree/cards/Products";
import {getSuggestedScopeById, SuggestedScopeContext, suggestedScopes} from "./tree/suggestedScopes";
import classNames from "classnames";
import {SuggestedWrapper} from "./tree/SuggestedWrapper";
import {BrandsMeta} from "./tree/cards/Brands";
import {TagsMeta} from "./tree/cards/Tags";
import {AttributesMeta} from "./tree/cards/Attributes";
import {CategoriesMeta} from "./tree/cards/Categories";
import {TextPreSuFixed} from "./tree/TextPreSuFixed";
import {EmulatedButton} from "./tree/EmulatedButton";
import {ColorWithCustomTint, groupHover, hover, resolveTint} from "./tree/Color";
import {ConditionColorPalette, defaultConditionColorPalette, getColorForComponentType} from "./tree/Colors";

export type TestablesPopupWindowProps = {
    context: PopupWindowsState['context'],
} & Pick<PopupWindowsState, 'onClose'>

const filtersMeta = ComponentsMeta.filters

export enum SelectedFiltersStateType {
    onlyBase,
    baseAndQuantity,
    baseAndExtra,
    baseQuantityAndExtra
}

const getSelectedFiltersStateType = (filters: ComponentRuntimeDataWithParentType[]): SelectedFiltersStateType => {
    if (filters.length === 1) {
        return SelectedFiltersStateType.onlyBase;
    } else {
        if (filters.length === 2) {
            if (filters[1].type === 'Quantity') {
                return SelectedFiltersStateType.baseAndQuantity;
            } else {
                return SelectedFiltersStateType.baseAndExtra;
            }
        } else {
            // we have more than 2
            if (filters[1].type === 'Quantity') {
                return SelectedFiltersStateType.baseQuantityAndExtra;
            }

            return SelectedFiltersStateType.baseAndExtra;
        }
    }

}
export const fieldsContentWidth = 114 * 4;

type ScrollableListWithFadeProps = {
    children: ReactNode;
    maxHeight: number | string;
    className?: string;
    style?: React.CSSProperties;
}

const ScrollableListWithFade: FC<ScrollableListWithFadeProps> = ({children, maxHeight, className, style}) => {
    const scrollContainerRef = useRef<HTMLDivElement>(null)
    const scrollContentRef = useRef<HTMLDivElement>(null)
    const bottomFadeRef = useRef<HTMLDivElement>(null)
    const bottomFadeVisibleRef = useRef(false)
    const animationFrameIdRef = useRef<number | null>(null)

    const updateFadeVisibility = React.useCallback(() => {
        const container = scrollContainerRef.current
        const bottomFade = bottomFadeRef.current
        if (!container || !bottomFade) {
            return
        }

        const hasVerticalOverflow = container.scrollHeight > container.clientHeight + 1
        const hasMoreItemsBelow = container.scrollTop + container.clientHeight < container.scrollHeight - 1
        const shouldShowBottomFade = hasVerticalOverflow && hasMoreItemsBelow

        if (bottomFadeVisibleRef.current === shouldShowBottomFade) {
            return
        }

        bottomFadeVisibleRef.current = shouldShowBottomFade
        bottomFade.style.opacity = shouldShowBottomFade ? '1' : '0'
    }, [])

    const scheduleFadeVisibilityUpdate = React.useCallback(() => {
        if (animationFrameIdRef.current !== null) {
            return
        }

        animationFrameIdRef.current = requestAnimationFrame(() => {
            animationFrameIdRef.current = null
            updateFadeVisibility()
        })
    }, [updateFadeVisibility])

    useLayoutEffect(() => {
        scheduleFadeVisibilityUpdate()
    }, [children, maxHeight, scheduleFadeVisibilityUpdate])

    useLayoutEffect(() => {
        const container = scrollContainerRef.current
        if (!container) {
            return
        }

        scheduleFadeVisibilityUpdate()

        const resizeObserver = new ResizeObserver(scheduleFadeVisibilityUpdate)
        resizeObserver.observe(container)
        if (scrollContentRef.current) {
            resizeObserver.observe(scrollContentRef.current)
        }

        container.addEventListener('scroll', scheduleFadeVisibilityUpdate, {passive: true})
        window.addEventListener('resize', scheduleFadeVisibilityUpdate)

        return () => {
            resizeObserver.disconnect()
            container.removeEventListener('scroll', scheduleFadeVisibilityUpdate)
            window.removeEventListener('resize', scheduleFadeVisibilityUpdate)
            if (animationFrameIdRef.current !== null) {
                cancelAnimationFrame(animationFrameIdRef.current)
                animationFrameIdRef.current = null
            }
        }
    }, [scheduleFadeVisibilityUpdate])

    return <div className="relative w-full">
        <div
            ref={scrollContainerRef}
            className={classNames('overflow-y-auto overflow-x-hidden', className)}
            style={{maxHeight, ...style}}
        >
            <div ref={scrollContentRef}>
                {children}
            </div>
        </div>
        <div
            ref={bottomFadeRef}
            className="pointer-events-none absolute bottom-0 left-0 right-0 h-20"
            style={{
                opacity: 0,
                transition: 'opacity 140ms ease-out',
                background: 'linear-gradient(to bottom, rgba(255,255,255,0) 0%, rgba(255,255,255,0.8) 45%, rgba(255,255,255,0.98) 78%, rgba(255,255,255,1) 100%)',
            }}
        />
    </div>
}

export type QuantityDecision = 'any' | 'limit' | null;
const filterTabColor = getColorForComponentType('filter')
const conditionTabColor = getColorForComponentType('condition')
const offerTabColor = getColorForComponentType('offer')
const getConditionPopupThemeClasses = (palette: ConditionColorPalette) => ({
    tabSelectedBackground: `bg-${palette}-80`,
    tabSelectedText: `text-${palette}-normal`,
    popupTitle: 'text-gray-550',//`text-${palette}-shade-200`,
    popupLink: `text-${palette}-normal hover:text-${palette}-normal`,
    popupIconBackground: `bg-${palette}-normal`,
    popupIconText: 'text-white',
    listTitleHover: `--group-hover:text-${palette}-normal`,
    listCardHoverBackground: `hover:bg-${palette}-20 hover:bg-opacity-[.20]`,
    listCardHoverBorder: `hover:border-${palette}-70 hover:border-opacity-[.80]`,
    listCardIconBackground: `bg-gray-500 group-hover:bg-${palette}-normal`,
    listCardIconText: 'text-white group-hover:text-white',
    listCardExampleBullet: `bg-${palette}-40 group-hover:bg-${palette}-normal`,
    listRowHoverBorder: `hover:border-${palette}-70`,
    listRowIcon: `group-hover:text-${palette}-normal`,
})
const defaultConditionPopupThemeClasses = getConditionPopupThemeClasses(defaultConditionColorPalette)
const filterPopupThemeClasses = {
    listTitleHover: filterTabColor.text(groupHover(100)),
    listCardHoverBackground: classNames(filterTabColor.background(hover(20)), 'hover:bg-opacity-[.20]'),
    listCardHoverBorder: classNames(filterTabColor.border(hover(70)), 'hover:border-opacity-[.80]'),
    listCardIconBackground: classNames('bg-gray-500', filterTabColor.background(groupHover(100))),
    listCardIconText: 'text-white group-hover:text-white',
    listCardExampleBullet: classNames(filterTabColor.background(40), filterTabColor.background(groupHover(100))),
    listRowHoverBorder: filterTabColor.border(hover(70)),
    listRowIcon: filterTabColor.text(groupHover(100)),
}
const offerPopupThemeClasses = {
    tabIcon: 'text-[#6498ed]',
    tabSelectedBackground: 'bg-[#cddef9]',
    tabSelectedText: 'text-[#6498ed]',
    popupTitle: 'text-[#6498ed]',
    popupLink: 'text-[#6498ed] hover:text-[#6498ed]',
    popupIconBackground: 'bg-[#6498ed]',
    popupIconText: 'text-white',
    listTitleHover: 'group-hover:text-[#6498ed]',
    listCardHoverBackground: 'hover:bg-[#eef4fd] hover:bg-opacity-[.20]',
    listCardHoverBorder: 'hover:border-[#6498ed] hover:border-opacity-[.80]',
    listCardIconBackground: 'bg-gray-500 group-hover:bg-[#6498ed]',
    listCardIconText: 'text-white group-hover:text-white',
    listCardExampleBullet: 'bg-[#a4c3f4] group-hover:bg-[#367ae8]',
    listRowHoverBorder: 'hover:border-[#6498ed]',
    listRowIcon: 'group-hover:text-[#6498ed]',
}
const offerTabThemeColor: ColorWithCustomTint = {
    color: offerTabColor,
    tint: (color, type, context) => {
        switch (context) {
            case 'iconic-tab.selected.background':
                return offerPopupThemeClasses.tabSelectedBackground
            case 'iconic-tab.selected.text':
                return offerPopupThemeClasses.tabSelectedText
            default:
                return undefined
        }
    }
}
const conditionTabThemeColor: ColorWithCustomTint = {
    color: conditionTabColor,
    tint: (color, type, context) => {
        switch (context) {
            case 'iconic-tab.selected.background':
                return defaultConditionPopupThemeClasses.tabSelectedBackground
            case 'iconic-tab.selected.text':
                return defaultConditionPopupThemeClasses.tabSelectedText
            default:
                return undefined
        }
    }
}
const OfferIcon = ({className}: {className?: string}) => (
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className={className}>
        <path fillRule="evenodd" d="M12 1.5c-1.921 0-3.816.111-5.68.327-1.497.174-2.57 1.46-2.57 2.93V21.75a.75.75 0 0 0 1.029.696l3.471-1.388 3.472 1.388a.75.75 0 0 0 .556 0l3.472-1.388 3.471 1.388a.75.75 0 0 0 1.029-.696V4.757c0-1.47-1.073-2.756-2.57-2.93A49.255 49.255 0 0 0 12 1.5Zm3.53 7.28a.75.75 0 0 0-1.06-1.06l-6 6a.75.75 0 1 0 1.06 1.06l6-6ZM8.625 9a1.125 1.125 0 1 1 2.25 0 1.125 1.125 0 0 1-2.25 0Zm5.625 3.375a1.125 1.125 0 1 0 0 2.25 1.125 1.125 0 0 0 0-2.25Z" clipRule="evenodd" />
    </svg>
);
const PreconditionsIcon = ({className}: {className?: string}) => (
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className={className}>
        <path d="M10.5 1.875a1.125 1.125 0 0 1 2.25 0v8.219c.517.162 1.02.382 1.5.659V3.375a1.125 1.125 0 0 1 2.25 0v10.937a4.505 4.505 0 0 0-3.25 2.373 8.963 8.963 0 0 1 4-.935A.75.75 0 0 0 18 15v-2.266a3.368 3.368 0 0 1 .988-2.37 1.125 1.125 0 0 1 1.591 1.59 1.118 1.118 0 0 0-.329.79v3.006h-.005a6 6 0 0 1-1.752 4.007l-1.736 1.736a6 6 0 0 1-4.242 1.757H10.5a7.5 7.5 0 0 1-7.5-7.5V6.375a1.125 1.125 0 0 1 2.25 0v5.519c.46-.452.965-.832 1.5-1.141V3.375a1.125 1.125 0 0 1 2.25 0v6.526c.495-.1.997-.151 1.5-.151V1.875Z"/>
    </svg>
);
const PredatesIcon = ({className}: {className?: string}) => (
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className={className}>
        <path fillRule="evenodd" d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25ZM12.75 6a.75.75 0 0 0-1.5 0v6c0 .414.336.75.75.75h4.5a.75.75 0 0 0 0-1.5h-3.75V6Z" clipRule="evenodd"/>
    </svg>
);
const tabs: TabsProps['tabs'] & { title: string | ((suggestedScope: string) => string | ReactNode) , description?: ReactNode }[] = [
    {
        id: 'filter' as TestableData['testableType'],
        title: (suggestedScope: string) => {
            if (!suggestedScope) {
                return __('What Kind of Products?')
            } else if (suggestedScope === 'bogo') {
                return __('What Kind of Products Must the Customer Buy?')
            } else if (suggestedScope === 'item_discounts') {
                return __('What Kind of Products to Discount?')
            } else if (suggestedScope === 'cart_discounts') {
                return __('What Kind of Products Must the Customer Buy to Qualify for the Cart Discount?'/*'Does the customer need to have specific products in their cart to be eligible for the discount?'*/)
            }
        },
        description: (suggestedScope: string) => {
            if (!suggestedScope) {
                return <div className="space-y-4 mt-2">
                    {true && <div>{__('These products will be filtered from the cart and passed to your selected offers.')}</div>}
                    {false && <FilterTabs/>}
                </div>
            } else if (suggestedScope === 'bogo') {
                return __('The products that the customer must buy to be eligible for the offer. These products will not be discounted but will be used to trigger offer. These represent the "buy" part of the "Buy One Get One" offer.')
            } else if (suggestedScope === 'item_discounts') {
                return __('These are the products that will be discounted.')
            }

        },
        label: isSelected => <IconicTab
            icon={<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
                       className={classNames('w-4 h-4', filterTabColor.text(10))}>
                <path fillRule="evenodd"
                      d="M3.792 2.938A49.069 49.069 0 0 1 12 2.25c2.797 0 5.54.236 8.209.688a1.857 1.857 0 0 1 1.541 1.836v1.044a3 3 0 0 1-.879 2.121l-6.182 6.182a1.5 1.5 0 0 0-.439 1.061v2.927a3 3 0 0 1-1.658 2.684l-1.757.878A.75.75 0 0 1 9.75 21v-5.818a1.5 1.5 0 0 0-.44-1.06L3.13 7.938a3 3 0 0 1-.879-2.121V4.774c0-.897.64-1.683 1.542-1.836Z"
                      clipRule="evenodd"/>
            </svg>}
            label={__('Filters')}
            isSelected={isSelected}
            color={filterTabColor}
        />,
    },
    {
        id: 'condition' as TestableData['testableType'],
        title: __('Add Preconditions'),
        description: __('Add one or more conditions that need to pass before the coupon and offers can be applied.'),
        label: isSelected => <IconicTab
            icon={<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
                       className={classNames('w-4 h-4', conditionTabColor.text(10))}>
                <path
                    d="M10.5 1.875a1.125 1.125 0 0 1 2.25 0v8.219c.517.162 1.02.382 1.5.659V3.375a1.125 1.125 0 0 1 2.25 0v10.937a4.505 4.505 0 0 0-3.25 2.373 8.963 8.963 0 0 1 4-.935A.75.75 0 0 0 18 15v-2.266a3.368 3.368 0 0 1 .988-2.37 1.125 1.125 0 0 1 1.591 1.59 1.118 1.118 0 0 0-.329.79v3.006h-.005a6 6 0 0 1-1.752 4.007l-1.736 1.736a6 6 0 0 1-4.242 1.757H10.5a7.5 7.5 0 0 1-7.5-7.5V6.375a1.125 1.125 0 0 1 2.25 0v5.519c.46-.452.965-.832 1.5-1.141V3.375a1.125 1.125 0 0 1 2.25 0v6.526c.495-.1.997-.151 1.5-.151V1.875Z"/>
            </svg>}
            label={__('Conditions')}
            isSelected={isSelected}
            color={conditionTabThemeColor}
        />,
    },
    {
        id: 'offer' as ComponentDataType,
        title: __('Add an offer'),
        label: isSelected => <IconicTab
            icon={<OfferIcon className={classNames('w-4 h-4', offerPopupThemeClasses.tabIcon)} />}
            label={__('Offers')}
            isSelected={isSelected}
            color={offerTabThemeColor}
        />,
    },
]
export const isConfigurable = (filterMeta: ComponentMeta | undefined, context: PopupWindowStateContext<any>['scope'] | string = ''): boolean => {
    if (!filterMeta) {
        return false
    }

    let configurable = filterMeta.configurable;

    if (typeof configurable === 'function') {
        configurable = configurable(context)
    }

    return configurable !== false;
}
export const TestablesPopupWindow: FC<TestablesPopupWindowProps> = ({context, onClose}) => {
    const TestablesPopupWindows = useAtomValue(TestablesPopupWindowsAtom)
    const windowId = context.id
    const [isOpen, setIsOpen] = useState(true)
    const hasClosedRef = useRef(false)
    const defaultScreenId = 1//2
    const popupWindowZIndex = 1000000
    // @ts-ignore
    const [currentScreenId, setCurrentScreenId, {next, prev}] = useScreenNavigation(defaultScreenId)
    // null means no decision has been made
    const [filters, setFilters] = useState<(ComponentRuntimeDataWithParentType)[]>([/*{
        id: 'khgkgk',
        parentType: 'offers',
        type: 'BuyXGetY',
        options: {
            mode: 'products',
            whenNotInCart: {
                notification: {
                    button: {
                        url: {
                            type: 'smart',
                        }
                    }
                }
            }
        }
        /!*{
                    name: '', // the html name of the field
                    type: 'text',
                    comparisonTypes: {
                        text: {
                            expectedValue: '',
                            comparisonType: 'is'
                        },
                        number: {
                            quantity: {
                                type: 'equals',
                                amount: 0,
                                range: {
                                    minimum: 0,
                                    maxmimum: 0
                                },
                            }
                        },
                        exists: true
                    }
                }
        *!/
    }*/])
    const [suggestedScope, setSuggestedScope] = useState<string | undefined>(context.data?.extraData?.suggestedScope)
    type QuantityChoice = 'no-quantity' | 'with-quantity'
    const [qtyChoice, setQtyChoice] = useState<QuantityChoice>('no-quantity')
    // this is for READ ONLY operations, mutable operations should be done with the updateTestables() from the store!
    const testables = useLazyTestables()
    const offers = useLazyOffers()
    const [testableType, setTestableType] = useState<ComponentDataType>(context.data.componentType as ComponentDataType)
    const popupColor = getColorForComponentType(testableType)
    const popupConditionPalette = context.data?.conditionColorPalette || defaultConditionColorPalette
    const conditionPopupThemeClasses = useMemo(
        () => getConditionPopupThemeClasses(popupConditionPalette),
        [popupConditionPalette]
    )
    const popupThemeColor = useMemo<ColorWithCustomTint | typeof popupColor>(() => {
        if (testableType === 'filter') {
            return {
                color: popupColor,
                tint: (color, type, context) => {
                    switch (context) {
                        case 'testables-popup.list.title-hover':
                            return filterPopupThemeClasses.listTitleHover
                        case 'testables-popup.list.card.hover-background':
                            return filterPopupThemeClasses.listCardHoverBackground
                        case 'testables-popup.list.card.hover-border':
                            return filterPopupThemeClasses.listCardHoverBorder
                        case 'testables-popup.list.card.icon.background':
                            return filterPopupThemeClasses.listCardIconBackground
                        case 'testables-popup.list.card.icon.text':
                            return filterPopupThemeClasses.listCardIconText
                        case 'testables-popup.list.card.example-bullet':
                            return filterPopupThemeClasses.listCardExampleBullet
                        case 'testables-popup.list.row.hover-border':
                            return filterPopupThemeClasses.listRowHoverBorder
                        case 'testables-popup.list.row.icon':
                            return filterPopupThemeClasses.listRowIcon
                        default:
                            return undefined
                    }
                }
            }
        }

        if (testableType === 'condition') {
            return {
                color: popupColor,
                tint: (color, type, context) => {
                    switch (context) {
                        case 'popupwindow.title':
                            return conditionPopupThemeClasses.popupTitle
                        case 'popupwindow.link':
                            return conditionPopupThemeClasses.popupLink
                        case 'popupwindow.icon.background':
                            return conditionPopupThemeClasses.popupIconBackground
                        case 'popupwindow.icon.text':
                            return conditionPopupThemeClasses.popupIconText
                        case 'testables-popup.list.title-hover':
                            return conditionPopupThemeClasses.listTitleHover
                        case 'testables-popup.list.card.hover-background':
                            return conditionPopupThemeClasses.listCardHoverBackground
                        case 'testables-popup.list.card.hover-border':
                            return conditionPopupThemeClasses.listCardHoverBorder
                        case 'testables-popup.list.card.icon.background':
                            return conditionPopupThemeClasses.listCardIconBackground
                        case 'testables-popup.list.card.icon.text':
                            return conditionPopupThemeClasses.listCardIconText
                        case 'testables-popup.list.card.example-bullet':
                            return conditionPopupThemeClasses.listCardExampleBullet
                        case 'testables-popup.list.row.hover-border':
                            return conditionPopupThemeClasses.listRowHoverBorder
                        case 'testables-popup.list.row.icon':
                            return conditionPopupThemeClasses.listRowIcon
                        default:
                            return undefined
                    }
                }
            }
        }

        if (testableType !== 'offer') {
            return popupColor
        }

        return {
            color: popupColor,
            tint: (color, type, context) => {
                switch (context) {
                    case 'popupwindow.title':
                        return offerPopupThemeClasses.popupTitle
                    case 'popupwindow.link':
                        return offerPopupThemeClasses.popupLink
                    case 'popupwindow.icon.background':
                        return offerPopupThemeClasses.popupIconBackground
                    case 'popupwindow.icon.text':
                        return offerPopupThemeClasses.popupIconText
                    case 'testables-popup.list.title-hover':
                        return offerPopupThemeClasses.listTitleHover
                    case 'testables-popup.list.card.hover-background':
                        return offerPopupThemeClasses.listCardHoverBackground
                    case 'testables-popup.list.card.hover-border':
                        return offerPopupThemeClasses.listCardHoverBorder
                    case 'testables-popup.list.card.icon.background':
                        return offerPopupThemeClasses.listCardIconBackground
                    case 'testables-popup.list.card.icon.text':
                        return offerPopupThemeClasses.listCardIconText
                    case 'testables-popup.list.card.example-bullet':
                        return offerPopupThemeClasses.listCardExampleBullet
                    case 'testables-popup.list.row.hover-border':
                        return offerPopupThemeClasses.listRowHoverBorder
                    case 'testables-popup.list.row.icon':
                        return offerPopupThemeClasses.listRowIcon
                    default:
                        return undefined
                }
            }
        }
    }, [popupColor, testableType, conditionPopupThemeClasses])
    /* const addTestables = useNodesStore(store => store.addTestables)*/

    if (filters.length > 1 && filters.find(({type}) => type === AnyProductsMeta.id)) {
        const onlyTwoFilters = filters.length === 2
        const hasQuantity = filters.find(({type}) => type === QuantityMeta.id)

        if (!(onlyTwoFilters && hasQuantity)) {
            setFilters(filters.filter(({type}) => type !== AnyProductsMeta.id))
        }
    }
    const [decisionOnQuantity, setDecisionOnQuantity] = useState<QuantityDecision>(null)
    const [currentFilterId, setCurrentFilterId] = React.useState<string | null>(null);

    const [startedOver, setStartedOver] = useState(false)
    const defaultScreenContentWidth = 400
    const resetting = useRef(false)
    /**
     * -----------*SCREEN IDS*-------------
     */
    let scopeSelectionScreenId = -1
    let mainListSelectionScreenId = 1
    let initialIndividualComponentConfigScreenId = 2
    let quantityScreenId: number = 3
    let refineSelectionScreenId = 4
    let editScreenId = 5

    if (context.data.suggestedScopeExperienceSelector) {
        scopeSelectionScreenId = 1
        mainListSelectionScreenId++
        initialIndividualComponentConfigScreenId++
        quantityScreenId++
        refineSelectionScreenId++
        editScreenId++
    }

    const refineCanNavigateToThisScreen = () => {
        if (!supportsMultipleComponents) {
            return applyFilters.bind(this);
        }

        if (!supportsRefiningSelection()) {
            return applyFilters.bind(this);
        }

        if (!isEditMode()) {
            // check if the selected filter can be configured,
            // if not, send an action to close this window
            const hasAnyProductsSelected = !!filters.find(({type}) => type === AnyProductsMeta.id)

            if (testableType === 'filter' && hasAnyProductsSelected) {
                // return an action to perfom, in this case, just close the popup successfully
                return applyFilters.bind(this)
            }
        }
        return true // edit mode can always be configured
    }

    /*const {next} = UseScreenNavigationActions("testables")*/
    const getSelectedFilter = (customFilters?: ComponentRuntimeDataWithParentType[]) => {
        return filters.find(({type}) => type === currentFilterId)
        /*
                if (currentScreenId === 2) {
                    // get me the base filter
                    return filters[0]
                }
        */
        /*
                return (customFilters || filters).find(({type}) => type === currentFilterId)
        */
    }

    const getSelectedFilterMeta = (): ComponentMeta | undefined => {
        const selectedFilter = getSelectedFilter()

        if (!selectedFilter) {
            return;
        }

        return getComponentMeta((testableType + 's' as unknown as ComponentType), selectedFilter?.type || '')
    }

    const removeComponents = (remove: ComponentRuntimeDataWithParentType[]) => {
        const typesToRemove = remove.map(({type}) => type)

        typesToRemove.forEach(type => removeComponent(type))
    }

    const removeComponent = (type: string) => {
        const selectedFiltersAfterRemoval: {
            current: ComponentRuntimeDataWithParentType[] | [],
            removed: boolean
        } = {current: [], removed: false}

        setFilters(filters => {
            selectedFiltersAfterRemoval.current = filters.filter(filter => type !== filter.type)
            selectedFiltersAfterRemoval.removed = true

            return selectedFiltersAfterRemoval.current
        })

        if (!selectedFiltersAfterRemoval.removed) {
            return;
        }

        setTimeout(() => {
            if (selectedFiltersAfterRemoval.current.length === 0) {
                // if they've removed all, just remove this group all together if edit, reset/start over otherwise
                if (isEditMode()) {
                    // here let's remove the whole group by sending a close request with 0 components
                    close({
                        componentType: testableType,
                        status: 'success',
                        components: []
                    })
                } else {
                    reset(true)
                }
            } else {
                setCurrentFilterId(selectedFiltersAfterRemoval.current[0].type);
            }
        }, 10)

        /*setTimeout(() => {
            if (selectedFiltersAfterRemoval.current.length === 0) {
                reset(true)
            } else {
                setCurrentFilterId(selectedFiltersAfterRemoval.current[0].type);
            }
        }, 10)*/
    }

    if ((!currentFilterId && !!decisionOnQuantity) || (filters.length > 1 && !currentFilterId) || (currentFilterId === QuantityMeta.id && !filters.find(({type}) => type === QuantityMeta.id))) {
        if (filters.length > 1) {
            setCurrentFilterId(filters[0].type)
        }
    }

    if (filters.length > 1 && !decisionOnQuantity) {
        // if you've added 2 filters, you have made a decision on quantity
        setDecisionOnQuantity('any')
    }
    const isEditMode = () => {
        return context.data.targetType?.startsWith('testable') && context.data.mode === 'edit';
    }
// set the current screen to 2 upon mounting if were on edit mode
    useLayoutEffect(() => {
        if (isEditMode() && !resetting.current) {
            // here we'll get the filters from the store
            // and set them to the filters state
            if ((context.data.componentType as TreeContextData['componentType']) === 'offer') {
                const offer = cloneDeep(offers.get(context.data.targetId as string))
                setFilters([offer])
                setCurrentFilterId(offer.type)
                setCurrentScreenId(getNextScreenToJumpToIfSupported(editScreenId))
            } else {
                if (context.scope === 'tier-subchild') {
                    // we need to set the filters as the testable partial but with the added type so that we can use it as expected, in other words, merge the base testable but use the options of the partial including its id ofc
                    const baseTestable = cloneDeep(testables.getParent(context.data.targetId as string) as TestableData)
                    const testablePartial = cloneDeep(testables.getNode(context.data.targetId as string) as TestablePartial)
                    setFilters([
                        {
                            ...baseTestable,
                            ...testablePartial,
                            parentType: `${baseTestable.testableType}s`,
                        }
                    ])
                    setCurrentFilterId(baseTestable.type)
                    setCurrentScreenId(getNextScreenToJumpToIfSupported(editScreenId))
                } else if (context.scope === 'tier-base') {
                    let testableBase = cloneDeep(testables.getNode(context.data.targetId as string));
                    setFilters([
                        testableBase
                    ])
                    setCurrentFilterId(testableBase.type)
                    setCurrentScreenId(getNextScreenToJumpToIfSupported(editScreenId))
                } else {
                    const parentContext = testables.getParent(context.data.targetId as string)!
                    if (parentContext) {
                        setFilters(cloneDeep(
                            (testables.getChildren(parentContext.id) as TestableData[]).map(testables.getWithParentType.bind(testables))
                        ))
                        //also set the current filter id to the targetId bc were in edit mode
                        setCurrentFilterId(testables.getNode(context.data.targetId as string)!.type)
                        setCurrentScreenId(getNextScreenToJumpToIfSupported(editScreenId))
                    }
                }
            }
        }
    }, [context.data.targetType])

    const reset = (startedOver: boolean = true) => {
        resetting.current = true
        setFilters([])
        setCurrentFilterId(null);
        (currentScreenId !== mainListSelectionScreenId) && setCurrentScreenId(mainListSelectionScreenId)
        setQtyChoice('no-quantity')
        setDecisionOnQuantity(null)
        // this should be set to false on close, true when resetting on edit mode and window is not closing
        setStartedOver(startedOver)
        // this behaves weirdly here so we reset it in the main screen: setSuggestedScope(undefined)
        resetting.current = false
    }

    if (isOpen && filters.length === 0 && currentScreenId !== scopeSelectionScreenId && currentScreenId !== mainListSelectionScreenId) {
        reset(true)
    }
    const applyFilters = (extraFiltersToAdd: ComponentRuntimeDataWithParentType[] = []) => {
        close({
            componentType: testableType,
            status: 'success',
            components: filters,
            // only send the scope when its a root node, ignore for everything else
            ...(suggestedScope && context.data.targetType === 'root'? {
                extraData: {suggestedScope}
            } : {
            })
        });
        // check if target is root, add it to the root
    };

    const FieldsToRender = ({fieldToRender, notes, setNotes}: {
        fieldToRender: ComponentRuntimeDataWithParentType | undefined,
        notes: Note[],
        setNotes: (value: (((prevState: Note[]) => Note[]) | Note[])) => void
    }) => {

        //getSelectedFilter()
        if (!fieldToRender) {
            return;

        }
        return <Fields componentRuntimeData={fieldToRender!}
                       scope={context.scope}
                       origin={'testables'}
                       notes={notes}
                       setNotes={setNotes}
                       onUpdate={(componentRuntimeData, field, value, action, extraNestedFieldPathRelative = '') => {
                           const newFilters = [...filters];

                           // for categories.tsx:
                           // here the fields would be expectedValues
                           // we need to only save the id from the value which is a SelectOption
                           // the thing is, how? so that this is reusable and maintainable

                           const selectedFilter = fieldToRender;
                           const renderType = typeof field.renderType === 'function' ? field.renderType(selectedFilter?.options) : field.renderType

                           let maybeDot = ''

                           if (extraNestedFieldPathRelative) {
                               if (!extraNestedFieldPathRelative.startsWith('[')) {
                                   maybeDot = '.';
                               }
                           }
                           const fieldPath = `options.${field.id}${extraNestedFieldPathRelative ? maybeDot + extraNestedFieldPathRelative : ''}`;

                           /**
                            * ok so here get the component meta and trigger the hooks for this field if needed
                            */
                           const componentMeta = getComponentMeta(`${testableType}s`, componentRuntimeData.type)

                           if (renderType === FieldType.MultiSelect) {
                               const selectedIds = get(selectedFilter, fieldPath) || []//selectedFilter.options[field.id] || [];
                               const hooks = get(componentMeta?.fieldHooks || {}, field.id) || {} as ComponentMeta['fieldHooks'];

                               if (action?.action === FieldUpdateAction.Add) {
                                   // if the action is add, we need to add the value to the options
                                   // so we can use the value as it is
                                   let valueToAdd = (value as SelectOption).id;
                                   if (typeof hooks?.add === 'function') {
                                       valueToAdd = hooks.add((value as SelectOption).id, (value as SelectOption))
                                   }
                                   value = [...selectedIds, valueToAdd];
                               } else if (action?.action === FieldUpdateAction.Remove) {
                                   // if the action is remove, we need to remove the value from the options
                                   value = selectedIds.filter((id: string) => id !== (value as SelectOption).id)
                               }
                           } else if (renderType === FieldType.Custom) {
                               if (action?.action === FieldUpdateAction.Add) {
                                   // if the action is add, we need to add the value to the options
                                   // so we can use the value as it is
                                   value = [...(get(selectedFilter, fieldPath) || []), value];
                               }
                           } else if (renderType === FieldType.Select) {
                               value = (value as SelectOption).id
                           }

                           if (action?.action === FieldUpdateAction.Remove && renderType === FieldType.Custom) {
                               removeArrayElementAtPath(selectedFilter, fieldPath);
                           } else {
                               let valueToSet = field?.settingValue?.(value) || value;

                               set(selectedFilter, fieldPath, typeof valueToSet === 'object' ? cloneDeep(valueToSet) : value);
                           }

                           if (action?.merge) {
                               Object.entries(action.merge).forEach(([fieldId, mergeValue]) => {
                                   set(selectedFilter, `options.${fieldId}`, typeof mergeValue === 'object' ? cloneDeep(mergeValue) : mergeValue);
                               });
                           }

                           setFilters(newFilters);
                       }}/>;
    }
    const isEditing = () => {
        return isEditMode() && !startedOver;
    }
    const unavailableComponentMessage = __('This component is currently unavailable. If it was configured with Coupons+ Pro, re-enable Pro to edit it.')

    function getFieldTitle(metaTypeAndId: [ComponentType, string]) {
        const componentMeta = getComponentMeta(...metaTypeAndId)

        if (!componentMeta) {
            return __('Unavailable component')
        }

        let titleTemplate: string = '*' //__('Select *')

        if (componentMeta?.fieldsTitlePrefix === false || !hasImplementation(componentMeta)) {
            titleTemplate = __('*')
        }

        let replacedTitle = titleTemplate.replace('*', withDecodedEntities(getComponentMetaData(componentMeta)).name);

        return <>{componentMeta?.titlePrefix} {replacedTitle}</>;
    }

    function getFieldDescription(metaTypeAndId: [ComponentType, string]) {
        const componentMeta = getComponentMeta(...metaTypeAndId)

        if (!componentMeta) {
            return;
        }

        return withDecodedEntities(getComponentMetaData(componentMeta))?.description;
    }

    const availableTabs = tabs.filter(
        ({id}) => id === testableType || context.data?.supportedComponentTypes?.includes(id as ComponentDataType)
    )
    const showTabs = context.data?.showTabs !== false;
    const selectedTab = availableTabs.find(tab => tab.id === testableType);
    const supportsMultipleComponents = context.data?.supportsMultipleComponents !== false;
    const supportedComponents = [
        ...context.data?.supportedComponents || [],
    ];
    /**
     * Will return the next screend id if its currently supported, if not, it will jump to the next supported screen
     */
    const supports: (type: string) => boolean = (type): boolean => {
        if (context.data?.unsupportedComponents?.includes(type)) {
            return false;

        }
        return context.data?.supportedComponents ? context.data.supportedComponents.includes(type) : true;

    }

    const supportsQuantity = supports(QuantityMeta.id);
    const getNextScreenToJumpToIfSupported = (screedId: number) => {
        if (screedId === quantityScreenId) {
            // we want to jump to the quantity screen, let's check if its supported though
            return supportsQuantity ? screedId : 5

        }
        return screedId;


    }
    const addComponentByMeta = (componentMeta: ComponentMeta, selectIt = true) => {
        // make sure its type dont exist already because we can only have one of each type
        if (filters.find(({type}) => type === componentMeta.id)) {
            return;

        }

        const filterToAdd = addIdToComponent({
            parentType: testableType + 's' as ComponentRuntimeDataWithParentType['parentType'],
            ...getDefaultRuntimeComponentData(componentMeta)
        }, false, testableType === 'offer');
        setFilters([...filters, filterToAdd])
        selectIt && setCurrentFilterId(filterToAdd.type)

    }
    const removeComponentByMeta = (componentMeta: ComponentMeta) => {
        setFilters(filters => filters.filter(({type}) => type !== componentMeta.id))
        if (currentFilterId === componentMeta.id) {
            setCurrentFilterId(null)
        }

    }

    const canShowBottomNavigation = () => {
        if (currentFilterId) {
            let currentFilter = filters.find(({type}) => type === currentFilterId);

            const parentType = currentFilter?.parentType

            if (parentType) {
                const canShowButtonsCallable = getComponentMeta(...[parentType, currentFilterId])?.canShowBottomNavigation

                if (canShowButtonsCallable) {
                    return canShowButtonsCallable(currentFilter)
                }
            }
        }

        return true
    }

    const shouldUseSelectableCards = ['offer', 'condition', /*'filter'*/].includes(testableType)

    const selectComponentFromList = (componentMeta: ComponentMeta, canBeSelected: boolean) => {
        if (!canBeSelected) {
            return
        }

        addComponentByMeta(componentMeta)
        next()
    }

    const getSelectableComponentTitle = (componentMeta: ComponentMeta) => {
        const componentMetaData = withDecodedEntities(getComponentMetaData(componentMeta))
        const cardRenderer = getCardRendererFromType(componentMeta.id)
        const preRenderedTitle = getNamePrefixedForCardRenderer(cardRenderer, context.scope).map((prefix, index) => {
            return (
                index === 0
                    ? prefix && <span className="text-gray-350">{prefix}</span>
                    : <span className={classNames(
                        'text-1x font-medium transition-all duration-200',
                        resolveTint(
                            popupThemeColor,
                            'testables-popup.list.title-hover',
                            'group-hover:text-purple-tint-90',
                            'text'
                        )
                    )}>{prefix} {componentMetaData.name}</span>
            )
        })

        if (typeof componentMeta.listTitle === 'function') {
            return componentMeta.listTitle(preRenderedTitle, componentMetaData.name)
        }

        return preRenderedTitle
    }

    const decodeSelectableText = (value: unknown) => {
        return typeof value === 'string' ? decodeHtmlEntities(value) : undefined
    }

    const normalizeForSearch = (value: string): string => {
        return value
            .toLowerCase()
            .normalize('NFD')
            .replace(/[\u0300-\u036f]/g, '')
    }

    const getSelectableComponentDescription = (componentMeta: ComponentMeta) => {
        const descriptionSource = getComponentMetaDataField(componentMeta, 'description')
        const description = typeof descriptionSource === 'function'
            ? descriptionSource({suggestedScope: suggestedScope || ''})
            : descriptionSource

        return decodeSelectableText(description) ?? description
    }

    const getSelectableComponentExample = (componentMeta: ComponentMeta) => {
        const exampleSource = getCardRendererFromType(componentMeta.id)?.example
        const example = typeof exampleSource === 'function'
            ? exampleSource(context.scope)
            : exampleSource

        return decodeSelectableText(example) ?? example
    }

    const getSelectableComponentUseCases = (componentMeta: ComponentMeta) => {
        return (getComponentMetaData(componentMeta).useCases || [])
            .slice(0, 2)
            .map((useCase) => ({
                ...useCase,
                title: decodeSelectableText(useCase.title) || '',
                content: (useCase.content || [])
                    .map(content => decodeSelectableText(content))
                    .filter((content): content is string => !!content),
            }))
            .filter(({title, content}) => title || content.length)
    }

    const SelectableFiltersList = ({componentsOrderedByCategory, canBeSelected = true, suggestedCategoryId}: {componentsOrderedByCategory: CategorizedComponents, canBeSelected?: boolean, suggestedCategoryId?: string}) => {
        const [pressedComponentId, setPressedComponentId] = useState<string | null>(null)

        return <div className="relative flex flex-col space-y-6 mb-4">
            {values(mapValues(componentsOrderedByCategory, (componentMetas, category) => {
                if (!componentMetas?.length) {
                    return null;
                }
                const isSuggestedCategory = suggestedCategoryId === category;
                const categoryContent = <div className="space-y-2">
                    <div
                        className="text-smaller-1 text-gray-400">{componentCategories[category]?.label ?? componentCategories[category]}</div>
                    <div className="space-y-3">
                        {componentMetas.map((filterMeta) => {
                            const componentMetaData = withDecodedEntities(getComponentMetaData(filterMeta))
                            const title = getSelectableComponentTitle(filterMeta)
                            const description = getSelectableComponentDescription(filterMeta)
                            const example = getSelectableComponentExample(filterMeta)
                            const useCases = getSelectableComponentUseCases(filterMeta)
                            const exampleItems = example
                                ? [example]
                                : useCases.flatMap((useCase) => useCase.content).slice(0, 2)

                            return <div key={filterMeta.id} className="space-y-1">
                                {shouldUseSelectableCards
                                    ? <EmulatedButton
                                        className={classNames(
                                            'group w-full rounded-[24px] border-px border-gray-200 --bg-white bg-opacity-70 px-4 py-4 text-left transition-all duration-200 transition-transform ease-out',
                                            resolveTint(
                                                popupThemeColor,
                                                'testables-popup.list.card.hover-background',
                                                classNames(
                                                    popupColor.background(hover(10)),
                                                    'hover:bg-opacity-80'
                                                ),
                                                'background'
                                            ),
                                            resolveTint(
                                                popupThemeColor,
                                                'testables-popup.list.card.hover-border',
                                                'hover:border-purple-tint-60',
                                                'border'
                                            ),
                                        )}
                                        style={pressedComponentId === filterMeta.id ? {transform: 'scale(0.97)'} : undefined}
                                        onMouseDownCapture={() => setPressedComponentId(filterMeta.id)}
                                        onMouseUp={() => setPressedComponentId(null)}
                                        onMouseLeave={() => setPressedComponentId(null)}
                                        onClick={() => selectComponentFromList(filterMeta, canBeSelected)}
                                    >
                                        <div className="flex flex-col gap-3">
                                            <div className="flex items-start gap-3">
                                                <div className={classNames(
                                                    'flex min-w-7 min-h-7 max-w-7 max-h-7 items-center justify-center rounded-[10px] bg-gray-200 text-gray-500 transition-all duration-200',
                                                    resolveTint(
                                                        popupThemeColor,
                                                        'testables-popup.list.card.icon.background',
                                                        'group-hover:bg-purple-normal',
                                                        'background'
                                                    ),
                                                    resolveTint(
                                                        popupThemeColor,
                                                        'testables-popup.list.card.icon.text',
                                                        'group-hover:text-white',
                                                        'text'
                                                    )
                                                )}>
                                                    {filterMeta.icon?.({className: "w-4 h-4", context: 'card'})}
                                                </div>
                                                <div className="min-w-0 flex-1 space-y-2">
                                                    <div className="flex flex-wrap items-center gap-2">
                                                        <div className="flex flex-wrap items-center gap-1 text-gray-750">
                                                            {title}
                                                        </div>
                                                        {!hasImplementation(filterMeta) && <ProBadge />}
                                                    </div>
                                                    {description && <div className="text-smaller-1 leading-4 text-gray-500">
                                                        {description}
                                                    </div>}
                                                </div>
                                            </div>
                                            {!!useCases.length && <div className="flex flex-wrap gap-2">
                                                {useCases.map((useCase) => (
                                                    <span
                                                        key={useCase.title}
                                                        className="rounded-full border-px border-gray-200 bg-white px-2 py-1 text-smaller-2 text-gray-500"
                                                    >
                                                        {useCase.title}
                                                    </span>
                                                ))}
                                            </div>}
                                            {!!exampleItems.length && <div className="border-t border-gray-100 pt-3">
                                                <div className="text-smaller-2 font-medium uppercase tracking-[0.18em] text-gray-400">
                                                    {__('Examples')}
                                                </div>
                                                <ul className="mt-2 flex flex-col gap-2">
                                                    {exampleItems.map((exampleItem, index) => <li
                                                        key={`${componentMetaData.name}-${index}`}
                                                        className="flex items-start gap-2 text-smaller-1 leading-4 text-gray-500"
                                                    >
                                                        <span className={classNames(
                                                            'mt-[5px] min-w-2 min-h-2 max-w-2 max-h-2 rounded-full bg-gray-300',
                                                            resolveTint(
                                                                popupThemeColor,
                                                                'testables-popup.list.card.example-bullet',
                                                                'group-hover:bg-purple-normal',
                                                                'background'
                                                            )
                                                        )}/>
                                                        <span>{exampleItem}</span>
                                                    </li>)}
                                                </ul>
                                            </div>}
                                        </div>
                                    </EmulatedButton>
                                    : <>
                                        <button
                                            className={classNames(
                                                'flex items-center space-x-3 py-[8px] px-7 text-gray-800 rounded-2 border-px border-gray-200 w-full hover:bg-opacity-50 group transition-transform ease-out',
                                                popupColor.background(hover(10)),
                                                resolveTint(
                                                    popupThemeColor,
                                                    'testables-popup.list.row.hover-border',
                                                    'hover:border-purple-tint-50',
                                                    'border'
                                                )
                                            )}
                                            style={pressedComponentId === filterMeta.id ? {transform: 'scale(0.97)'} : undefined}
                                            onMouseDown={() => setPressedComponentId(filterMeta.id)}
                                            onMouseUp={() => setPressedComponentId(null)}
                                            onMouseLeave={() => setPressedComponentId(null)}
                                            onClick={() => selectComponentFromList(filterMeta, canBeSelected)}>
                                            {filterMeta.icon?.({className: classNames(
                                                "w-5 h-5 text-gray-350",
                                                resolveTint(
                                                    popupThemeColor,
                                                    'testables-popup.list.row.icon',
                                                    'group-hover:text-purple-tint-90',
                                                    'text'
                                                )
                                            )})}
                                            <div className="flex flex-col items-start space-y-0">
                                                <div className="space-x-1 flex items-center gap-2">
                                                    <div className="flex items-center gap-1">
                                                        {title}
                                                    </div>
                                                    {!hasImplementation(filterMeta) && <ProBadge />}
                                                </div>
                                            </div>
                                        </button>
                                        {example && <div
                                            className="text-smaller-1 text-gray-350 pl-15">{example}</div>}
                                    </>}
                            </div>
                        })}
                    </div>
                </div>;
                return <SuggestedWrapper key={category} show={isSuggestedCategory} className={'px-5 py-3'}>{categoryContent}</SuggestedWrapper>;
            }))}
        </div>
    }


    const componentType = `${testableType}s` as ComponentType

    let FullSelectableFiltersList = ({extraItemsToIgnore = [], nonImplementedCanBeSelected = true}: {extraItemsToIgnore?: string[], nonImplementedCanBeSelected?: boolean}) => {
        const [searchQuery, setSearchQuery] = useState('')
        const searchInputRef = useRef<HTMLInputElement | null>(null)
        const isSearchEnabled = testableType === 'filter' || (testableType === 'condition' && context.scope === 'preconditions')
        const activeSearchQuery = isSearchEnabled ? searchQuery : ''
        const trimmedSearchQuery = activeSearchQuery.trim()
        const hasSearchQuery = trimmedSearchQuery.length > 0
        const normalizedSearchQuery = normalizeForSearch(trimmedSearchQuery)
        let componentsOrderedByCategory: CategorizedComponents = getComponentsOrderedByCategory(
            componentType,
            supportedComponents,
            [...extraItemsToIgnore, ...context.data?.unsupportedComponents || [], ...filters.map(({type}) => type)],
            // @ts-ignore
            context.scope,
            true
        );

        React.useEffect(() => {
            if (!isSearchEnabled) {
                setSearchQuery('')
            }
        }, [isSearchEnabled])

        React.useEffect(() => {
            if (!isSearchEnabled) {
                return
            }

            const focusAndSelectInput = () => {
                const input = searchInputRef.current

                if (!input) {
                    return
                }

                input.focus({preventScroll: true})
                if (input.value.length > 0) {
                    input.select()
                }
            }

            const animationFrameId = requestAnimationFrame(focusAndSelectInput)
            const delayedFocusTimeoutId = window.setTimeout(focusAndSelectInput, 340)

            return () => {
                cancelAnimationFrame(animationFrameId)
                clearTimeout(delayedFocusTimeoutId)
            }
        }, [currentScreenId, isSearchEnabled])

        const suggested = context.data?.suggested;
        let suggestedCategoryId: string | undefined;

        if (suggested) {
            if (suggested.type === 'category') {
                // Sort the suggested category to be first
                const categoryId = suggested.id;
                if (componentsOrderedByCategory[categoryId]) {
                    suggestedCategoryId = categoryId;
                    const suggestedCategoryComponents = componentsOrderedByCategory[categoryId];
                    delete componentsOrderedByCategory[categoryId];
                    componentsOrderedByCategory = {
                        [categoryId]: suggestedCategoryComponents,
                        ...componentsOrderedByCategory
                    };
                }
            } else if (suggested.type === 'component') {
                // Find and extract the component from its category
                const componentId = suggested.id;
                let foundComponent: ComponentMeta | undefined;

                for (const [category, components] of Object.entries(componentsOrderedByCategory)) {
                    const componentIndex = components?.findIndex(c => c.id === componentId);
                    if (componentIndex !== undefined && componentIndex >= 0 && components) {
                        foundComponent = components[componentIndex];
                        // Remove the component from its original category
                        components.splice(componentIndex, 1);
                        // Remove category if empty
                        if (components.length === 0) {
                            delete componentsOrderedByCategory[category];
                        }
                        break;
                    }
                }

                if (foundComponent) {
                    // Create a 'suggested' category with the component and put it at the top
                    suggestedCategoryId = ComponentCategory.Suggested;
                    componentsOrderedByCategory = {
                        [ComponentCategory.Suggested]: [foundComponent],
                        ...componentsOrderedByCategory
                    };
                }
            }
        }

        type SearchEntry = {
            category: string,
            componentMeta: ComponentMeta,
            title: string,
            id: string,
            description: string,
            extras: string[],
            relatedSearchTerms: string[],
        }

        let filteredComponentsOrderedByCategory: CategorizedComponents

        if (hasSearchQuery) {
            const searchEntries: SearchEntry[] = Object.entries(componentsOrderedByCategory).flatMap(([category, componentMetas]) => {
                return (componentMetas || []).map((componentMeta) => {
                    const componentMetaData = withDecodedEntities(getComponentMetaData(componentMeta))
                    const description = getSelectableComponentDescription(componentMeta) || ''
                    const example = getSelectableComponentExample(componentMeta) || ''
                    const useCases = getSelectableComponentUseCases(componentMeta)
                    const relatedSearchTerms = (componentMeta.relatedSearchTerms || [])
                        .filter((value): value is string => typeof value === 'string' && value.trim().length > 0)

                    return {
                        category,
                        componentMeta,
                        title: componentMetaData.name || '',
                        id: componentMeta.id || '',
                        description,
                        extras: [
                            example,
                            ...useCases.map(({title}) => title),
                            ...useCases.flatMap(({content}) => content),
                        ].filter((value): value is string => typeof value === 'string' && value.trim().length > 0),
                        relatedSearchTerms,
                    }
                })
            })

            const getDirectMatchScore = (entry: SearchEntry): number => {
                const normalizedTitle = normalizeForSearch(entry.title)
                const normalizedId = normalizeForSearch(entry.id)
                const normalizedDescription = normalizeForSearch(entry.description)
                const normalizedRelatedSearchTerms = entry.relatedSearchTerms.map(normalizeForSearch)
                const normalizedExtras = entry.extras.map(normalizeForSearch)

                const getValueScore = (normalizedValue: string, baseWeight: number): number => {
                    if (!normalizedValue.includes(normalizedSearchQuery)) {
                        return 0
                    }

                    let score = baseWeight

                    if (normalizedValue.startsWith(normalizedSearchQuery)) {
                        score += 60
                    }

                    if (normalizedValue === normalizedSearchQuery) {
                        score += 90
                    }

                    return score
                }

                const directScores = [
                    getValueScore(normalizedTitle, 500),
                    getValueScore(normalizedId, 300),
                    getValueScore(normalizedDescription, 140),
                    ...normalizedRelatedSearchTerms.map((value) => getValueScore(value, 220)),
                    ...normalizedExtras.map((value) => getValueScore(value, 120)),
                ]

                return Math.max(0, ...directScores)
            }

            const directMatches = searchEntries
                .map((entry) => ({
                    entry,
                    directScore: getDirectMatchScore(entry),
                }))
                .filter(({directScore}) => directScore > 0)
                .sort((a, b) => {
                    const aTitle = normalizeForSearch(a.entry.title)
                    const bTitle = normalizeForSearch(b.entry.title)
                    const aTitleStartsWithQuery = aTitle.startsWith(normalizedSearchQuery)
                    const bTitleStartsWithQuery = bTitle.startsWith(normalizedSearchQuery)

                    if (aTitleStartsWithQuery !== bTitleStartsWithQuery) {
                        return aTitleStartsWithQuery ? -1 : 1
                    }

                    const aTitleIncludesQuery = aTitle.includes(normalizedSearchQuery)
                    const bTitleIncludesQuery = bTitle.includes(normalizedSearchQuery)

                    if (aTitleIncludesQuery !== bTitleIncludesQuery) {
                        return aTitleIncludesQuery ? -1 : 1
                    }

                    return b.directScore - a.directScore
                })

            const rankedEntries = directMatches.length > 0
                ? directMatches.map(({entry}) => entry)
                : (() => {
                    const fuse = new Fuse(searchEntries, {
                        keys: [
                            {name: 'title', weight: 0.70},
                            {name: 'relatedSearchTerms', weight: 0.10},
                            {name: 'description', weight: 0.10},
                            {name: 'id', weight: 0.08},
                            {name: 'extras', weight: 0.02},
                        ],
                        includeScore: true,
                        shouldSort: true,
                        threshold: 0.18,
                        minMatchCharLength: 2,
                        ignoreLocation: true,
                        isCaseSensitive: false,
                        ignoreDiacritics: true,
                    })

                    return fuse.search(trimmedSearchQuery)
                        .sort((a, b) => {
                            const aTitle = normalizeForSearch(a.item.title)
                            const bTitle = normalizeForSearch(b.item.title)
                            const aTitleStartsWithQuery = aTitle.startsWith(normalizedSearchQuery)
                            const bTitleStartsWithQuery = bTitle.startsWith(normalizedSearchQuery)

                            if (aTitleStartsWithQuery !== bTitleStartsWithQuery) {
                                return aTitleStartsWithQuery ? -1 : 1
                            }

                            const aTitleIncludesQuery = aTitle.includes(normalizedSearchQuery)
                            const bTitleIncludesQuery = bTitle.includes(normalizedSearchQuery)

                            if (aTitleIncludesQuery !== bTitleIncludesQuery) {
                                return aTitleIncludesQuery ? -1 : 1
                            }

                            return (a.score ?? 1) - (b.score ?? 1)
                        })
                        .map(({item}) => item)
                })()

            const rankedEntriesByCategory = new Map<string, SearchEntry[]>()

            rankedEntries.forEach((item) => {
                const currentEntries = rankedEntriesByCategory.get(item.category) || []
                rankedEntriesByCategory.set(item.category, [...currentEntries, item])
            })

            filteredComponentsOrderedByCategory = {}

            Object.keys(componentsOrderedByCategory).forEach((category) => {
                const categoryEntries = rankedEntriesByCategory.get(category) || []

                if (categoryEntries.length > 0) {
                    filteredComponentsOrderedByCategory[category] = categoryEntries.map(({componentMeta}) => componentMeta)
                }
            })
        } else {
            filteredComponentsOrderedByCategory = componentsOrderedByCategory
        }

        if (suggestedCategoryId && !filteredComponentsOrderedByCategory[suggestedCategoryId]?.length) {
            suggestedCategoryId = undefined
        }

        const hasSearchResults = Object.values(filteredComponentsOrderedByCategory).some((componentMetas) => componentMetas.length > 0)

        return <div className="space-y-2 w-full">
            {isSearchEnabled && <div className="">
                <div className="relative">
                    <svg xmlns="http://www.w3.org/2000/svg"
                         fill="none"
                         viewBox="0 0 24 24"
                         strokeWidth={1.5}
                         stroke="currentColor"
                         className="pointer-events-none absolute left-3 top-1/2 min-w-4 min-h-4 max-w-4 max-h-4 -translate-y-1/2 text-gray-350">
                        <path strokeLinecap="round" strokeLinejoin="round"
                              d="m21 21-4.35-4.35M16.65 10.8a5.85 5.85 0 1 1-11.7 0 5.85 5.85 0 0 1 11.7 0Z"/>
                    </svg>
                    <input
                        ref={searchInputRef}
                        type="text"
                        value={searchQuery}
                        onChange={(event) => setSearchQuery(event.target.value)}
                        placeholder={componentType === 'filters'? __('Search product filters...') : __('Search conditions...')}
                        className="w-full py-[6px] px-9 border border-gray-100 !bg-gray-150 !bg-opacity-40 text-smaller-1 rounded-[22px] caret-gray-400 transition bg-transparent focus:outline-none focus:border-blue-50/10 focus:!bg-opacity-60 ring-gray-200 ring-opacity-30 ring-offset-1 placeholder:text-gray-300"
                    />
                    {hasSearchQuery && (
                        <button
                            type="button"
                            onClick={() => setSearchQuery('')}
                            className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-350 hover:text-gray-450 transition"
                        >
                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="min-w-4 min-h-4 max-w-4 max-h-4">
                                <path
                                    d="M6.28 5.22a.75.75 0 0 0-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 1 0 1.06 1.06L10 11.06l3.72 3.72a.75.75 0 1 0 1.06-1.06L11.06 10l3.72-3.72a.75.75 0 0 0-1.06-1.06L10 8.94 6.28 5.22Z"/>
                            </svg>
                        </button>
                    )}
                </div>
            </div>}
            {hasSearchResults
                ? <SelectableFiltersList componentsOrderedByCategory={filteredComponentsOrderedByCategory} suggestedCategoryId={suggestedCategoryId} />
                : <div className="px-5 py-6 text-smaller-1 text-gray-400">{__('No components match your search.')}</div>}
            {!hasSearchQuery && <NonImplementedComponentsList extraItemsToIgnore={extraItemsToIgnore} canBeSelected={nonImplementedCanBeSelected} />}
        </div>;
    };

    const NonImplementedComponentsList = ({extraItemsToIgnore = [], canBeSelected = true}: { extraItemsToIgnore?: string[], canBeSelected: boolean}) => {
        const [showList, setShowList] = useState(false)
        const nonImplementedComponents: CategorizedComponents = getComponentsOrderedByCategory(
            componentType,
            supportedComponents,
            [...extraItemsToIgnore, ...context.data?.unsupportedComponents || [], ...filters.map(({type}) => type)],
            // @ts-ignore
            context.scope,
            false
        )

        const hasNonImplementedComponents = Object.values(nonImplementedComponents).some(components => components.length > 0)

        if (!hasNonImplementedComponents) {
            return null;
        }

        return <div className="pt-2 pb-4">
            {!showList && <Button size={'medium'} preset={'gray'} onClick={setShowList.bind(this, true)}>
                <div className={'inline-flex items-center gap-2 w-[max-content] hover:cursor-pointer justify-center'}>
                    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="min-w-5 min-h-5 max-w-5 max-h-5">
                        <path strokeLinecap="round" strokeLinejoin="round" d="M12 9v6m3-3H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
                    </svg>
                    <span>{__(`More *`).replace('*', getLabelForComponentType(componentType))}</span>
                </div>
            </Button>}
            {showList && <SelectableFiltersList componentsOrderedByCategory={nonImplementedComponents} canBeSelected={canBeSelected}/>}
        </div>
    }

    const renderSuggestedScope = (renderer: string | ((suggestedScope: string) => string) | undefined) => {
        return typeof renderer === 'function' ? renderer(suggestedScope || '') : renderer
    };

    const supportsRefiningSelection = () => {
        if (!supportsMultipleComponents) {
            return false;
        }
        // is type filters
        if (testableType === 'filter') {
            const hasProductsFilterInAllowedMode = filters.find(({type, options}) => {
                return type === ProductsMeta.id && (options as ProductsOptions).inclusionType === 'allowed'
            })
            const hasQuantity = filters.find(({type}) => type === QuantityMeta.id)

            if (hasProductsFilterInAllowedMode) {
                /**
                 * By this point the filters are products and in 'allowed' mode, so the only refining supported here is qty.
                 */

                if (!isEditMode()) {
                    return false // only allow adding quantity on the edit screen since the user has already made a choice regarding quantity on the previous screen
                }

                return !hasQuantity
            }
        }

        return true;
    }

    const getDescFromComponentMeta = (componentMetaArg: [ComponentType,  string]): string | undefined => {
        const componentMeta = getComponentMeta(...componentMetaArg)

        if (!componentMeta) {
            return;
        }

        const desc = getComponentMetaDataField(componentMeta, 'description');

        const suggestedScopeDesc =  typeof desc == 'function' ? desc({suggestedScope}) : desc;

        if (suggestedScopeDesc) {
            return suggestedScopeDesc;
        }
        // else lets fallback to the default description
        return withDecodedEntities(getComponentMetaData(componentMeta))?.description;
    }

    const getTitleFromComponentMeta = (componentMetaArg: [ComponentType,  string]): ReactNode => {
        const fieldTitle = getFieldTitle(componentMetaArg);
        const componentMeta = getComponentMeta(...componentMetaArg)

        if (!suggestedScope || !componentMeta?.supportsCustomTitleForSuggestedScopes?.(suggestedScope)) {
            return fieldTitle
        } else if (suggestedScope === 'bogo') {
            return <TextPreSuFixed direction={'prefix'} primary={fieldTitle} secondary={<>{__('Buy')}:</>} />
        } else if (suggestedScope === 'item_discounts') {
            return <TextPreSuFixed direction={'suffix'} primary={fieldTitle} secondary={__('to discount')} />
        } else if (suggestedScope === 'cart_discounts') {
            return <TextPreSuFixed direction={'suffix'} primary={fieldTitle} secondary={__('required for the offer')} />
        }
    }

    const iconFromSelectedFilter = () => {
        if ([editScreenId, initialIndividualComponentConfigScreenId].includes(currentScreenId)) {
            return getSelectedFilterMeta()?.icon?.({
                className: classNames(
                    "min-w-5 min-h-5 max-w-5 max-h-5",
                    resolveTint(
                        popupThemeColor,
                        'popupwindow.icon.text',
                        'text-purple-tint-10',
                        'text'
                    )
                ),
                context: 'card'
            },)
        }
    }

    const mainListScreenIcon = (() => {
        const iconClassName = classNames(
            "min-w-5 min-h-5 max-w-5 max-h-5",
            resolveTint(
                popupThemeColor,
                'popupwindow.icon.text',
                'text-purple-tint-10',
                'text'
            )
        )

        if (testableType === 'offer') {
            return () => <OfferIcon className={iconClassName}/>
        }

        if (testableType === 'condition' && context.scope === 'preconditions') {
            return () => <PreconditionsIcon className={iconClassName}/>
        }

        if (testableType === 'condition' && context.scope === 'predates') {
            return () => <PredatesIcon className={iconClassName}/>
        }

        return undefined
    })();
    const isPredatesConditionMainList = testableType === 'condition' && context.scope === 'predates'
    const mainListScreenTitle = context.data?.mainWindow?.title
        ?? (
            isPredatesConditionMainList
                ? __('Schedule')
                : renderSuggestedScope(selectedTab?.title)
        )
    const mainListScreenDescription = context.data?.mainWindow?.description
        ?? (
            isPredatesConditionMainList
                ? __('Select the date & time this coupon will be available.')
                : renderSuggestedScope(selectedTab?.description)
        )
    const screens: ScreenProps[] = [
        ...[context.data?.suggestedScopeExperienceSelector? {
            id: scopeSelectionScreenId,
            title: __('What kind of promo are these items for?'), // ''What offer will these items target?'
            description: __('Select the type of offer you plan to add to get a more personalized product filtering experience.'),
            contentWidth: () => fieldsContentWidth,
            content: ({height, width}) => {
                return <ScrollableListWithFade maxHeight={height || '100%'} className="pb-4">
                    <SelectableList<typeof suggestedScopes[number]>
                        categorizedItems={{
                            uncategorized: suggestedScopes
                        }}
                        categoryData={{
                            // not currently used..
                            bogo: {
                                label: __('BOGO')
                            }
                        }}
                        itemRenderer={{
                            customRender: ({id, description, label, examples}, selectItem) => {
                                return <EmulatedButton className="p-4 border-px border-gray-200 hover:border-purple-normal rounded-1 flex flex-col gap-3 w-full group transition-all ease active:scale-95" onClick={selectItem}>
                                    <header className="flex flex-col gap-2 text-left">
                                        <div className="flex items-center gap-2">
                                            <div className="min-w-7 min-h-7 max-w-7 max-h-7 rounded-[10px] bg-gray-300/90 group-hover:bg-purple-normal text-white flex items-center justify-center transition-all ease">
                                                <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="min-w-4 min-h-4 max-w-4 max-h-4">
                                                    <path strokeLinecap="round" strokeLinejoin="round" d="M21 11.25v8.25a1.5 1.5 0 0 1-1.5 1.5H5.25a1.5 1.5 0 0 1-1.5-1.5v-8.25M12 4.875A2.625 2.625 0 1 0 9.375 7.5H12m0-2.625V7.5m0-2.625A2.625 2.625 0 1 1 14.625 7.5H12m0 0V21m-8.625-9.75h18c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125h-18c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125Z" />
                                                </svg>
                                            </div>
                                            <h1 className="text-base font-medium text-gray-750 group-hover:text-purple-normal transition-all ease">{label}</h1>
                                        </div>
                                        <p className="text-[13px] leading-4 text-gray-450">{description}</p>
                                    </header>
                                    <ul className={classNames('grid gap-1', {
                                        'grid-cols-1': true,//examples.length <= 2,
                                        //'grid-cols-2': examples.length > 2
                                    })}>
                                        {examples.map(example => <li className="flex items-start gap-1 text-gray-400">
                                            <div className="min-w-2 min-h-2 max-w-2 max-h-2 rounded-[3px] bg-gray-300 mt-1"></div>
                                            <p className="leading-4 text-smaller-2">{example}</p>
                                        </li>)}
                                    </ul>
                                </EmulatedButton>
                            },
                            content: ({label}) => {
                                return label
                            },
                            example: ({examples}) => (<div className={classNames('grid gap-2 text-smaller-1 pt-2', {
                                'grid-cols-1': examples.length <= 2,
                                'grid-cols-2': examples.length > 2
                            })}>
                                {examples.map(example => <p className="flex items-start gap-1 text-gray-450 ">
                                    <div className="min-w-2 min-h-2 max-w-2 max-h-2 rounded-[3px] bg-gray-300 mt-1"></div>
                                    <p className="leading-4">{example}</p>
                                </p>)}
                            </div>)
                        }}
                        onSelect={({id}) => {
                            // here set the suggested scope id and move on to the next screen
                            setSuggestedScope(id)
                            // move to next screen
                            next()
                        }}
                    />
                </ScrollableListWithFade>;
            },
            showBottomNavigation: false,
        } : undefined].filter(screen => !!screen),
        {
            id: mainListSelectionScreenId,
            title: mainListScreenTitle,
            description: mainListScreenDescription,
            icon: mainListScreenIcon,
            onBackToThisScreen: () => {
                reset()
            },
            contentWidth: () => fieldsContentWidth,
            content: ({height, width}) => {
                const [lastHoveredFilterType, setLastHoveredFilterType] = useState<string | null>(null)
                const selectableFiltersListHeight = typeof height === 'number' ? Math.max(0, height - (showTabs ? 54 : 0)) : '100%'
                return <div
                    className="flex flex-col items-start space-y-3 w-full " style={{textWrap: 'auto'}}>
                    {showTabs && <Tabs tabs={availableTabs} selectedTabId={testableType} onTabSelect={testableType => {
                        setTestableType(testableType as ComponentDataType)
                    }} width={'auto'} style={'transparent'}/>
                    }
                    <ScrollableListWithFade maxHeight={selectableFiltersListHeight} className="w-full pb-5">
                        <FullSelectableFiltersList />
                    </ScrollableListWithFade>
                </div>
            },
            showBottomNavigation: false,
        },
        {
            id: initialIndividualComponentConfigScreenId,
            canNavigateToThisScreen: () => {
                const filterMeta = getSelectedFilterMeta()!

                if (!filterMeta) {
                    return false;
                }
                // check if the selected filter can be configured,
                return isConfigurable(filterMeta, context.scope)
            },
            title() {
                const firstFilter = filters[0]

                if (!firstFilter?.parentType) {
                    return ''
                }

                const metaArgs = [firstFilter.parentType, firstFilter.type] as [ComponentType, string]
                return getTitleFromComponentMeta(metaArgs)
            },
            description() {
                const firstFilter = filters[0]

                if (!firstFilter?.parentType) {
                    return ''
                }

                const componentMetaArg = [firstFilter.parentType, firstFilter.type]

                return getDescFromComponentMeta(componentMetaArg)
            },
            icon: iconFromSelectedFilter,
            contentWidth: () => fieldsContentWidth,
            content: ({width, height}) => {
                const [notes, setNotes] = useState<Note[]>([])
                return <ContentWithSidebars width={width!}
                                            height={height}
                                            fitHeightToContent={true}
                                            rightSidebar={<AnimatePresence>
                                                {notes.map(({id, title, content}) => <ToolTip key={
                                                    /*
                                                        id might be the same for different notes
                                                        (this is on purpose)
                                                        we might want to add a different key for each note
                                                        so that animations work on new notees with the same id
                                                     */
                                                    id + title
                                                }
                                                                                          id={id}
                                                                                          title={title}
                                                                                          content={content}
                                                                                            />)}
                                            </AnimatePresence>}
                >
                    {hasImplementation(getSelectedFilterMeta())? FieldsToRender({fieldToRender: getSelectedFilter(), notes, setNotes}) :
                        <NonImplementedMessage />
                    }
                </ContentWithSidebars>
            },
            showBottomNavigation: () => canShowBottomNavigation() && hasImplementation(getSelectedFilterMeta()),
            primaryBottom: () => {
                return {
                    label: (supportsMultipleComponents ? __('Next') : __('Done')),
                    onClick: () => {
                        if (!supportsMultipleComponents) {
                            applyFilters();
                            return
                        } else if (testableType === 'filter' && getSelectedFilter()?.type !== QuantityMeta.id) {
                            /*
                                                        edit: let's not.

                                                        // let's add the base quantity
                                                        setFilters(filters => {
                                                            // @ts-ignore
                                                            const quantityDefaultRuntimeData = testables.getWithParentType(getDefaultRuntimeComponentData(QuantityMeta), testableType);

                                                            (quantityDefaultRuntimeData.options as QuantityOptions).type = QuantityType.Any;

                                                            const newFilters = [...filters, quantityDefaultRuntimeData];

                                                            // and the set is as the selected so that in screen 3 the user can select the quantity
                                                            setCurrentFilterId(QuantityMeta.id)

                                                            return newFilters;
                                                        })
                            */
                        }
                        next()
                    }
                }
            }
        },
        {
            id: quantityScreenId,
            canNavigateToThisScreen: () => {
                if (context.data?.supportsOptionalQuantity === false) {
                    return false;
                }
                if (testableType !== 'filter') {
                    // conditions and offers dont have a quantity to configure
                    return false;
                }
                // we're in testables, so let's check if quantity is supported
                return supportsQuantity;
            },
            title() {
                if (suggestedScope === 'bogo') {
                    return __('How many items must the customer buy?');
                }

                return <TextPreSuFixed direction={'prefix'} primary={__('Select a Quantity')} secondary={`${__('Optional')}:`} />;
            },
            description() {
                return QuantityMeta?.description?.({suggestedScope: suggestedScope || ''})
            },
            contentWidth: () => fieldsContentWidth,
            content: ({width}) => {
                const addQuantityFilter = () => addComponentByMeta(QuantityMeta, false);
                let selectedQuantityFilter = filters.find(({type}) => type === QuantityMeta.id);

                if (suggestedScope === 'bogo' && !selectedQuantityFilter) {
                    // auto select the quantity filter because its required for bogo's
                    addQuantityFilter()
                }

                const canShowOptionalTabs = suggestedScope !== 'bogo'

                return <ContentWithSidebars width={width!}>
                    <div className="space-y-2 w-full">
                        {canShowOptionalTabs && <Tabs direction={'vertical'} tabs={useMemo(() => [
                                {
                                    id: 'no-quantity' as QuantityChoice,
                                    label: __('Any Quantity'),
                                    subLabel: __('At least 1 item'),
                                },
                                {
                                    id: 'with-quantity' as QuantityChoice,
                                    label: __('Specific Quantity'),
                                    subLabel: __('Fixed, minimum, maximum, or range'),
                                }
                            ], [qtyChoice]
                        )}
                               selectedTabId={qtyChoice as QuantityChoice}
                               onTabSelect={(id) => {
                                   const choice = id as QuantityChoice
                                   if (choice === 'with-quantity') {
                                       addQuantityFilter()
                                   } else {
                                       // remove the qty filter from the state
                                       removeComponentByMeta(QuantityMeta)
                                   }
                                   setQtyChoice(choice)
                               }}
                        />}
                        {selectedQuantityFilter && <div className={"flex flex-col items-center"}>
                            {selectedQuantityFilter && false &&
                                <h3 className={"text-smaller-1 text-gray-500"}>Buy Quantity</h3>}
                            {selectedQuantityFilter && FieldsToRender({fieldToRender: selectedQuantityFilter, notes: [],
                                                                       setNotes: () => {
                                                                       }})}
                        </div>
                        }
                        <ToolTip id={'note'}
                                 content={[
                                     !suggestedScope && __('These are the number of items that will be selected. For BOGO offers, these are the number of items that the user needs to buy. For other offers, these are the number of items that might get discounted or that need to be present in the cart.')
                                 ]}
                                 style={'light'} width={'full'} animate={false}
                        />
                    </div>
                </ContentWithSidebars>
                /*
                                return <div>
                                    <p>the third screen, here ww will:</p>
                                    <p>show the otpion to whetre to add qunatity to this gourp</p>
                                    <p>theres no quantoty filter added here yet</p>
                                    <p>if the user decided to add it, then we'll manually add it and will show the fields for the
                                        quanoty</p>
                                    <p>if after adding, the user decied to not add qty, we'll remove it</p>
                                    <p>in either case, we will NOT automat8ically move the user to the next screen, all qty operations
                                        neeed be handled in this screen</p>
                                    <p>after the user has mnade a decision (and after setting the qty), the user will click next and we
                                        will go to the next 4th screen</p>
                                </div>
                */
            },
            primaryBottom: () => {
                let label = __('Next');
                // a true bool needs be checked. 'truthy' doesnt cut it here.
                if (refineCanNavigateToThisScreen() !== true) {
                    label =  __('Apply')
                }
                return {
                    label,
                }
            }
        },
        {
            id: refineSelectionScreenId,
            canNavigateToThisScreen: refineCanNavigateToThisScreen,
            title: undefined,
            description: undefined,
            contentWidth: () => fieldsContentWidth,
            content: ({height}) => {
                return <ScrollableListWithFade maxHeight={height || '100%'} className="w-full pb-4">
                    <div className="flex flex-col gap-10">
                        <div className="space-y-2">
                            <div className="relative space-y-2 --min-w-65">
                                <div className="text-gray-750 --text-center">
                                    <h1 className="text-5x --font-medium space-x-1">
                                        <span>{__('Your selected *').replace('*', {
                                            filter: __('filters'),
                                            condition: __('conditions'),
                                            default: __('components')
                                        }[testableType])}</span></h1>
                                    <p className="text-base opacity-0">f</p>
                                </div>
                                <div
                                    className="relative flex flex-row justify-center space-y-3 p-5 border-px border-gray-200 rounded-1 w-full max-w-full whitespace-nowrap flex-nowrap overflow-x-auto">
                                    <div className="grid grid-cols-1 items-center justify-center gap-1">
                                         <Context data={{
                                             id: filters[0]?.id,
                                             type: 'context',
                                             mode: testableType === 'condition' ? 'test' : 'filter',
                                             [testableType === 'offer' ? 'parentType' : 'testableType']: testableType,
                                             children: filters.map(filter => ({
                                                 ...filter,
                                                 [testableType === 'offer' ? 'parentType' : 'testableType']: testableType,
                                             })),
                                         }}
                                                  color={popupColor}
                                                   isRoot={false}
                                                   buttonsAreEnabled={false}
                                                   removable={false}
                                                   scaleOnHover={false}
                                                   animateLayout={false}
                                         />
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div className="space-y-2">
                            <div className="text-gray-750 --text-center">
                                <h1 className="text-5x --font-medium space-x-2">
                                    <span
                                    className="text-gray-550 font-normal ">{__('Optional')}:</span>
                                    <span>{{
                                        filter: __('Refine your selection'),
                                        condition: __('Restrict the scope'),
                                        default: __('Add more components')
                                    }[testableType]}</span></h1>
                                <p className="text-base text-gray-500">{{
                                    filter: __('Add more filters to create a more specific group of products.'),
                                    condition: __('Add another condition that must also be met'),
                                    default: __('Select another component')
                                }[testableType]}</p>
                            </div>
                            <FullSelectableFiltersList extraItemsToIgnore={[AnyProductsMeta.id]} nonImplementedCanBeSelected={false} />
                            {/*<div
                                className="relative px-2 py-6 border-px border-gray-200 rounded-1 w-full max-w-full whitespace-nowrap flex-nowrap">
                            </div>
                            */}
                        </div>
                    </div>
                </ScrollableListWithFade>
            },
            primaryBottom: () => {
                return ({
                    label: __('Apply'),
                    /*
                                        label: testableType === 'filter' ? __('Add Filters') : __('Add Conditions'),
                    */
                    onClick: applyFilters,
                })
            }
        },
        {
            id: editScreenId,
            disableScreenTransition: true,
            canNavigateToThisScreen: () => {
                if (isEditMode()) {
                    // check if the selected filter can be configured,
                    // if not, let's go to the 'refine' selection screen so that the user may add other filters, which should be what they want when editing
                    const selectedFilter = getSelectedFilter()
                    const selectedFilterMeta = selectedFilter ? getComponentMeta((testableType + 's' as unknown as ComponentType), selectedFilter?.type || '') : undefined

                    if (!selectedFilterMeta) {
                        return true;
                    }

                    const canBeConfigured = isConfigurable(selectedFilterMeta, context.scope) !== false

                    if (!canBeConfigured) {
                        // return an action to perfom, in this case, just close the popup successfully
                        return () => setCurrentScreenId(refineSelectionScreenId)
                    }
                }
                return true // edit mode can always be configured
            },
            title: () => {
                if (currentFilterId) {
                    const parentType = filters.find(({type}) => type === currentFilterId)?.parentType

                    if (parentType) {
                        return getTitleFromComponentMeta([parentType, currentFilterId])
                    }

                    return '';
                }

                return '';
            },
            description: () => {
                if (currentFilterId) {
                    const parentType = filters.find(({type}) => type === currentFilterId)?.parentType

                    if (parentType) {

                        return  getDescFromComponentMeta([parentType, currentFilterId])

                        //return getFieldDescription([parentType, currentFilterId])
                    }
                }
            },
            icon: iconFromSelectedFilter,
            content: ({width, height}) =>
                <ContentWithSidebars width={width!}
                                     leftSidebarPortal={false}
                                     floatingZIndex={popupWindowZIndex + 1}
                                     height={height}
                                     fitHeightToContent={true}
                                     leftSidebar={supportsMultipleComponents && <div>
                                           <Context data={{
                                              id: filters[0]?.id,
                                             type: 'context',
                                             mode: testableType === 'condition' ? 'test' : 'filter',
                                             [testableType === 'offer' ? 'parentType' : 'testableType']: testableType,
                                             children: filters.map(filter => ({
                                                 ...filter,
                                                 [testableType === 'offer' ? 'parentType' : 'testableType']: testableType,
                                             })),
                                         }}
                                                  color={popupColor}
                                                   activeItemIds={[getSelectedFilter()?.id || undefined].filter(id => !!id) as string[]}
                                                   onItemClick={filterData => setCurrentFilterId((filterData as TestableData).type)}
                                                   onRemoveClick={(filter) => {
                                                      const filterToRemove = filter as TestableData
                                                      removeComponent(filterToRemove.type)
                                                  }}
                                                  isRoot={false}
                                                  buttonsAreEnabled={false}
                                                  removable={filters.length > 1}
                                                  scaleOnHover={true}
                                                  popupScope={'popup-sidebar'}
                                         />
                                     </div>}
                                     rightSidebar={undefined}
                >
                    <div className="space-y-5 w-full">
                        {(() => {
                            const [notes, setNotes] = useState<Note[]>([])
                            const selectedFilter = getSelectedFilter()
                            const selectedFilterMeta = getSelectedFilterMeta()

                            if (!hasImplementation(selectedFilterMeta)) {
                                return <NonImplementedMessage note={unavailableComponentMessage} />
                            }

                            return FieldsToRender({fieldToRender: selectedFilter, notes,
                                                   setNotes})
                        })()}
                    </div>
                </ContentWithSidebars>,
            contentWidth: () => fieldsContentWidth,
            backTo: () => {
                const startOverScreen = {
                    id: 1,
                    label: __('Start over'),
                };

                if (isEditMode()) {
                    return startOverScreen;
                }

                return filters.length > 1 ? startOverScreen : null
            },
            showBottomNavigation: canShowBottomNavigation,
            primaryBottom: () => {
                if (isEditMode()) {
                    return {
                        label: __('Apply Changes'),
                        onClick: () => {
                            close({status: 'success', components: filters, componentType: testableType});
                            return
                        },
                    };
                }

                return ({
                    label: testableType === 'filter' ? __('Apply Filters') : __('Apply Conditions'),
                    onClick: applyFilters,
                })
            },
            secondaryBottom: supportsMultipleComponents ? (() => {
                    if (!supportsRefiningSelection()) {
                        return undefined;
                    }
                    return ({
                        label: __('Add another *').replace('*', {
                            ['filter']: __('filter'),
                            ['condition']: __('condition'),
                            ['offer']: __('offer'),
                            default: __('component'),
                        }[testableType]),
                        onClick: () => {
                            setCurrentScreenId(refineSelectionScreenId)
                        },
                    })
                }
            ) : undefined
        },
    ].filter(screen => screen !== undefined) as ScreenProps[];

    const finalizeClose = (data: Parameters<typeof onClose>[0]) => {
        // @ts-ignore
        context.data.onClose?.(data);
        onClose(cloneDeep(data), {
            deferWindowRemoval: true,
        })
    }

    const close = (data: Parameters<typeof onClose>[0], shouldAnimate: boolean = true) => {
        if (hasClosedRef.current) {
            return
        }

        hasClosedRef.current = true

        if (shouldAnimate) {
            setIsOpen(false)
            finalizeClose(data)
            return
        }

        // @ts-ignore
        context.data.onClose?.(data);
        onClose(cloneDeep(data))

        // we have got to wait a lil because the useLayoutEffect will set data again
        setTimeout(() => {
            reset(false)
        }, 600)
    }

    let canGoBack: boolean = true;

    if (context.scope === 'tier-subchild') {
        canGoBack = false
    } else if (isEditMode() && testableType === 'offer') {
        // offers in edit mode can never go back
        canGoBack = false
    }


    return <SuggestedScopeContext.Provider value={suggestedScope || ''}>
        <PopupWindow
            id={windowId}
            isOpen={isOpen}
            zIndex={popupWindowZIndex}
            color={popupThemeColor}
            onClose={() => close({
                status: 'fail',
                componentType: testableType,
                components: [],
            }, false)}
            screenId={currentScreenId}
            setCurrentScreenId={setCurrentScreenId}
            defaultScreenId={defaultScreenId}
            screens={screens}
            showBackNavigation={canGoBack}
            customTop={context?.data?.top}
            customWidth={context?.data?.width}
            headerCenteredContent={suggestedScope? <div className={"flex items-center gap-1 tracking-normal text-gray-400 text-base rounded-2 border-px border-gray-150 border-opacity-80 --bg-gray-150 bg-opacity-40 px-3 py-1"}>
                <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
                     stroke="currentColor" className="min-w-4 min-h-4 max-w-4 max-h-4">
                    <path stroke-linecap="round" stroke-linejoin="round"
                          d="M12 18v-5.25m0 0a6.01 6.01 0 0 0 1.5-.189m-1.5.189a6.01 6.01 0 0 1-1.5-.189m3.75 7.478a12.06 12.06 0 0 1-4.5 0m3.75 2.383a14.406 14.406 0 0 1-3 0M14.25 18v-.192c0-.983.658-1.823 1.508-2.316a7.5 7.5 0 1 0-7.517 0c.85.493 1.509 1.333 1.509 2.316V18"/>
                </svg>
                <span className="font-normal">{getSuggestedScopeById(suggestedScope)?.label}</span>
            </div> : undefined}
        />
    </SuggestedScopeContext.Provider>

}

/**
 * Will remove the parent too if empty
 */
function removeArrayElementAtPath(obj, path) {
    // Handle nested array paths like "options.expectedValues[0][0]"
    const matches = path.match(/(.+?)(\[\d+\])(\[\d+\])?$/);

    if (!matches) return obj; // Not a valid array path

    const [, basePath, firstBracket, secondBracket] = matches;
    const firstIndex = parseInt(firstBracket.slice(1, -1));

    // Get the parent array
    const parentArray = get(obj, basePath);

    if (!Array.isArray(parentArray)) return obj;

    if (secondBracket) {
        // Handle nested array case: "options.expectedValues[0][0]"
        const secondIndex = parseInt(secondBracket.slice(1, -1));
        const nestedArray = parentArray[firstIndex];

        if (Array.isArray(nestedArray)) {
            // Remove the element from the nested array
            nestedArray.splice(secondIndex, 1);

            // If the nested array is now empty, remove it from the parent array
            if (nestedArray.length === 0) {
                parentArray.splice(firstIndex, 1);
            }
        }
    } else {
        // Handle single-level array case: "options.expectedValues[0]"
        parentArray.splice(firstIndex, 1);
    }

    // Update the object with the modified array
    set(obj, basePath, parentArray);

    return obj;
}
