import React, {FC, useEffect, useRef, useState} from "react";
import {useAtom} from "jotai";
import classNames from "classnames";
import {POPUP_CLOSE_ANIMATION_MS, PopupWindow} from "../PopupWindow";
import {__} from "../globals";
import {EmulatedButton} from "./EmulatedButton";
import {
    getDefaultClosedTestableGroupModePopupWindowState,
    PopupWindowStateContext,
    TestableGroupModePopupWindowStateAtom,
    TreeContextData
} from "./atoms";
import {useOpenTestablesPopupWindow} from "./useOpenTestablesPopupWindow";
import {TestablesSaver} from "./TestablesSaver";
import {TestableGroupMode, TestableGroupTypeId} from "./testableGroupTypes";

type IllustrationState = 'success' | 'failure' | 'skipped-success'

type IllustrationToken = {
    state: IllustrationState,
    label?: string,
}

type IllustrationCase = {
    id: 'success' | 'failure',
    label: string,
    connector: string,
    input: IllustrationToken[],
    result: IllustrationToken[],
    emptyResultLabel?: string,
    helperText?: string,
}

type GroupModeOption = {
    id: TestableGroupMode,
    label: string,
    badge?: string,
    description: string,
    example: string,
    cases: IllustrationCase[],
}

const GroupModeIcon = () => (
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="w-6 h-6 text-purple-tint-20">
        <path opacity="0.32"
              d="M16.5 6a3 3 0 0 0-3-3H6a3 3 0 0 0-3 3v7.5a3 3 0 0 0 3 3v-6A4.5 4.5 0 0 1 10.5 6h6Z"/>
        <path
            d="M18 7.5a3 3 0 0 1 3 3V18a3 3 0 0 1-3 3h-7.5a3 3 0 0 1-3-3v-7.5a3 3 0 0 1 3-3H18Z"/>
    </svg>
)

const groupModeOptions: GroupModeOption[] = [
    {
        id: TestableGroupTypeId.AND,
        label: __('AND'),
        description: __('All product groups must match. All matcing products will be passed to the next node (usually offers)'),
        example: __('Good for product combos, bundles and pairs.'),
        cases: [
            {
                id: 'success',
                label: __('Pass'),
                connector: '+',
                input: [
                    {label: 'A', state: 'success'},
                    {label: 'B', state: 'success'},
                ],
                result: [
                    {label: 'A', state: 'success'},
                    {label: 'B', state: 'success'},
                ],
            },
            {
                id: 'failure',
                label: __('Fail'),
                connector: '+',
                input: [
                    {label: 'A', state: 'success'},
                    {label: 'B', state: 'failure'},
                ],
                result: [],
                emptyResultLabel: __('Empty group'),
                helperText: __('Any failing group clears the result.'),
            },
        ],
    },
    /*{
        id: TestableGroupTypeId.OR,
        label: __('AND/OR'),
        badge: __('Inclusive OR'),
        description: __('Will select any matching product groups.'),
        example: __('Useful for flexible product combinations where multiple matches are allowed across different product types, like any of: Products a, b, c, and/or Categories a, b.'),
        cases: [
            {
                id: 'success',
                label: __('Pass'),
                connector: __('and/or'),
                input: [
                    {label: 'A', state: 'success'},
                    {label: 'B', state: 'failure'},
                    {label: 'C', state: 'success'},
                ],
                result: [
                    {label: 'A', state: 'success'},
                    {label: 'C', state: 'success'},
                ],
                helperText: __('All passing groups are kept.'),
            },
            {
                id: 'failure',
                label: __('Fail'),
                connector: __('and/or'),
                input: [
                    {label: 'A', state: 'failure'},
                    {label: 'B', state: 'failure'},
                ],
                result: [],
                emptyResultLabel: __('Empty group'),
            },
        ],
    },
    {
        id: TestableGroupTypeId.OR_EXCLUSIVE,
        label: __('OR'),
        badge: __('Exclusive OR'),
        description: __('Will select only one matching product group. If multiple groups match, only one is selected.'),
        example: __('Useful for single choice product combinations, like 1 of: Product a, b, c OR 1 of Categories a or b.'),
        cases: [
            {
                id: 'success',
                label: __('Pass'),
                connector: __('or'),
                input: [
                    {label: 'A', state: 'failure'},
                    {label: 'B', state: 'success'},
                    {label: 'C', state: 'skipped-success'},
                ],
                result: [
                    {label: 'B', state: 'success'},
                ],
                helperText: __('Later passing groups are ignored once one group wins.'),
            },
            {
                id: 'failure',
                label: __('Fail'),
                connector: __('or'),
                input: [
                    {label: 'A', state: 'failure'},
                    {label: 'B', state: 'failure'},
                ],
                result: [],
                emptyResultLabel: __('Empty group'),
            },
        ],
    },*/
]

