import {atom, createStore} from "jotai";
import {cloneDeep} from "lodash";
import {ComponentRuntimeDataWithParentType} from "./components";
import {ComponentDataType} from "./store";
import {TreeDashboardSection} from "./TreeDashboardSections";
import {ReactFlowInstance} from "@xyflow/react";
import mitt from "mitt";
import {PreloadedDynamicValues} from "./export/ComponentExporterImporter";
import {NotInCartNotification, ProductData} from "./cards/BuyXGetY";
import {PopupWindowProps} from "../PopupWindow";
import {SelectOption} from "./fields/MultiSelectSearch";
import {ReactNode} from "react";
import {TestableGroupMode} from "./testableGroupTypes";
import type {ConditionColorPalette} from "./Colors";

export type Prettify<T> = {
    [K in keyof T]: Prettify<T[K]>;
} & {}
/**
 * Atoms here are used for performance critical global state that is only used by specific components.
 *
 * Using Zustand for this would make the app re-render the whole tree on every change.
 */

export const atomsStore = createStore()
export const ActivePopupWindowIdsAtom = atom<string[]>([])
export const HoveredContextIDAtom = atom<null | string>(null)
export const HoveredTestableCompositeIDAtom = atom<null | string>(null)
export const LayoutAnimationInProgressAtom = atom(false)

export const HorizontalOffsetAtom = atom(0)
export const VerticalOffsetAtom = atom(0)
export const DocumentDimensionsAtom = atom({
    width: 0,
    height: 0,
})

export const leftSidebarWidthAtom = atom<number>(50 * 4) // this doesn't really change but i want to keep it consistent with the right sidebar which is dynamic
export const rightSidebarWidthAtom = atom<number>(0)
export const sidebarEdgeOffsetAtom = atom<number>(3 * 4) // again, doesn't change either but its here for consistency
export const FloatingHeaderHeightAtom = atom<number>(9 * 4) // again currently doesn't change but its here for consistency and for future use because this might become dynamic so might as well start with an atom
export const MobileSidebarOpenAtom = atom<boolean>(false)
export const MobileOptionsPanelOpenAtom = atom<boolean>(false)
export const CurrentDashboardSectionIdAtom = atom<TreeDashboardSection>(TreeDashboardSection.Offers)

export const mainReactFlowInstanceAtom = atom<ReactFlowInstance | null>(null)
export const treeEmitterAtom = atom(mitt<{ 'renderedNodes:stabilized': ReactFlowInstance }>())
/**
 * Example:
 * {
 *   popupWindowId: screenId
 * }
 */
export const CurrentScreenIdsAtom = atom<Record<string, number>>({})
export const viewportDimensionsAtom = atom({
    width: 0,
    height: 0,
})

const ModelBaseAtom = atom<'tree' | 'rows' | 'classic'>('tree')
export const LastModelAtom = atom<'tree' | 'rows' | 'classic' | null>(null)

export const ModelAtom = atom(
    (get) => get(ModelBaseAtom),
    (get, set, newMode: 'tree' | 'rows' | 'classic') => {
        set(ModelBaseAtom, newMode)
    }
)


// Intercept writes: appending a single value, replacing with an array, or handling functional updates
export const DynamicTestableDataBaseAtom = atom<PreloadedDynamicValues[]>([])

export type WindowData = {
    window: {
        title?: ReactNode,
        description?: ReactNode,
    }
}
export type PopupWindowStateContext<ContextData> = {
    id: string, // unique id for each popup window, used to identify the popup
    scope: 'tree-node' | 'tier-base' | 'tier-subchild' | 'tier-branch' | 'custom-filtered-items-map' | 'preconditions' | 'predates' | 'prefilters',
    data: ContextData & Partial<WindowData>,
};

export type PopupWindowState<ContextData> = {
    isOpen: boolean,
    context: PopupWindowStateContext<ContextData> | ContextData,
};

