import React from 'react';
import cx from 'classnames';
import { Alert } from '../alert';
import { LoadingSpinner } from '../loadingSpinner';
import {
    ListViewDefaultAside,
    ListViewDefaultFooter,
    ListViewDefaultHeader,
    ListViewDefaultListContainer,
    ListViewFilterMessage,
} from './components';
import { CLASS_NAME } from './constants';
import { useControlledValue } from './hooks';
import { t } from './i18n';
import { isMultiValue } from './utils';
import type { ListViewMode, ListViewItem, ListViewProps } from './types';

const TEST_ID = 'SuiteListView';

/**
 * ListView is a component that displays a list of items. It's primarily used in ListBox and Select components.
 *
 * @type molecule
 * @status live
 * @tags tables-lists
 */
export function ListView<T extends ListViewItem, M extends boolean>({
    className,
    testId = TEST_ID,
    filterMessage,
    ...props
}: ListViewProps<T, M>) {
    const {
        emptyStateText = t('noOptions'),
        error,
        filter,
        height,
        loading,
        listMode,
        maxHeight,
        noMatchText = t('noMatch'),
        noRemainingOptionsText = t('noRemainingOptions'),
        onListModeChange,
        selectedValue,
        totalCount = 0,
        width,
        maxWidth,
        minHeight,
        minWidth,
        components = {},
        excludeMode = false,
        onExcludeModeChange,
        onApply,
        requireApply,
    } = props;

    const {
        Header = ListViewDefaultHeader,
        Aside = ListViewDefaultAside,
        Footer = ListViewDefaultFooter,
        ListContainer = ListViewDefaultListContainer,
    } = components;

    const { value: listViewMode, setValue: setListViewMode } = useControlledValue<ListViewMode>({
        defaultValue: 'consolidated',
        controlledValue: listMode,
    });
    const { value: controlledExcludeMode, setValue: setControlledExcludeMode } =
        useControlledValue<boolean>({
            defaultValue: excludeMode,
            controlledValue: requireApply ? undefined : excludeMode,
        });

    const handleOnApply = () => {
        onApply();
        onExcludeModeChange?.(controlledExcludeMode);
    };

    const getEmptyStateText = () => {
        if (isMultiValue(selectedValue) && totalCount > 0 && selectedValue.length === totalCount)
            return noRemainingOptionsText;

        if (filter) return noMatchText;
        return emptyStateText;
    };

    return (
        <div
            className={cx(CLASS_NAME, className, {
                static: props.variant === 'static',
                dividers: props.dividers,
            })}
            data-testid={testId}
            style={{ width, height, maxWidth, maxHeight, minHeight, minWidth }}
        >
            <div className={`${CLASS_NAME}-main`}>
                <Aside {...props} onApply={handleOnApply} />

                <div className={`${CLASS_NAME}-body`}>
                    <Header
                        {...props}
                        onApply={handleOnApply}
                        onListModeChange={(mode) => {
                            onListModeChange?.(mode);
                            if (!listMode) setListViewMode(mode);
                        }}
                        onExcludeModeChange={(mode) => {
                            if (requireApply) setControlledExcludeMode(mode);
                            else onExcludeModeChange?.(mode);
                        }}
                        excludeMode={controlledExcludeMode}
                    />
                    {filterMessage && <ListViewFilterMessage {...filterMessage} />}
                    {error && <Alert variant="error" text={error.message} />}

                    {!error && loading && <LoadingSpinner show />}

                    <ListContainer
                        {...props}
                        onApply={handleOnApply}
                        listMode={listViewMode}
                        emptyStateText={getEmptyStateText()}
                    />
                </div>
            </div>

            <Footer {...props} onApply={handleOnApply} />
        </div>
    );
}