type IllustrationModeSizeConfig = {
    chipClassName: string,
    chipInnerBorderRadiusClassName: string,
    chipLabelClassName: string,
    iconClassName: string,
    connectorClassName: string,
    connectorStyle?: React.CSSProperties,
    resultLabelClassName: string,
    resultIconClassName: string,
    sequenceGapClassName: string,
}

const illustrationModeSizes: Record<TestableGroupMode, IllustrationModeSizeConfig> = {
    [TestableGroupTypeId.AND]: {
        chipClassName: 'h-12 min-w-[58px] rounded-[18px] px-2.5 py-2',
        chipInnerBorderRadiusClassName: 'rounded-[15px]',
        chipLabelClassName: 'left-2 top-[6px] text-[9px] tracking-[0.18em]',
        iconClassName: 'w-6 h-6',
        connectorClassName: 'px-[2px] text-2x leading-none tracking-[0.02em]',
        resultLabelClassName: 'text-[10px] leading-none tracking-[0.18em]',
        resultIconClassName: 'w-4 h-4',
        sequenceGapClassName: 'gap-1.5',
    },
    [TestableGroupTypeId.OR]: {
        chipClassName: 'h-9 min-w-10 rounded-[15px] px-2 py-1',
        chipInnerBorderRadiusClassName: 'rounded-[12px]',
        chipLabelClassName: 'left-[6px] top-[4px] text-[7px] tracking-[0.12em]',
        iconClassName: 'w-4 h-4',
        connectorClassName: 'px-0 tracking-[0.08em]',
        connectorStyle: {
            fontSize: 8,
            lineHeight: '8px',
        },
        resultLabelClassName: 'text-[8px] leading-none tracking-[0.14em]',
        resultIconClassName: 'w-3 h-3',
        sequenceGapClassName: 'gap-1',
    },
    [TestableGroupTypeId.OR_EXCLUSIVE]: {
        chipClassName: 'h-11 min-w-[52px] rounded-[17px] px-2.5 py-1.5',
        chipInnerBorderRadiusClassName: 'rounded-[14px]',
        chipLabelClassName: 'left-[7px] top-[5px] text-[8px] tracking-[0.16em]',
        iconClassName: 'w-5 h-5',
        connectorClassName: 'px-[1px] tracking-[0.12em]',
        connectorStyle: {
            fontSize: 10,
            lineHeight: '10px',
        },
        resultLabelClassName: 'text-[9px] leading-none tracking-[0.16em]',
        resultIconClassName: 'w-[14px] h-[14px]',
        sequenceGapClassName: 'gap-1',
    },
}

const PassGlyph: FC<{className?: string}> = ({className}) => {
    return <svg xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 24 24"
                fill="currentColor"
                className={className}>
        <path fillRule="evenodd"
              d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z"
              clipRule="evenodd"/>
    </svg>
}

const FailGlyph: FC<{className?: string}> = ({className}) => {
    return <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.25Zm-1.72 6.97a.75.75 0 1 0-1.06 1.06L10.94 12l-1.72 1.72a.75.75 0 1 0 1.06 1.06L12 13.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L13.06 12l1.72-1.72a.75.75 0 1 0-1.06-1.06L12 10.94l-1.72-1.72Z"
              clipRule="evenodd"/>
    </svg>
}