export type TreeContextData = Prettify<{
        componentType: ComponentDataType,
        supportedComponentTypes?: ComponentDataType[],
        unsupportedComponentTypes?: ComponentDataType[],
        supportsOptionalQuantity?: boolean, // this is window-specific, ignoring individual components, default is true
        supportedComponents?: string[], // eg: Categories.id, Brands.id, etc
        unsupportedComponents?: string[], // eg: Categories.id, Brands.id, etc
        supportsMultipleComponents?: boolean, // default is true
        suggestedScopeExperienceSelector?: boolean,
        suggested?: {
            type: 'category' | 'component', // a whole category, 'like' BOGO or a specific component like 'BuyXGetY'
            id: string, // possibly in the future that multiple ids can be supported
        }
        groupType?: TestableGroupMode,
        conditionColorPalette?: ConditionColorPalette,
        extraData?: any,
        showTabs?: boolean,
        mainWindow?: {
            title?: string,
            description?: string,
        },
        onClose?: PopupWindowsState['onClose'],
        top?: PopupWindowProps['customTop'],
        width?: PopupWindowProps['customWidth']
    }
    &
    (
        // this is for when adding a new root tree
        {
            targetType: 'root',
            targetId: number, // index of the root
        }
        |
        // this is for adding to (or editing!) an existing group
        {
            targetType: 'testableComposite' | 'testablePartial',
            mode?: 'create' | 'edit',
            targetId: string,
        }
        |
        // this is for adding testables to the memory, but they are not directly inserted in the tree
        ({
            targetType: 'ghost',
        } &
            ({
                    mode: 'create',
                }
                | {
                mode: 'edit',
                targetId: string, // an existing testable id because mode is 'edit',
            }))
        )>;
export type PopupWindowsState<ExtraData = any> = {
    context: PopupWindowStateContext<TreeContextData>,
    onClose: (data: {
        status: 'success' | 'fail',
        components: ComponentRuntimeDataWithParentType[],
        componentType: ComponentDataType,
        extraData?: ExtraData,
    }, options?: {
        deferWindowRemoval?: boolean
        skipCallback?: boolean
    }) => void
}
export type PopupWindowCloseOptions = {
    deferWindowRemoval?: boolean
    skipCallback?: boolean
}
export const TestablesPopupWindowsAtom = atom<PopupWindowsState[]>([/*{
    context: {
        id: 'offers-new',
        scope: 'tree-node',
        data: {
            componentType: 'offer',
            supportedComponentTypes: ['offer'], // only offers pls
            targetType: 'root', // root | testableComposite
            targetId: null, // string id if testableComposite, index if root (eg: 0 for the first root, 1 for the second root, etc)
        }
    },
    onClose: () => {
    }
}*/])


export const TestablesPopupWindowStateAtom = atom<PopupWindowState<TreeContextData>>({
    isOpen: false,
    context: {
        id: "tree-node",
        data: {
            targetType: 'root',
            targetId: 0,
        },
    },
})

export type TestableGroupModePopupWindowContextData = {
    targetId: string,
    componentType: Exclude<ComponentDataType, 'offer'>,
    suggestedScope?: string,
}
export type TestableGroupModePopupWindowState = PopupWindowState<TestableGroupModePopupWindowContextData>
export const getDefaultClosedTestableGroupModePopupWindowState = (): TestableGroupModePopupWindowState => cloneDeep({
    isOpen: false,
    context: {
        id: 'testable-group-mode',
        scope: 'tree-node',
        data: {
            targetId: '',
            componentType: 'filter',
        },
    },
})
export const TestableGroupModePopupWindowStateAtom = atom<TestableGroupModePopupWindowState>(
    getDefaultClosedTestableGroupModePopupWindowState()
)

