import React, {
  useState,
  useEffect,
  useImperativeHandle,
  forwardRef,
} from 'react';
import _ from 'lodash';
import i18next from 'i18next';
import { ThemeProvider } from 'styled-components';

import { getTheme } from '../utils/theme';

import {
  Container,
  ContainerList,
  ContainerView,
  ContentContainer,
  Header,
  LoadingRowItem,
  Box,
  ItemContainer,
  EmptyStateContainer,
} from './styledComponents';

import { Props } from './interfaces';
import { filterAndSortListBy, getPaginatedData, sortListBy } from './utils';
import {
  ORDER_TYPE,
  DEFAULT_PROPS,
  NB_EMPTY_ROWS_LOADING_STATE,
} from './constants';

import ListOptions from './components/ListOptions';
import ListViewContent from './components/ListViewContent';
import ListViewHeader from './components/ListViewHeader';
import Paginator from './components/Paginator';

function renderLoadingState(columns) {
  return (
    <>
      {_.times(NB_EMPTY_ROWS_LOADING_STATE, (index) => (
        <Box key={`row-${index}`}>
          {columns &&
            columns.map(({ name, large, minWidth }) => (
              <ItemContainer
                key={`row-column-${name}`}
                large={large}
                minWidth={minWidth}
              >
                <LoadingRowItem fadeInOut />
              </ItemContainer>
            ))}
        </Box>
      ))}
    </>
  );
}