const StatusGlyph: FC<{state: IllustrationState, className: string}> = ({state, className}) => {
    const isSuccess = state === 'success' || state === 'skipped-success'

    return isSuccess
        ? <PassGlyph className={className}/>
        : <FailGlyph className={className}/>
}

const IllustrationChip: FC<{token: IllustrationToken, mode: TestableGroupMode}> = ({token, mode}) => {
    const isSuccess = token.state === 'success'
    const isFailure = token.state === 'failure'
    const isSkipped = token.state === 'skipped-success'
    const size = illustrationModeSizes[mode]

    return <div className={classNames('relative flex items-center justify-center overflow-hidden border backdrop-blur-md shadow-card', size.chipClassName, {
        'border-purple-tint-20 bg-gradient-to-br from-purple-tint-10 to-purple-tint-5 text-purple-tint-90': isSuccess,
        'border-gray-200 bg-gradient-to-br from-gray-100 to-white text-gray-400': isFailure,
        'border-purple-tint-25 bg-gradient-to-br from-purple-tint-10 to-white text-purple-tint-70': isSkipped,
    })}>
        <span className={classNames('pointer-events-none absolute inset-[3px] border', size.chipInnerBorderRadiusClassName, {
            'border-white border-opacity-70': isSuccess,
            'border-white border-opacity-60': isFailure,
            'border-white border-opacity-50': isSkipped,
        })}/>
        <span className={classNames('pointer-events-none absolute inset-x-3 bottom-[-20px] h-8 rounded-full blur-xl', {
            'bg-purple-tint-30 bg-opacity-70': isSuccess,
            'bg-gray-200 bg-opacity-70': isFailure,
            'bg-purple-tint-20 bg-opacity-50': isSkipped,
        })}/>
        {token.label && <span className={classNames('absolute font-black leading-none', size.chipLabelClassName, {
            'text-purple-tint-70': isSuccess,
            'text-gray-350': isFailure,
            'text-purple-tint-60 opacity-70': isSkipped,
        })}>
            {token.label}
        </span>}
        <span className={classNames('relative z-10 flex items-center justify-center', {
            'opacity-50': isSkipped,
        })}>
            <StatusGlyph state={token.state} className={size.iconClassName}/>
        </span>
        {isSkipped && <>
            <span className="pointer-events-none absolute inset-0 bg-white bg-opacity-35"/>
            <span className="pointer-events-none absolute inset-x-1 top-1/2 h-px -translate-y-1/2 -rotate-[16deg] bg-purple-tint-70 opacity-60"/>
        </>}
    </div>
}

const IllustrationConnector: FC<{children: React.ReactNode, mode: TestableGroupMode}> = ({children, mode}) => {
    const size = illustrationModeSizes[mode]
    return <div
        className={classNames('font-semibold uppercase text-purple-tint-60', size.connectorClassName)}
        style={size.connectorStyle}
    >
        {children}
    </div>
}

const EmptyResultChip: FC<{label: string}> = ({label}) => {
    return <div className="rounded-[18px] border border-dashed border-gray-250 bg-white bg-opacity-60 px-3 py-2 text-smaller-1 font-medium text-gray-400">
        {label}
    </div>
}

const ResultMarker: FC<{mode: TestableGroupMode}> = ({mode}) => {
    const size = illustrationModeSizes[mode]

    return <div className="flex items-center gap-1 px-[2px]">
        <span className={classNames('font-semibold uppercase text-gray-350', size.resultLabelClassName)}>
            {__('Result')}
        </span>
        <svg xmlns="http://www.w3.org/2000/svg"
             viewBox="0 0 20 20"
             fill="currentColor"
             className={classNames('text-purple-tint-45', size.resultIconClassName)}>
            <path fillRule="evenodd"
                  d="M3.25 10a.75.75 0 0 1 .75-.75h9.19L10.72 6.78a.75.75 0 1 1 1.06-1.06l3.75 3.75a.75.75 0 0 1 0 1.06l-3.75 3.75a.75.75 0 0 1-1.06-1.06l2.47-2.47H4A.75.75 0 0 1 3.25 10Z"
                  clipRule="evenodd"/>
        </svg>
    </div>
}

