import React, {FC, useEffect, useMemo, useRef, useState} from "react";
import {TestableCompositeData, TestableCompositeDataWithChildren, TestableData, useNodesStore} from "./store";
import {CardItemProps} from "./CardItem";
import {colors, ConditionColorPalette, defaultConditionColorPalette} from "./Colors";
import classNames from "classnames";
import {__} from "../globals";
import {RightContextButtons, RightContextButtonsProps} from "./RightContextButtons";
import {Color, ColorWithCustomTint, request} from "./Color";
import {useChildrenIds, useNodeData, useParentId, useTestableTypeFromContext} from "./testables";
import {atom, useAtomValue, useSetAtom} from "jotai";
import {HoveredContextIDAtom, PopupWindowStateContext, TestablesPopupWindowStateAtom, TreeContextData} from "./atoms";
import {RemoveButton} from "./RemoveButton";
import {useOpenEditTestablePopup} from "./hooks";
import {ContextCardItem} from "./ContextCardItem";
import {Card, CardProps} from "./Card";
import {RightContextButtonProps} from "./RightContextButton";
import {motion} from 'motion/react';
import {TestablesSaver} from "./TestablesSaver";
import {useOpenTestablesPopupWindow} from "./useOpenTestablesPopupWindow";
import {getSuggestedScopeForTestable} from "./getSuggestedScopeForTestable";
import {TargetedConfirmDialog} from "../TargetedConfirmDialog";
import {TestableGroupTypeId} from "./testableGroupTypes";
import {applyConditionContextCardTheme, getConditionContextCardTheme} from "./conditionCardTheme";

export type ContextProps =
/*
    Pick<TestableCompositeData, 'id'>
*/
    & Pick<RightContextButtonProps, 'onRightButtonHoverStart' | 'onRightButtonHoverEnd'>
    & Pick<CardProps, 'fullWidth' | 'color' | 'scaleOnHover' | 'animateLayout'>
    & {
    id?: string, // only needed when data is not provided
    data: TestableCompositeData | TestableCompositeDataWithChildren,
    buttonsAreEnabled?: boolean,
    removable?: boolean,
    confirmRemoval?: boolean,
    showRemoveButtonLabel?: boolean,
    buttonsDisplay?: RightContextButtonsProps['displayType'],
    onRightButtonsClick?: RightContextButtonsProps['onClick'],
    showRightContextButtonsLabel?: boolean, // defaults to true
    highlight?: boolean,
    onItemClick?: (id: string | TestableData) => void,
    onRemoveClick?: (id: string | TestableData) => void,
    activeItemIds?: string[],
    supportsMultipleComponents?: boolean, // for the testables popup when clicking an existing context, setting this to false will make the popup not show the add another filter/condition- defaults to
    unsupportedComponents?: [],
    // true
    floatButtons?: boolean,
    conditionColorPalette?: ConditionColorPalette,
    popupScope?: PopupWindowStateContext<TreeContextData>['scope'] | 'popup-sidebar',
    popupSupportedComponents?: string[],
    popupShowTabs?: boolean,
    popupConditionColorPalette?: ConditionColorPalette,
}


