import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { createContext, useCallback, useContext, useEffect, useMemo, useReducer, useRef, useState, } from 'react';
import { useCombobox } from 'downshift';
import { Box, Button, Table } from '../../next';
import { ConstrainedText, Icon, Loader, SearchInput, SecondaryText, Text, Tooltip, } from '../../index';
import styled from 'styled-components';
import { spacing, Stack, Wrap } from '../../spacing';
import { AttachmentAction, } from './AttachmentTypes';
import { useQuery } from 'react-query';
import { EmptyCell } from '../../components/tablev2/Tablev2.component';
import { tableRowHeight } from '../../components/tablev2/TableUtils';
const rowHeight = 'h48';
const MenuContainer = styled.ul `
  background-color: ${(props) => props.theme.backgroundLevel1};
  background-clip: content-box;
  padding: 0;
  list-style: none;
  position: absolute;
  width: ${(props) => props.width};
  z-index: 1;
  margin: 0;
  ${(props) => props.isOpen
    ? `
      border-top-left-radius: 0;
      border-top-right-radius: 0;
      border-bottom-right-radius: 4px;
      border-bottom-left-radius: 4px;
      border: 1px solid ${props.theme.selectedActive};
  `
    : props.searchInputIsFocused
        ? `border-bottom: 1px solid ${props.theme.selectedActive};`
        : ''}
  border-top: 0;
  li {
    padding: ${spacing.r8};
    cursor: pointer;
    border-top: 1px solid ${(props) => props.theme.backgroundLevel2};
    &[aria-selected='true'] {
      background: ${(props) => props.theme.highlight};
    }
  }
`;
const SearchBoxContainer = styled.div `
  position: relative;
  padding: ${spacing.r16};
`;
const StyledSearchInput = styled(SearchInput) `
  flex-grow: 1;

  & > div:focus-within {
    border-color: ${(props) => props.theme.selectedActive};
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
    border-bottom: 0;
  }
`;
const AttachmentTableContainer = styled.div `
  height: 100%;
`;
const CenterredSecondaryText = styled(SecondaryText) `
  display: block;
  text-align: center;
  line-height: ${tableRowHeight[rowHeight]}rem;
`;
const PrivateAttachmentContext = createContext(null);
const AttachmentContext = createContext(null);
export const AttachmentProvider = ({ children, }) => {
    const [resetAttachmentTable, setResetAttachementTable] = useState((_, __) => { });
    return (_jsx(PrivateAttachmentContext.Provider, { value: { setResetAttachementTable }, children: _jsx(AttachmentContext.Provider, { value: { resetAttachmentTable }, children: children }) }));
};
export const useAttachmentOperations = () => {
    const ctx = useContext(AttachmentContext);
    if (ctx === null) {
        throw new Error("useAttachmentOperations can't be used outside AttachmentProvider");
    }
    return ctx;
};
export const AttachmentTable = ({ initiallyAttachedEntities, initiallyAttachedEntitiesStatus, initialAttachmentOperations, onAttachmentsOperationsChanged, entityName, searchEntityPlaceholder, getNameQuery, filteredEntities, onEntitySearchChange, }) => {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
    const privateAttachmentContext = useContext(PrivateAttachmentContext);
    const exposedAttachmentContext = useContext(AttachmentContext);
    if (!privateAttachmentContext || !exposedAttachmentContext) {
        throw new Error('Cannot use AttachmentTable outside AttachmentProvider');
    }
    //Desired attached entities and onAttachmentsOperationsChanged handling
    const convertInitiallyAttachedEntitiesToDesiredAttachedEntities = useCallback((initiallyAttachedEntities, operations = initialAttachmentOperations) => {
        return initiallyAttachedEntities
            .filter((attachedEntities) => !operations.find((op) => op.entity.id === attachedEntities.id))
            .map((entity) => ({
            ...entity,
            isPending: false,
            action: null,
        }));
    }, [initialAttachmentOperations]);
    const convertInitiallyAttachementOperationsToDesiredAttachedEntities = useCallback((initialAttachmentOperations) => {
        return initialAttachmentOperations
            .filter((op) => op.action !== AttachmentAction.REMOVE)
            .map((op) => ({
            ...op.entity,
            isPending: true,
            action: op.action,
        }));
    }, []);
    const [{ desiredAttachedEntities, attachmentsOperations }, dispatch] = useReducer((state, action) => {
        switch (action.action) {
            case 'RESET_DESIRED_ATTACHED_ENTITIES':
                return {
                    desiredAttachedEntities: action.entities,
                    attachmentsOperations: action.operations,
                };
            case AttachmentAction.ADD:
                if (!state.desiredAttachedEntities.find((entity) => entity.id === action.entity.id)) {
                    const newAttachmentsOperations = [...state.attachmentsOperations];
                    const existingOperationIndexOnThisEntity = state.attachmentsOperations.findIndex((operation) => operation.entity.id === action.entity.id);
                    //When ADD, we check if it's already exist in operations. If so, we delete the previous operation and not proceed to the ADD.
                    if (existingOperationIndexOnThisEntity !== -1 &&
                        state.attachmentsOperations[existingOperationIndexOnThisEntity]
                            .action === AttachmentAction.REMOVE) {
                        newAttachmentsOperations.splice(existingOperationIndexOnThisEntity, 1);
                        const newState = {
                            ...state,
                            desiredAttachedEntities: [
                                { ...action.entity },
                                ...state.desiredAttachedEntities,
                            ],
                            attachmentsOperations: [...newAttachmentsOperations],
                        };
                        return newState;
                    }
                    else {
                        const newState = {
                            ...state,
                            desiredAttachedEntities: [
                                { ...action.entity, isPending: true },
                                ...state.desiredAttachedEntities,
                            ],
                            attachmentsOperations: [...newAttachmentsOperations, action],
                        };
                        return newState;
                    }
                }
                break;
            case AttachmentAction.REMOVE:
                if (state.desiredAttachedEntities.find((entity) => entity.id === action.entity.id)) {
                    const newDesiredAttachedEntities = [
                        ...state.desiredAttachedEntities,
                    ];
                    newDesiredAttachedEntities.splice(state.desiredAttachedEntities.findIndex((entity) => entity.id === action.entity.id), 1);
                    const newAttachmentsOperations = [...state.attachmentsOperations];
                    const existingOperationIndexOnThisEntity = state.attachmentsOperations.findIndex((operation) => operation.entity.id === action.entity.id);
                    if (existingOperationIndexOnThisEntity !== -1 &&
                        state.attachmentsOperations[existingOperationIndexOnThisEntity]
                            .action === AttachmentAction.ADD) {
                        newAttachmentsOperations.splice(existingOperationIndexOnThisEntity, 1);
                    }
                    else if (existingOperationIndexOnThisEntity !== -1 &&
                        state.attachmentsOperations[existingOperationIndexOnThisEntity]
                            .action === AttachmentAction.REMOVE) {
                        return state;
                    }
                    else {
                        newAttachmentsOperations.push(action);
                    }
                    const newState = {
                        ...state,
                        desiredAttachedEntities: newDesiredAttachedEntities,
                        attachmentsOperations: newAttachmentsOperations,
                    };
                    return newState;
                }
                break;
        }
        return state;
    }, {
        desiredAttachedEntities: [
            ...convertInitiallyAttachedEntitiesToDesiredAttachedEntities(initiallyAttachedEntities),
            ...convertInitiallyAttachementOperationsToDesiredAttachedEntities(initialAttachmentOperations),
        ],
        attachmentsOperations: initialAttachmentOperations,
    });
    useEffect(() => {
        onAttachmentsOperationsChanged(attachmentsOperations);
    }, [onAttachmentsOperationsChanged, attachmentsOperations]);
    const previousInitiallyAttachedEntitiesStatus = useRef(initiallyAttachedEntitiesStatus);
    useMemo(() => {
        if (initiallyAttachedEntitiesStatus === 'success' &&
            previousInitiallyAttachedEntitiesStatus.current !==
                initiallyAttachedEntitiesStatus) {
            previousInitiallyAttachedEntitiesStatus.current = 'success';
            dispatch({
                action: 'RESET_DESIRED_ATTACHED_ENTITIES',
                entities: [
                    ...convertInitiallyAttachedEntitiesToDesiredAttachedEntities(initiallyAttachedEntities),
                    ...convertInitiallyAttachementOperationsToDesiredAttachedEntities(initialAttachmentOperations),
                ],
                operations: initialAttachmentOperations,
            });
        }
        else {
            previousInitiallyAttachedEntitiesStatus.current =
                initiallyAttachedEntitiesStatus;
        }
    }, [
        initiallyAttachedEntitiesStatus,
        initiallyAttachedEntities,
        initialAttachmentOperations,
        convertInitiallyAttachedEntitiesToDesiredAttachedEntities,
        convertInitiallyAttachementOperationsToDesiredAttachedEntities,
    ]);
    useEffect(() => {
        privateAttachmentContext.setResetAttachementTable(() => {
            return (newlyAttachedEntities, newAttachmentOperations) => {
                dispatch({
                    action: 'RESET_DESIRED_ATTACHED_ENTITIES',
                    entities: [
                        ...convertInitiallyAttachedEntitiesToDesiredAttachedEntities(newlyAttachedEntities, newAttachmentOperations),
                        ...convertInitiallyAttachementOperationsToDesiredAttachedEntities(newAttachmentOperations),
                    ],
                    operations: newAttachmentOperations,
                });
            };
        });
    }, [
        convertInitiallyAttachedEntitiesToDesiredAttachedEntities,
        convertInitiallyAttachementOperationsToDesiredAttachedEntities,
        dispatch,
    ]);
    const resetRef = useRef(null);
    const searchInputRef = useRef(null);
    const onSelectedItemChange = useCallback((onChangeParams) => {
        if (onChangeParams.selectedItem) {
            dispatch({
                action: AttachmentAction.ADD,
                entity: onChangeParams.selectedItem,
            });
            if (resetRef.current)
                resetRef.current();
            if (searchInputRef.current)
                searchInputRef.current.blur();
        }
    }, [resetRef]);
    const { isOpen, getMenuProps, getInputProps, openMenu, getItemProps, reset } = useCombobox({
        items: filteredEntities.status === 'success'
            ? filteredEntities.data.entities
            : [],
        onSelectedItemChange,
        onInputValueChange: ({ inputValue }) => {
            onEntitySearchChange(inputValue);
        },
    });
    useMemo(() => {
        //@ts-expect-error assigning to the ref is expected here
        resetRef.current = reset;
    }, [reset]);
    // UI styling states
    const [searchWidth, setSearchWidth] = useState('0px');
    const [searchInputIsFocused, setSearchInputIsFocused] = useState(false);
    return (_jsxs(Table, { columns: [
            {
                Header: 'Name',
                accessor: 'name',
                cellStyle: {
                    flex: 1.5,
                    marginRight: '1.5rem',
                },
                //@ts-expect-error
                Cell: ({ value, row: { original: entity }, }) => {
                    const { data: asyncName, status } = useQuery({
                        ...(getNameQuery
                            ? getNameQuery(entity)
                            : { queryKey: ['fakeQuery'], queryFn: () => value }),
                        enabled: !value,
                    });
                    if (value) {
                        return _jsx(ConstrainedText, { text: value, lineClamp: 2 });
                    }
                    if (status === 'error') {
                        return (_jsxs(_Fragment, { children: ["An error occured while loading ", entityName.singular, " name"] }));
                    }
                    if (status === 'loading' || status === 'idle') {
                        return _jsx(_Fragment, { children: "Loading..." });
                    }
                    if (status === 'success') {
                        if (!asyncName) {
                            return _jsx(EmptyCell, {});
                        }
                        return _jsx(ConstrainedText, { text: asyncName, lineClamp: 2 });
                    }
                    return _jsx(EmptyCell, {});
                },
            },
            {
                Header: 'Attachment',
                accessor: 'isPending',
                cellStyle: {
                    flex: 0.5,
                },
                Cell: ({ value }) => {
                    return value ? _jsx(_Fragment, { children: "Pending" }) : _jsx(_Fragment, { children: "Attached" });
                },
            },
            {
                Header: _jsx(Box, { flex: 0.5 }),
                accessor: 'action',
                cellStyle: {
                    textAlign: 'right',
                    flex: 0.5,
                    marginLeft: 'auto',
                    marginRight: '0.5rem',
                },
                Cell: ({ row: { original: entity }, }) => (_jsx(Button, { size: "inline", onClick: () => {
                        dispatch({
                            action: AttachmentAction.REMOVE,
                            entity: {
                                name: entity.name,
                                id: entity.id,
                                type: entity.type,
                            },
                        });
                    }, icon: _jsx(Icon, { name: "Close" }), label: "Remove", variant: "danger", disabled: !!entity.disableDetach })),
            },
        ], data: desiredAttachedEntities.map((entity) => ({
            ...entity,
            isPending: entity.isPending || false,
            action: null,
        })), defaultSortingKey: "name", children: [_jsxs(SearchBoxContainer, { ref: (element) => {
                    if (element === null || element === void 0 ? void 0 : element.firstElementChild) {
                        setSearchWidth(element.firstElementChild.getBoundingClientRect().width -
                            2 +
                            'px');
                    }
                }, children: [filteredEntities.status === 'error' ? (_jsx(Tooltip, { overlay: _jsx(_Fragment, { children: "We failed to load the entities, hence search is disabled" }), children: _jsxs(Stack, { children: [_jsx(StyledSearchInput, { autoComplete: "off", placeholder: searchEntityPlaceholder, ...getInputProps({
                                        ref: (element) => {
                                            if (element)
                                                searchInputRef.current = element;
                                        },
                                    }), onFocus: () => {
                                        openMenu();
                                        setSearchInputIsFocused(true);
                                    }, onBlur: () => {
                                        setSearchInputIsFocused(false);
                                    }, disabled: filteredEntities.status === 'error' }), _jsx(Loader, {})] }) })) : (_jsx(StyledSearchInput, { autoComplete: "off", placeholder: searchEntityPlaceholder, ...getInputProps({
                            ref: (element) => {
                                if (element)
                                    searchInputRef.current = element;
                            },
                        }), onFocus: () => {
                            openMenu();
                            setSearchInputIsFocused(true);
                        }, onBlur: () => {
                            setSearchInputIsFocused(false);
                        }, searchInputIsFocused: searchInputIsFocused })), _jsxs(MenuContainer, { ...getMenuProps(), width: searchWidth, isOpen: isOpen, searchInputIsFocused: searchInputIsFocused, children: [isOpen &&
                                filteredEntities.status === 'success' &&
                                ((_a = filteredEntities.data) === null || _a === void 0 ? void 0 : _a.entities.map((item, index) => (_jsx("li", { ...getItemProps({ item, index }), children: _jsx(Text, { children: item.name }) }, `${item.id}${index}`)))), isOpen && filteredEntities.status === 'loading' && (_jsx("li", { children: _jsx(Text, { children: "Searching..." }) })), isOpen && filteredEntities.status === 'error' && (_jsx("li", { children: _jsx(Text, { color: "statusCritical", children: "An error occured while searching" }) })), isOpen &&
                                filteredEntities.status === 'success' &&
                                (((_b = filteredEntities.data) === null || _b === void 0 ? void 0 : _b.number) || 0) >
                                    ((_c = filteredEntities.data) === null || _c === void 0 ? void 0 : _c.entities.length) && (_jsx("li", { children: _jsxs(Text, { isGentleEmphazed: true, color: "textSecondary", style: { textAlign: 'right' }, children: ["There", ' ', (((_d = filteredEntities.data) === null || _d === void 0 ? void 0 : _d.number) || 0) -
                                            ((_e = filteredEntities.data) === null || _e === void 0 ? void 0 : _e.entities.length) ===
                                            1
                                            ? 'is'
                                            : 'are', ' ', (((_f = filteredEntities.data) === null || _f === void 0 ? void 0 : _f.number) || 0) -
                                            ((_g = filteredEntities.data) === null || _g === void 0 ? void 0 : _g.entities.length), ' ', "more", ' ', (((_h = filteredEntities.data) === null || _h === void 0 ? void 0 : _h.number) || 0) -
                                            ((_j = filteredEntities.data) === null || _j === void 0 ? void 0 : _j.entities.length) ===
                                            1
                                            ? entityName.singular
                                            : entityName.plural, ' ', "matching your search. Suggestion: try more specific search expression."] }) })), isOpen &&
                                filteredEntities.status === 'success' &&
                                ((_k = filteredEntities.data) === null || _k === void 0 ? void 0 : _k.entities.length) === 0 && (_jsx("li", { children: _jsxs(Text, { isGentleEmphazed: true, color: "textSecondary", children: ["No ", entityName.plural, " found matching your search."] }) }))] })] }), _jsx(Table.SingleSelectableContent, { rowHeight: rowHeight, separationLineVariant: "backgroundLevel2", children: (rows) => (_jsxs(_Fragment, { children: [initiallyAttachedEntitiesStatus === 'idle' ||
                            initiallyAttachedEntitiesStatus === 'loading' ? (_jsxs(Wrap, { style: { height: `${tableRowHeight[rowHeight]}rem` }, children: [_jsx("p", {}), _jsxs(Stack, { children: [_jsx(Loader, {}), _jsxs(Text, { children: ["Loading ", entityName.plural, "..."] })] }), _jsx("p", {})] })) : initiallyAttachedEntitiesStatus === 'error' ? (_jsxs(Stack, { style: {
                                justifyContent: 'center',
                                height: `${tableRowHeight[rowHeight]}rem`,
                            }, children: [_jsx(Icon, { name: "Exclamation-circle", color: "statusWarning" }), _jsxs(Text, { color: "textSecondary", children: ["Failed to load attached ", entityName.plural, "."] })] })) : (desiredAttachedEntities.length === 0 && (_jsxs(CenterredSecondaryText, { children: ["No ", entityName.plural, " attached"] }))), desiredAttachedEntities.length > 0 && rows] })) })] }));
};