const IllustrationCaseCard: FC<{modeCase: IllustrationCase, mode: TestableGroupMode}> = ({modeCase, mode}) => {
    const size = illustrationModeSizes[mode]

    return <div className={classNames('rounded-1 border p-3 space-y-2.5', {
        'border-purple-tint-15 bg-purple-tint-5 bg-opacity-95': modeCase.id === 'success',
        'border-gray-150 bg-white bg-opacity-80': modeCase.id === 'failure',
    })}>
        <div className="overflow-x-auto overflow-y-hidden pb-1 pr-1">
            <div className="flex min-w-max items-center gap-2">
                <span className={classNames('shrink-0 text-[10px] font-semibold uppercase leading-none tracking-[0.18em]', {
                'text-purple-tint-75': modeCase.id === 'success',
                'text-gray-450': modeCase.id === 'failure',
                })}>
                    {modeCase.label}
                </span>
                <div className={classNames('flex items-center', size.sequenceGapClassName)}>
                    {modeCase.input.map((token, index) => (
                        <React.Fragment key={`${modeCase.label}-input-${index}`}>
                            {index > 0 && <IllustrationConnector mode={mode}>{modeCase.connector}</IllustrationConnector>}
                            <IllustrationChip token={token} mode={mode}/>
                        </React.Fragment>
                    ))}
                    <ResultMarker mode={mode}/>
                    {modeCase.result.length > 0
                        ? modeCase.result.map((token, index) => (
                            <IllustrationChip key={`${modeCase.label}-result-${index}`} token={token} mode={mode}/>
                        ))
                        : <EmptyResultChip label={modeCase.emptyResultLabel || __('Empty group')}/>}
                </div>
            </div>
        </div>
        {modeCase.helperText && <p className={classNames('text-smaller-1', {
            'text-purple-tint-60': modeCase.id === 'success',
            'text-gray-400': modeCase.id === 'failure',
        })}>
            {modeCase.helperText}
        </p>}
    </div>
}