export type SelectActionContextDefault = {
    /**
     * This is for selecting a brand new action
     */
    action: null,
    data?: {},
};
export type SelectActionContextOffers = {
    /**
     * This is for opening the panel from an already selected action (select-offers)
     */
    action: 'select-offers', // or null, 'select-tiered-branch', 'select-or-branch'
    // once an action is selected, can you change it
    data: {
        canChangeAction?: boolean,
        offerId?: string, // if initialized, the mode is 'edit'
    },
};
export type SelectActionContext = {
    targetBranchId: string
} & (SelectActionContextDefault | SelectActionContextOffers)

export const getDefaultClosedPopupWindowState = <ContextType = SelectActionContext & SelectActionContextDefault>(): PopupWindowState<ContextType> => cloneDeep(({
    isOpen: false, //false
    context: {
        action: null, //'select-offers',
        targetBranchId: ''
    }
}))

export const getDefaultClosedExamplesPopupWindowState = (): PopupWindowState<ExamplesContext> => cloneDeep(({
    isOpen: false, //false
    context: {
        id: ''
    }
}))

export const SelectActionPopupWindowStateAtom = atom<PopupWindowState<SelectActionContext>>(
    getDefaultClosedPopupWindowState()
)

export type ExamplesContext = {
    id: string
}

export const ExamplesPopupWindowStateAtom = atom<PopupWindowState<ExamplesContext>>(getDefaultClosedExamplesPopupWindowState())

export const AddNewTreePopupWindowIsOpen = atom<boolean>(false)
export type ProUpgradePopupWindowState = {
    isOpen: boolean,
    title?: PopupWindowProps['screens'][number]['title'],
    icon?: PopupWindowProps['screens'][number]['icon'],
    content?: PopupWindowProps['screens'][number]['content'],
}
export const getDefaultClosedProUpgradePopupWindowState = (): ProUpgradePopupWindowState => ({
    isOpen: false,
})
export const ProUpgradePopupWindowIsOpen = atom<ProUpgradePopupWindowState>(getDefaultClosedProUpgradePopupWindowState())
export type GetYProductDataToSave = Omit<BuyXGetYSpecificProductWindowContextData, 'onClose'>;

export type BuyXGetYSpecificProductWindowContextData = {
    index?: number, // if not set, mode will be computed as "new". modes: new | edit
    data?: ProductData[number],
    onClose: (status: 'success' | 'fail', data: GetYProductDataToSave) => void
};
export type BuyXGetYNotInCartNotificationWindowContextData = {
    notification: NotInCartNotification,
    onClose: (status: 'success' | 'fail', data: NotInCartNotification) => void
};
export const BuyXGetYSpecificProductWindowStateAtom = atom<BuyXGetYSpecificProductWindowContextData | null>(null)
export const BuyXGetYNotInCartNotificationWindowStateAtom = atom<BuyXGetYNotInCartNotificationWindowContextData | null>(null)

export const CustomPresetSourceAtom = atom<string>('')

export const PermissionToLoadPresetsAtom = atom<boolean>(
    (window as any).CouponsPlus?.dashboardPreferences?.permissions?.connect || false
)

// Select Menu Popup Windows (supports multiple instances)
export type SelectMenuPopupWindowContextData = {
    options: SelectOption[],
    selectedValues: SelectOption['id'][],
    // placeholder for future context data
}
export type SelectMenuPopupWindowState = {
    context: PopupWindowStateContext<SelectMenuPopupWindowContextData>,
    onClose: (data: {
        status: 'success' | 'fail',
        values: SelectOption['id'][],
    }, options?: {
        deferWindowRemoval?: boolean
        skipCallback?: boolean
    }) => void
}
export type SelectMenuPopupWindowCloseOptions = {
    deferWindowRemoval?: boolean
    skipCallback?: boolean
}
export const SelectMenuPopupWindowsAtom = atom<SelectMenuPopupWindowState[]>([])

// Overlay for TestableComposite "folder open" effect
export type OverlayOpenCompositeState = {
    id: string,
    rootId: string,
    rect: DOMRect,
} | null
export const OverlayOpenCompositeAtom = atom<OverlayOpenCompositeState>(null)