const ListView = forwardRef((props: Props, ref): JSX.Element => {
  const {
    columns,
    data,
    isLoading,
    minWidth,
    defaultCurrentPage,
    handleCurrentPageChange,
    defaultOrderBy,
    handleOrderByChange,
    defaultOrderType,
    handleOrderTypeChange,
    defaultMaxPerPage,
    handleMaxPerPageChange,
    maxPerPageOptions,
    hideAllPerPageOption,
    hideSearchbar,
    defaultSearchInput,
    handleSearchInputChange,
    actions,
    actionsOnHover,
    rowActions,
    renderEmptyState,
    maxActionsInLine,
    exportFunction,
    actionOnClick,
    placeholderShape,
    placeholderShapePlural,
    padding,
    margin,
    setSelectedItems,
    renderFilterButton,
    languageCode,
    forceEnableSelection,
    disableSelection,
    theme,
    disableListOptions,
    disableMultipleSelection,
    disableFullSelection,
    disableFooter,
    disableResetSelectionOnNavigation,
    // Dynamic fetch
    queryParams,
    onQueryParamsChange,
    countElements,
    markerConfiguration,
    allSelectedByDefault,
    minActionsInActionsDropdown,
  } = props;

  const updatedTheme = getTheme(theme, 'listView');

  const [orderBy, setOrderBy] = useState<string>(defaultOrderBy);
  const [orderType, setOrderType] = useState<'asc' | 'desc'>(defaultOrderType);
  const [maxPerPage, setMaxPerPage] = useState<number>(defaultMaxPerPage);

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const [filteredData, setFilteredData] = useState<any[]>([]);

  const [selectedCount, setSelectedCount] = useState(0);

  const [currentPage, setCurrentPage] = useState<number>(defaultCurrentPage);

  const [searchInput, setSearchInput] = useState<string>(defaultSearchInput);

  useEffect(() => {
    setSearchInput(defaultSearchInput);
  }, [defaultSearchInput]);

  useEffect(() => {
    if (Number.isInteger(defaultMaxPerPage) && defaultMaxPerPage > 0) {
      return setMaxPerPage(defaultMaxPerPage);
    }

    return setMaxPerPage(DEFAULT_PROPS.maxPerPage);
  }, [defaultMaxPerPage]);

  useEffect(() => {
    const columnPropertyKeys = columns.map((column) => column.propertyKey);
    if (columnPropertyKeys.includes(defaultOrderBy)) {
      return setOrderBy(defaultOrderBy);
    }

    return setOrderBy(columnPropertyKeys[0]);
  }, [columns, defaultOrderBy]);

  useEffect(() => {
    if (
      defaultOrderType === ORDER_TYPE.ASCENDING ||
      defaultOrderType === ORDER_TYPE.DESCENDING
    ) {
      return setOrderType(defaultOrderType);
    }

    return setOrderType(ORDER_TYPE.ASCENDING);
  }, [defaultOrderType]);

  const [filteredDataCount, setFilteredDataCount] = useState(
    queryParams ? countElements : filteredData.length
  );

  const [filterablePropertyKeys, setFilterablePropertyKeys] = useState<
    string[]
  >([]);

  const [paginatedData, setPaginatedData] = useState([[]]);

  const [notSelectableItemsCount, setNotSelectableItemsCount] = useState(0);

  const selectAllRows = () => {
    const items = disableFooter
      ? filteredData
      : paginatedData[
          onQueryParamsChange && !!currentPage ? 0 : currentPage - 1
        ];

    const areRowsSelected = selectedCount > 0;
    const areAllRowsSelected = !!items && selectedCount === items.length;

    const selectedItems: unknown[] = [];

    // eslint-disable-next-line no-nested-ternary
    const startPaginatedIndex = disableFooter
      ? 0
      : onQueryParamsChange
      ? 0
      : (currentPage - 1) * maxPerPage;

    // eslint-disable-next-line no-nested-ternary
    const endPaginatedIndex = disableFooter
      ? (items || []).length
      : onQueryParamsChange
      ? maxPerPage
      : currentPage * maxPerPage;

    const updatedFilteredData = filteredData.map((item, index) => {
      if (item.isNotSelectable) {
        return item;
      }

      if (index < startPaginatedIndex || index >= endPaginatedIndex) {
        return {
          ...item,
          isRowSelected: false,
        };
      }

      if (areRowsSelected && !areAllRowsSelected) {
        return {
          ...item,
          isRowSelected: false,
        };
      }

      if (!areAllRowsSelected) {
        selectedItems.push(item);
      }

      return {
        ...item,
        isRowSelected: !areAllRowsSelected,
      };
    });

    setFilteredData(updatedFilteredData);
    setSelectedCount(selectedItems.length);
    if (setSelectedItems) {
      setSelectedItems(selectedItems);
    }
  };

  const resetSelection = () => {
    if (disableResetSelectionOnNavigation) {
      return;
    }

    const startPaginatedIndex = (currentPage - 1) * maxPerPage;
    const endPaginatedIndex = currentPage * maxPerPage;

    const updatedFilteredData = filteredData.map((item, index) => {
      if (index >= startPaginatedIndex && index <= endPaginatedIndex) {
        return {
          ...item,
          isRowSelected: false,
        };
      }

      return item;
    });

    setFilteredData(updatedFilteredData);

    if (setSelectedItems) {
      setSelectedItems([]);
    }

    setSelectedCount(0);
  };

  useEffect(() => {
    i18next.changeLanguage(languageCode);
  }, [languageCode]);

  useEffect(() => {
    const propertyKeys = columns
      .filter((column) => !column.excludeFromSearch)
      .map((column) => column.propertyKey);

    if (!_.isEqual(propertyKeys, filterablePropertyKeys)) {
      setFilterablePropertyKeys(propertyKeys);
    }
  }, [columns, filterablePropertyKeys]);

  useEffect(() => {
    if (onQueryParamsChange) {
      setFilteredData(data);

      return;
    }

    const filteredAndSortedData = filterAndSortListBy(
      _.cloneDeep(data),
      orderBy,
      orderType,
      filterablePropertyKeys,
      searchInput
    );

    setFilteredData(filteredAndSortedData);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [filterablePropertyKeys, searchInput, data, onQueryParamsChange]);

  useEffect(() => {
    if (!data || !data.length || !filteredData.length) {
      return;
    }

    if (allSelectedByDefault) {
      selectAllRows();
      return;
    }

    if (!queryParams) {
      const alreadySelectedItems = data.filter((item) => item.isRowSelected);

      setSelectedCount(alreadySelectedItems.length);

      if (setSelectedItems) {
        setSelectedItems(alreadySelectedItems);
      }

      const itemsNotSelectable = data.filter((item) => item.isNotSelectable);

      setNotSelectableItemsCount(itemsNotSelectable.length);
      return;
    }

    if (setSelectedItems) {
      setSelectedItems([]);
    }

    setSelectedCount(0);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [data]);

  useEffect(() => {
    if (currentPage === 1 && onQueryParamsChange) {
      onQueryParamsChange({
        ...queryParams,
      });
    }

    const itemsCount = onQueryParamsChange ? countElements : filteredDataCount;

    setCurrentPage(
      defaultCurrentPage > 0 &&
        defaultCurrentPage <= Math.ceil(itemsCount / maxPerPage)
        ? defaultCurrentPage
        : DEFAULT_PROPS.currentPage
    );
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [
    countElements,
    filteredDataCount,
    maxPerPage,
    searchInput,
    orderBy,
    orderType,
  ]);

  // Order list if no dynamic pagination is set
  useEffect(() => {
    if (!onQueryParamsChange) {
      const sortedFilteredData = sortListBy(filteredData, orderBy, orderType);
      setFilteredData(sortedFilteredData);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [orderBy, orderType]);

  useEffect(() => {
    if (queryParams && onQueryParamsChange) {
      onQueryParamsChange({
        ...queryParams,
      });
    }

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [currentPage]);

  useEffect(() => {
    if (filteredDataCount !== filteredData.length) {
      setFilteredDataCount(
        onQueryParamsChange ? countElements : filteredData.length
      );

      const alreadySelectedItems = filteredData.filter(
        (item) => item.isRowSelected
      );

      setSelectedCount(alreadySelectedItems.length);

      const itemsNotSelectable = filteredData.filter(
        (item) => item.isNotSelectable
      );

      setNotSelectableItemsCount(itemsNotSelectable.length);
    }

    setPaginatedData(getPaginatedData(filteredData, maxPerPage));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [
    countElements,
    filteredData,
    maxPerPage,
    filteredDataCount,
    setFilteredDataCount,
  ]);

  useImperativeHandle(ref, () => ({
    resetPagination() {
      if (currentPage !== 1) {
        setCurrentPage(1);
      }
    },
    getFilteredData() {
      return filteredData;
    },
  }));

  const setOrder = (columnId) => {
    let updatedOrderType: 'asc' | 'desc' = 'asc';

    if (orderBy === columnId) {
      updatedOrderType =
        orderType === ORDER_TYPE.ASCENDING
          ? ORDER_TYPE.DESCENDING
          : ORDER_TYPE.ASCENDING;
    } else {
      setOrderBy(columnId);
      if (handleOrderByChange && handleCurrentPageChange) {
        handleOrderByChange(columnId);
        handleCurrentPageChange(1);
      }
    }

    setOrderType(updatedOrderType);

    if (handleOrderTypeChange && handleCurrentPageChange) {
      handleOrderTypeChange(updatedOrderType);
      handleCurrentPageChange(1);
    }
  };

  const selectRow = (_row, index) => {
    const indexInFilteredData =
      !currentPage || onQueryParamsChange
        ? index
        : (currentPage - 1) * maxPerPage + index;

    if (!filteredData[indexInFilteredData]) {
      return;
    }

    if (disableMultipleSelection) {
      const { isRowSelected } = filteredData[indexInFilteredData];

      const updatedFilteredData = filteredData.map((item) => {
        return { ...item, isRowSelected: false };
      });

      updatedFilteredData[indexInFilteredData].isRowSelected = !isRowSelected;

      if (!isRowSelected) {
        setSelectedCount(1);
      } else {
        setSelectedCount(0);
      }

      setFilteredData(updatedFilteredData);

      if (setSelectedItems) {
        const selectedRows = updatedFilteredData.filter(
          (item) => item.isRowSelected
        );

        setSelectedItems(selectedRows);
      }

      return;
    }

    const updatedFilteredData = _.cloneDeep(filteredData);

    const { isRowSelected } = updatedFilteredData[indexInFilteredData];

    updatedFilteredData[indexInFilteredData].isRowSelected = !isRowSelected;

    setFilteredData(updatedFilteredData);

    if (!isRowSelected) {
      setSelectedCount((count) => count + 1);
    } else {
      setSelectedCount((count) => count - 1);
    }

    if (setSelectedItems) {
      const selectedRows = updatedFilteredData.filter(
        (item) => item.isRowSelected
      );

      setSelectedItems(selectedRows);
    }
  };

  const renderContent = (): JSX.Element => {
    if (!filteredData.length && !isLoading) {
      return (
        <div style={{ height: updatedTheme.content?.height }}>
          <EmptyStateContainer>
            {renderEmptyState && (renderEmptyState() as string)}
          </EmptyStateContainer>
        </div>
      );
    }

    // eslint-disable-next-line no-nested-ternary
    const isSelectable = forceEnableSelection
      ? true
      : disableSelection
      ? false
      : !!actions?.length;

    return (
      <ContentContainer margin={margin}>
        <Header hasCheckbox={isSelectable}>
          <ListViewHeader
            theme={theme}
            data={
              disableFooter
                ? filteredData
                : paginatedData[onQueryParamsChange ? 0 : currentPage - 1]
            }
            columns={columns}
            orderBy={orderBy}
            orderType={orderType}
            setOrder={setOrder}
            selectedCount={selectedCount}
            selectAllRows={selectAllRows}
            isSelectable={isSelectable}
            disableFullSelection={disableFullSelection}
            notSelectableItemsCount={notSelectableItemsCount}
            disableMultipleSelection={disableMultipleSelection}
            shouldDisplayActionColumn={!!actionsOnHover || !!rowActions}
          />
        </Header>
        <div
          style={{
            flexGrow: 1,
            margin: '0 -32px',
            padding: '0 32px',
            overflowY: 'scroll',
            position: 'absolute',
            width: 'calc(100% + 64px)',
            height: 'calc(100% - 58px)',
          }}
        >
          {isLoading && renderLoadingState(columns)}
          {!isLoading && (
            <ListViewContent
              columns={columns}
              data={
                disableFooter
                  ? filteredData
                  : paginatedData[onQueryParamsChange ? 0 : currentPage - 1]
              }
              selectRow={selectRow}
              isSelectable={isSelectable}
              actionsOnHover={actionsOnHover}
              rowActions={rowActions}
              actionOnClick={actionOnClick}
              disableMultipleSelection={disableMultipleSelection}
              markerConfiguration={markerConfiguration}
            />
          )}
        </div>
      </ContentContainer>
    );
  };

  return (
    <ThemeProvider theme={updatedTheme}>
      <Container>
        <ContainerList minWidth={minWidth}>
          <ContainerView
            padding={padding}
            fullHeight={isLoading || disableFooter}
          >
            {!disableListOptions && (
              <ListOptions
                theme={theme}
                actions={actions || []}
                searchInput={searchInput}
                hideSearchbar={hideSearchbar}
                setSearchInput={(input) => {
                  resetSelection();

                  setSearchInput(input);

                  if (handleSearchInputChange && handleCurrentPageChange) {
                    handleSearchInputChange(input);
                    handleCurrentPageChange(1);
                  }
                }}
                maxActionsInLine={maxActionsInLine || 0}
                itemsCount={
                  onQueryParamsChange ? countElements : filteredDataCount
                }
                selectedRows={filteredData.filter((row) => row.isRowSelected)}
                exportFunction={exportFunction}
                placeholderShape={placeholderShape}
                placeholderShapePlural={placeholderShapePlural}
                renderFilterButton={renderFilterButton}
                minActionsInActionsDropdown={minActionsInActionsDropdown}
              />
            )}
            {renderContent()}
          </ContainerView>
          {!disableFooter && filteredData.length > 0 && (
            <Paginator
              theme={theme}
              itemsCount={
                onQueryParamsChange ? countElements : filteredDataCount
              }
              selectedItemsCount={selectedCount}
              maxPerPage={maxPerPage}
              setMaxPerPage={(option) => {
                resetSelection();

                setMaxPerPage(option);

                if (handleMaxPerPageChange && handleCurrentPageChange) {
                  handleMaxPerPageChange(option);
                  handleCurrentPageChange(1);
                }
              }}
              currentPage={currentPage}
              setCurrentPage={(indexCurrentPage) => {
                resetSelection();

                setCurrentPage(indexCurrentPage);

                if (handleCurrentPageChange) {
                  handleCurrentPageChange(indexCurrentPage);
                }
              }}
              isLoading={isLoading}
              maxPerPageOptions={maxPerPageOptions}
              hideAllPerPageOption={hideAllPerPageOption}
            />
          )}
        </ContainerList>
      </Container>
    </ThemeProvider>
  );
});

ListView.defaultProps = {
  isLoading: false,
  minWidth: 800,
  countElements: 0,
  defaultCurrentPage: DEFAULT_PROPS.currentPage,
  handleCurrentPageChange: undefined,
  defaultOrderBy: undefined,
  handleOrderByChange: undefined,
  defaultOrderType: ORDER_TYPE.ASCENDING,
  handleOrderTypeChange: undefined,
  defaultMaxPerPage: DEFAULT_PROPS.maxPerPage,
  handleMaxPerPageChange: undefined,
  maxPerPageOptions: [10, 20, 50, 100],
  hideAllPerPageOption: false,
  hideSearchbar: false,
  defaultSearchInput: '',
  handleSearchInputChange: undefined,
  actions: undefined,
  actionsOnHover: undefined,
  rowActions: undefined,
  renderEmptyState: undefined,
  maxActionsInLine: 2,
  exportFunction: undefined,
  renderFilterButton: undefined,
  actionOnClick: undefined,
  placeholderShape: undefined,
  placeholderShapePlural: undefined,
  padding: '0',
  margin: null,
  setSelectedItems: undefined,
  queryParams: null,
  onQueryParamsChange: undefined,
  languageCode: 'fr',
  forceEnableSelection: false,
  disableSelection: false,
  disableMultipleSelection: false,
  disableResetSelectionOnNavigation: false,
  theme: null,
  disableListOptions: false,
  disableFooter: false,
  markerConfiguration: undefined,
  allSelectedByDefault: false,
  minActionsInActionsDropdown: 2,
};

export default ListView;