export const TestableGroupModePopupWindow: FC = () => {
    const [popupWindowState, setPopupWindowState] = useAtom(TestableGroupModePopupWindowStateAtom)
    const openTestablePopupWindow = useOpenTestablesPopupWindow()
    const [isOpen, setIsOpen] = useState(false)
    const hasClosedRef = useRef(false)
    const closeStartTimeoutRef = useRef<number | null>(null)
    const closeRemovalTimeoutRef = useRef<number | null>(null)
    const isConditionsFlow = popupWindowState.context.data.componentType === 'condition'

    const clearPendingCloseTimeouts = () => {
        if (closeStartTimeoutRef.current !== null) {
            window.clearTimeout(closeStartTimeoutRef.current)
            closeStartTimeoutRef.current = null
        }

        if (closeRemovalTimeoutRef.current !== null) {
            window.clearTimeout(closeRemovalTimeoutRef.current)
            closeRemovalTimeoutRef.current = null
        }
    }

    const resetPopupState = () => {
        clearPendingCloseTimeouts()
        hasClosedRef.current = false
        setIsOpen(false)
        setPopupWindowState(getDefaultClosedTestableGroupModePopupWindowState())
    }

    useEffect(() => {
        if (!popupWindowState.isOpen) {
            return
        }

        clearPendingCloseTimeouts()
        hasClosedRef.current = false
        setIsOpen(true)
    }, [popupWindowState.isOpen])

    useEffect(() => {
        return () => {
            clearPendingCloseTimeouts()
        }
    }, [])

    const closeWithAnimation = (delayToPreserveStackRegistration: boolean = false) => {
        if (hasClosedRef.current) {
            return
        }

        hasClosedRef.current = true

        const startClosing = () => {
            setIsOpen(false)
            closeRemovalTimeoutRef.current = window.setTimeout(() => {
                resetPopupState()
            }, POPUP_CLOSE_ANIMATION_MS)
        }

        if (delayToPreserveStackRegistration) {
            closeStartTimeoutRef.current = window.setTimeout(startClosing, 0)
            return
        }

        startClosing()
    }

    const openTestablesPopupForMode = (groupType: TestableGroupMode) => {
        const context: PopupWindowStateContext<TreeContextData> = {
            id: `context-right-button-${popupWindowState.context.data.targetId}`,
            scope: 'tree-node',
            data: {
                componentType: popupWindowState.context.data.componentType,
                targetType: 'testableComposite',
                targetId: popupWindowState.context.data.targetId,
                groupType,
                extraData: {
                    suggestedScope: popupWindowState.context.data.suggestedScope,
                }
            }
        }

        openTestablePopupWindow({
            context,
            onClose: (data) => {
                if (data.status !== 'success' || !data.components) {
                    return
                }

                const saver = new TestablesSaver(context)
                saver.add(data.components)
                closeWithAnimation(true)

            }
        })
    }

    if (!popupWindowState.isOpen && !isOpen) {
        return null
    }

    return <PopupWindow
        id={popupWindowState.context.id || 'testable-group-mode'}
        isOpen={isOpen}
        screens={[
            {
                id: 1,
                title: __('Choose group mode'),
                icon: () => <GroupModeIcon/>,
                description: isConditionsFlow
                    ? __('Choose how the new set of conditions should combine with the current group. You\'ll pick the conditions in the next popup.')
                    : __('Choose how the new set of products should combine with the current group. You\'ll pick the products in the next popup.'),
                content: ({height}) => {
                    return <div className="w-full overflow-y-auto overflow-x-hidden pr-2" style={{maxHeight: height}}>
                        <div className="flex flex-col gap-4 pb-6">
                            {groupModeOptions.map((option) => (
                                <EmulatedButton
                                    key={option.id}
                                    className="group w-full rounded-[28px] border-px border-gray-200 bg-white bg-opacity-60 px-5 py-5 text-left transition-all duration-200 hover:border-purple-tint-60 hover:bg-purple-tint-10 hover:bg-opacity-70 active:scale-[.97] transition-all ease"
                                    onClick={() => openTestablesPopupForMode(option.id)}
                                >
                                    <div className="flex flex-col gap-4">
                                        <div className="flex items-start justify-between gap-3">
                                            <div className="space-y-2">
                                                <div className="flex flex-wrap items-center gap-2">
                                                    <span className="text-2x font-medium text-gray-700 group-hover:text-purple-tint-90">{option.label}</span>
                                                    {option.badge && <span
                                                        className="rounded-full border-px border-purple-tint-40 bg-purple-tint-10 bg-opacity-80 px-2 py-1 text-smaller-2 font-medium text-purple-tint-80">
                                                        {option.badge}
                                                    </span>}
                                                </div>
                                                <p className="text-base text-gray-500">{option.description}</p>
                                            </div>
                                            <div className="rounded-[18px] border border-gray-200 bg-white bg-opacity-60 px-3 py-2 text-smaller-1 text-gray-400 max-w-[50%]">
                                                {option.example}
                                            </div>
                                        </div>
                                        <div className="flex flex-col gap-3">
                                            {option.cases.map((modeCase) => (
                                                <IllustrationCaseCard key={`${option.id}-${modeCase.id}`} modeCase={modeCase} mode={option.id}/>
                                            ))}
                                        </div>
                                    </div>
                                </EmulatedButton>
                            ))}
                            <p className="px-2 text-smaller-1 text-gray-350">
                                {isConditionsFlow
                                    ? __('Once you choose a mode, the next popup will open so you can select the new set of conditions.')
                                    : __('Once you choose a mode, the next popup will open so you can select the new set of products.')}
                            </p>
                        </div>
                    </div>
                },
                showBottomNavigation: false,
            }
        ]}
        screenId={1}
        defaultScreenId={1}
        setCurrentScreenId={() => {}}
        onClose={() => resetPopupState()}
    />
}