export const Context: FC<ContextProps> = ({
                                               id,
                                               data,
                                              buttonsDisplay,
                                              highlight,
                                              onRightButtonHoverStart,
                                              onRightButtonHoverEnd,
                                              removable = true,
                                              confirmRemoval = true,
                                              buttonsAreEnabled = true, // ideally this should be false but this was added after it was implemented in several places already so the default has to be true
                                              onItemClick,
                                              onRemoveClick,
                                              activeItemIds = [],
                                              fullWidth = false,
                                              color = colors.purple,
                                              scaleOnHover,
                                              animateLayout = true,
                                               supportsMultipleComponents = true,
                                                unsupportedComponents = [],
                                               showRemoveButtonLabel = true,
                                               onRightButtonsClick,
                                               floatButtons = false,
                                               showRightContextButtonsLabel = true,
                                               conditionColorPalette,
                                               popupScope = 'tree-node',
                                               popupSupportedComponents,
                                               popupShowTabs = false,
                                               popupConditionColorPalette,
                                           }) => {
    id = id || data.id
    let useDataObjectInsteadOfID = !!data?.children;
    const conditionOrFilterIds: (TestableData['id'] | TestableData)[] = useChildrenIds(useDataObjectInsteadOfID ? data : id) // in case data is stale
    const testableType = useTestableTypeFromContext(useDataObjectInsteadOfID ? data : id)
    const parentId = useParentId(id)
    const requestedConditionPalette = conditionColorPalette || data?.conditionColorPalette || popupConditionColorPalette
    const shouldUseConditionCardTheme =
        testableType === 'condition'
        || popupScope === 'preconditions'
        || popupScope === 'predates'
        || Boolean(requestedConditionPalette)
    const conditionThemePlace = (
        popupScope === 'preconditions'
        || popupScope === 'predates'
        || popupScope === 'tier-base'
        || popupScope === 'tier-subchild' || popupScope === 'popup-sidebar'
    ) ? popupScope : 'context'
    const conditionCardTheme = shouldUseConditionCardTheme
        ? getConditionContextCardTheme(requestedConditionPalette || defaultConditionColorPalette, conditionThemePlace)
        : undefined
    const contextCardColor: Color | ColorWithCustomTint = useMemo(
        () => applyConditionContextCardTheme(color, conditionCardTheme),
        [color, conditionCardTheme]
    )
    const parentTestableData = useNodeData(parentId)
    const isInsideAND = parentTestableData?.type === TestableGroupTypeId.AND
    const setHoveredContextID = useSetAtom(HoveredContextIDAtom)
    const isHoveredAtom = useMemo(() => atom(get => get(HoveredContextIDAtom) === id), [id])
    const isHovered = useAtomValue(isHoveredAtom)
    const [isHighlighted, setHighlighted] = useState(false)
    const shouldBeHighlighted = highlight || isHighlighted;
    const removeTestable = useNodesStore(store => store.removeTestable)
    const setTestablesPopupWindowState = useSetAtom(TestablesPopupWindowStateAtom)
    const openTestablePopupWindow = useOpenTestablesPopupWindow();
    const openEditTestablePopup = useOpenEditTestablePopup({
        testableType,
        supportsMultipleComponents,
        unsupportedComponents,
        scope: popupScope,
        supportedComponents: popupSupportedComponents,
        conditionColorPalette: popupConditionColorPalette || conditionColorPalette,
        showTabs: popupShowTabs,
    })
    const [isRemoveConfirmOpen, setIsRemoveConfirmOpen] = useState(false)
    const [removeOriginRect, setRemoveOriginRect] = useState<DOMRect | null>(null)
    const removeTitle = testableType === 'condition'
        ? __('Delete this group of conditions?')
        : __('Delete this group of product filters?')
    const removeDescription = testableType === 'condition'
        ? __('This will remove this group and all its conditions.')
        : __('This will remove this group and all its product filters.')

    const confirmRemoveContext = () => {
        removeTestable(id)
        setHoveredContextID(null)
        setIsRemoveConfirmOpen(false)
    }

    const rightContextButtons = buttonsAreEnabled && <RightContextButtons color={{
                                                                                color,
                                                                                tint: (color, type, context, path) => {
                                                                                    if (context === 'button.label.background' && !showRightContextButtonsLabel) {
                                                                                        return color.background(60)
                                                                                    }
                                                                                }
                                                                            } as ColorWithCustomTint}
                                                                          contextID={id}
                                                                          displayType={buttonsDisplay}
                                                                          isOpened={true}
                                                                          parentData={parentTestableData as TestableCompositeData}
                                                                          onRightButtonHoverStart={onRightButtonHoverStart}
                                                                          onRightButtonHoverEnd={onRightButtonHoverEnd}
                                                                          onClick={onRightButtonsClick}
                                                                          showLabel={showRightContextButtonsLabel}
    />

    const containerRef = useRef<HTMLDivElement | null>(null)
    let [height, setHeight] = useState<number | 'auto'>('auto')
    const [cardItemHeights, setCardItemHeights] = useState<Record<number, number>>({})
    const cardItemsSignature = conditionOrFilterIds.map((conditionOrFilterId) => {
        return typeof conditionOrFilterId === 'object' ? conditionOrFilterId.id : conditionOrFilterId
    }).join('|')

    useEffect(() => {
        if (containerRef.current) {
            const resizeObserver = new ResizeObserver((entries) => {
                // We only have one entry, so we can use entries[0].
                const observedHeight = entries[0].contentRect.height
                setHeight(observedHeight)
            })

            resizeObserver.observe(containerRef.current)

            return () => {
                // Cleanup the observer when the component is unmounted
                resizeObserver.disconnect()
            }
        }
    }, [])

    useEffect(() => {
        setCardItemHeights({})
    }, [id, cardItemsSignature])

    if (isHovered && typeof height === 'number') {
        //height = height + 100
    }

    return <motion.div style={{height}} animate={animateLayout ? {height} : undefined} transition={animateLayout ? {duration: 0.1} : undefined}
                       className={classNames("---h-10 ----hover:h-40 ---transition-all ---duration-300", {
                           'flex items-center': !floatButtons,
                       })}
                       onMouseEnter={() => setHoveredContextID(id)}
                       onMouseLeave={() => setHoveredContextID(null)}
    >
        <div ref={containerRef} className={classNames({
            'w-full': fullWidth,
        })}>
            <Card color={contextCardColor}
                  highlight={shouldBeHighlighted}
                  blur={false}
                  onHoverStart={() => setHoveredContextID.bind(null, id)}
                  onHoverEnd={() => setHoveredContextID.bind(null, null)}
                  outerRing={data?.mode === 'filter'}
                  fullWidth={fullWidth}
                  scaleOnHover={scaleOnHover}
                  animateLayout={animateLayout}
                  bottomButton={false ? {
                      icon: <>{false && <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
                                             className="h-3">
                          <path fillRule="evenodd"
                                d="M12 3.75a.75.75 0 0 1 .75.75v6.75h6.75a.75.75 0 0 1 0 1.5h-6.75v6.75a.75.75 0 0 1-1.5 0v-6.75H4.5a.75.75 0 0 1 0-1.5h6.75V4.5a.75.75 0 0 1 .75-.75Z"
                                clipRule="evenodd"/>
                      </svg>}
                          {true && <svg width="8" height="8" viewBox="0 0 8 8" fill="none"
                                        xmlns="http://www.w3.org/2000/svg">
                              <rect width="8" height="8" rx="4" fill="currentColor"/>
                              <path d="M4 2V6M6 4H2" stroke={'#fefefe'} stroke-width="1.5" stroke-linecap="round"
                                    stroke-linejoin="round"/>
                          </svg>}</>,
                      text: __('Add filter'),
                      onClick: () => {
                      }
                  } : undefined}
                  afterContent={<>
                      {removable && <RemoveButton onClick={(event) => {
                          if (confirmRemoval) {
                              setRemoveOriginRect(event.currentTarget.getBoundingClientRect())
                              setIsRemoveConfirmOpen(true)
                          } else {
                              confirmRemoveContext()
                          }
                       }}
                                                   entityID={id}
                                                   show={isHovered}
                                                   onRequestToHighlight={setHighlighted.bind(this, true)}
                                                   onRequestToUnHighlight={setHighlighted.bind(this, false)}
                                                  showLabel={showRemoveButtonLabel}
                      />}
                      {floatButtons && <div>
                          {rightContextButtons && <div
                              key="right-context-buttons"
                              className="absolute left-full top-1/2 -translate-y-1/2"
                              initial={{ opacity: 0, x: -8 }}
                              animate={{ opacity: 1, x: 0 }}
                              exit={{ opacity: 0, x: -8 }}
                              transition={{ duration: 0.25, ease: 'easeOut' }}
                          >
                              {rightContextButtons}
                          </div>}
                      </div>}
                  </>}
            >
                {conditionOrFilterIds.map((conditionOrFilterId, index) => {
                    const previousCardItemHeight = cardItemHeights[index - 1]
                    const currentCardItemHeight = cardItemHeights[index]
                    const topConnectorMarkerCenterOffset = previousCardItemHeight && currentCardItemHeight
                        ? (previousCardItemHeight + currentCardItemHeight) / 4
                        : undefined

                    return <ContextCardItem
                        color={contextCardColor}
                        id={typeof conditionOrFilterId === 'object' ? conditionOrFilterId.id : conditionOrFilterId}
                        data={typeof conditionOrFilterId === 'object' ? conditionOrFilterId : undefined}
                        active={activeItemIds.includes(typeof conditionOrFilterId === 'object' ? conditionOrFilterId.id : conditionOrFilterId)}
                        onMeasuredHeightChange={(measuredHeight) => {
                            setCardItemHeights((previousHeights) => {
                                if (previousHeights[index] === measuredHeight) {
                                    return previousHeights
                                }

                                return {
                                    ...previousHeights,
                                    [index]: measuredHeight,
                                }
                            })
                        }}
                        onClick={async () => {
                            const testableId = typeof conditionOrFilterId === 'object' ? conditionOrFilterId.id : conditionOrFilterId;
                            const suggestedScope = await getSuggestedScopeForTestable(testableId);

                            if (onItemClick) {
                                onItemClick(conditionOrFilterId)
                                return
                            }

                            openEditTestablePopup(testableId, suggestedScope!)

                            return
                            const context = {
                                id: 'context-edit',
                                scope: 'tree-node',
                                data: {
                                    componentType: testableType,
                                    targetType: 'testableComposite',
                                    supportsMultipleComponents,
                                    unsupportedComponents,
                                    mode: 'edit',
                                    targetId: conditionOrFilterId,
                                    showTabs: false,
                                    extraData: {
                                        suggestedScope,
                                    }
                                }
                            } as PopupWindowStateContext<TreeContextData>

                            openTestablePopupWindow({
                                context,
                                onClose: (data) => {
                                    if (data.status !== 'success' || !data.components) {
                                        return
                                    }
                                    // @ts-ignore
                                    const saver = new TestablesSaver(context)

                                    saver.update(data.components)
                                }
                            });
                            /*
                                                                                        setTestablesPopupWindowState({
                                                                                            isOpen: true,
                                                                                            context: {
                                                                                                scope: 'tree-node',
                                                                                                data: {
                                                                                                    targetType: 'testableComposite',
                                                                                                    mode: 'edit',
                                                                                                    targetId: conditionOrFilterId
                                                                                                }
                                                                                            }
                                                                                        })
                            */
                        }}
                        onRemoveClick={
                            /*onRemoveClick ? (() => onRemoveClick?.(conditionOrFilterId)) : undefined*/
                            removable? (() => {
                                if(onRemoveClick) {
                                    onRemoveClick(conditionOrFilterId)
                                } else if(removable) {
                                    typeof conditionOrFilterId === 'string' && removeTestable(conditionOrFilterId)
                                }
                            }) : undefined
                        }
                        dynamicRemoveButton={!removable?  false : (removable || !!onRemoveClick) }
                        topConnectorMarkerCenterOffset={topConnectorMarkerCenterOffset}
                        topConnectorMarker={
                            testableType === 'condition'
                            && conditionOrFilterIds.length > 1
                            && index > 0
                                ? (
                                    <span className={classNames(
                                        'flex items-center justify-center min-w-3 min-h-3 max-w-3 max-h-3 rounded-full text-smaller-1 leading-none font-semibold border-px border-opacity-40',
                                        request(contextCardColor, 'condition-connector-marker').get('background.dark'),
                                        request(contextCardColor, 'condition-connector-marker').text(10),
                                        request(contextCardColor, 'condition-connector-marker').border(20),
                                    )}>
                                        +
                                    </span>
                                )
                                : undefined
                        }
                        position={classNames({
                            'first': index === 0,
                            'last': index + 1 === conditionOrFilterIds.length,
                            'middle': index > 0 && (index + 1) < conditionOrFilterIds.length
                        }) as CardItemProps['position']}
                    />
                })}
            </Card>
            <TargetedConfirmDialog
                isOpen={isRemoveConfirmOpen}
                originRect={removeOriginRect}
                title={removeTitle}
                description={removeDescription}
                confirmLabel={__('Yes')}
                cancelLabel={__('No')}
                onConfirm={confirmRemoveContext}
                onCancel={() => setIsRemoveConfirmOpen(false)}
            />
        </div>
        {!floatButtons && rightContextButtons}
    </motion.div>;
}
