/* eslint react-hooks/rules-of-hooks:0 */

import _ from 'lodash';
import i18next from 'i18next';
import PropTypes from 'prop-types';
import React, { useEffect, useState, useRef, Ref } from 'react';

import { ThemeProvider } from 'styled-components';

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

import {
  Box,
  Row,
  Text,
  Item,
  Icon,
  Headers,
  Content,
  Container,
  PaginationBar,
  PaginationInfo,
  ContainerTable,
  LoadingRowItem,
  ContainerHeaders,
  DropdownContainer,
  CloseFilter,
  RowSeparator,
} from './styledComponents';

import { getPaginatedData, sortListBy, useOnClickOutside } from './utils';
import { Column, Props, ActiveFilter } from './interfaces';
import { TYPE_FIELD, getPageLimitOptions, ORDER_TYPE } from './constants';

import Tooltip from '../Tooltip';
import SelectorFilter from '../SelectorFilter';
import EmptyState from './components/EmptyState';

import iconSortedListDesc from '../images/icon-sorted-desc.svg';
import iconSortedListAsc from '../images/icon-sorted-asc.svg';
import iconSort from '../images/icon-sort.svg';

import iconSortedListDescDark from '../images/icon-sorted-desc-dark.svg';
import iconSortedListAscDark from '../images/icon-sorted-asc-dark.svg';
import iconSortDark from '../images/icon-sort-dark.svg';

import ArrowGrey from '../images/icon-arrow-grey.svg';
import DropdownGrey from '../images/icon-dropdown-grey.svg';
import ArrowLeftBlue from '../images/icon-arrow-left-blue.svg';
import CheckMarkBlue from '../images/icon-check-blue.svg';

import DropdownDark from '../images/icon-dropdown-dark.svg';
import ArrowLeftDark from '../images/icon-arrow-left-dark.svg';
import CheckMarkDark from '../images/icon-check-dark.svg';

import { useScrollListener } from '../commons/hooks/useScrollListener';

const icons = {
  blue: {
    arrow: ArrowGrey,
    check: CheckMarkBlue,
    dropdown: DropdownGrey,
    navigation: ArrowLeftBlue,
    sort: {
      default: iconSort,
      asc: iconSortedListAsc,
      desc: iconSortedListDesc,
    },
  },
  dark: {
    arrow: ArrowGrey,
    check: CheckMarkDark,
    dropdown: DropdownDark,
    navigation: ArrowLeftDark,
    sort: {
      default: iconSortDark,
      asc: iconSortedListAscDark,
      desc: iconSortedListDescDark,
    },
  },
};

const NB_EMPTY_ROWS_LOADING_STATE = 9;

function renderActiveFilterIndex(activeFilters, column) {
  const activeFilterIndex = activeFilters.findIndex(
    (activeFilter) => activeFilter.propertyKey === column.propertyKey
  );
  const filterIndex = activeFilterIndex !== -1 ? activeFilterIndex + 1 : null;

  return (
    filterIndex && (
      <span style={{ marginLeft: '5px', color: '#0A3EFF' }}>
        ({filterIndex})
      </span>
    )
  );
}

function renderContent(
  columns: Column[],
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  data: any[],
  highligthedRowsId: string[],
  labelEmptyState: string,
  displaySeparator: boolean,
  useCustomRender: boolean
) {
  return (
    <>
      {data.length ? (
        data.map((item) => (
          <Row key={`row-${item.id}`}>
            {columns.map(({ name, decimals, propertyKey, type, render }) => {
              const fieldFormat = TYPE_FIELD[type] || TYPE_FIELD.string;

              return (
                <Item key={`row-column-${name}`}>
                  <Text
                    blue={highligthedRowsId.includes(item.id)}
                    bold={highligthedRowsId.includes(item.id)}
                  >
                    {fieldFormat.format(
                      item,
                      item[propertyKey],
                      decimals,
                      (useCustomRender || type === TYPE_FIELD.currency.name) &&
                        render
                    )}
                  </Text>
                </Item>
              );
            })}
            {displaySeparator && <RowSeparator />}
          </Row>
        ))
      ) : (
        <EmptyState label={labelEmptyState} />
      )}
    </>
  );
}

function renderHeader(
  columns: Column[],
  orderBy: string,
  orderType: 'asc' | 'desc',
  setOrderColumn: (columnId: string | number) => void,
  selectedFilterId: string | number | object | null,
  handleFilterSelection: (filterId: string | number) => void,
  updateFilters: (filterId, propertyKey, doFilter, value) => void,
  activeFilters: ActiveFilter[],
  GetOrCreateRef: (id: string | number) => Ref<HTMLDivElement>,
  languageCode: string,
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  data: any[],
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  highlightedRowsId: any[],
  isScrolling: boolean,
  displayColumnsFilters: boolean,
  labelEmptyState: string,
  theme
) {
  const highlightedRows = data.filter((item) =>
    highlightedRowsId.includes(item.id)
  );

  return (
    <div>
      <Row header>
        {columns.map((column) => {
          const isColumnSelected = column.id === orderBy;
          const isFilterActive = !!activeFilters.find(
            (activeFilter) => activeFilter.propertyKey === column.propertyKey
          );

          return (
            <Item key={`headers-column-${column.id}`}>
              <Text
                onClick={() => setOrderColumn(column.id)}
                grey={!isColumnSelected}
                darkBlue={isColumnSelected || isFilterActive}
                interactive
              >
                {column.name}
              </Text>
              {column.id !== orderBy && (
                <Icon
                  onClick={() => setOrderColumn(column.id)}
                  width="16"
                  height="16"
                  src={icons[theme.pagination?.icon].sort?.default}
                  alt="icon-order"
                  marginLeft="5px"
                  interactive
                />
              )}
              {column.id === orderBy && (
                <Icon
                  onClick={() => setOrderColumn(column.id)}
                  width="16"
                  height="16"
                  src={
                    orderType === ORDER_TYPE.DESCENDING
                      ? icons[theme.pagination?.icon].sort?.desc
                      : icons[theme.pagination?.icon].sort?.asc
                  }
                  alt="icon-order"
                  marginLeft="5px"
                  interactive
                />
              )}
              {column.tooltipText && <Tooltip text={column.tooltipText} />}
              {column.filterType && displayColumnsFilters && (
                <>
                  <SelectorFilter
                    itemList={data}
                    filterId={column.id}
                    ref={GetOrCreateRef(column.id)}
                    type={column.filterType}
                    selectedFilter={selectedFilterId}
                    handleFilterSelection={handleFilterSelection}
                    isFilterActive={isFilterActive}
                    filterPropertyKey={column.propertyKey}
                    onFilterChange={(filterId, doFilter, value) =>
                      updateFilters(
                        filterId,
                        column.propertyKey,
                        doFilter,
                        value
                      )
                    }
                    languageCode={languageCode}
                  />
                  {renderActiveFilterIndex(activeFilters, column)}
                </>
              )}
            </Item>
          );
        })}
      </Row>
      <ContainerHeaders $scrolling={isScrolling}>
        {highlightedRows &&
          highlightedRows.length > 0 &&
          renderContent(
            columns,
            highlightedRows,
            highlightedRowsId,
            labelEmptyState,
            false,
            false
          )}
      </ContainerHeaders>
    </div>
  );
}

function renderPagination(
  currentPage: number,
  totalPage: number,
  pageLimit: number,
  isDropDownPageLimitOpened: boolean,
  nbTotalElements: number,
  isLoading: boolean,
  previousPage,
  nextPage,
  setPageLimit,
  setIsDropDownPageLimitOpened,
  theme,
  getFirstElementOfPage,
  getLastElementOfPage
) {
  return (
    <PaginationBar>
      <div
        style={{
          flex: '1',
          display: 'flex',
          alignItems: 'center',
        }}
      >
        <Box
          disabled={isLoading}
          onClick={() =>
            !isLoading &&
            setIsDropDownPageLimitOpened(!isDropDownPageLimitOpened)
          }
          onKeyDown={() =>
            !isLoading &&
            setIsDropDownPageLimitOpened(!isDropDownPageLimitOpened)
          }
          style={{
            color: theme.pagination?.dropdown?.color,
            border: theme.pagination?.dropdown?.border,
            boxShadow: theme.pagination?.dropdown?.boxShadow,
            background: theme.pagination?.dropdown?.backgroundColor,
            borderRadius: theme.pagination?.dropdown?.borderRadius,
          }}
          interactive
        >
          {isDropDownPageLimitOpened && (
            <DropdownContainer>
              {getPageLimitOptions(nbTotalElements).map((option) => (
                // eslint-disable-next-line jsx-a11y/no-static-element-interactions
                <div
                  key={option.id}
                  onClick={() => option.callback(setPageLimit, nbTotalElements)}
                  onKeyDown={() =>
                    option.callback(setPageLimit, nbTotalElements)
                  }
                >
                  {option.text}
                  {option.value === pageLimit && (
                    <Icon
                      style={{ marginLeft: '16px', minWidth: '12px' }}
                      src={icons[theme.pagination?.icon].check}
                    />
                  )}
                </div>
              ))}
            </DropdownContainer>
          )}
          <Text micro grey>
            {pageLimit === nbTotalElements
              ? i18next.t('GENERAL_ALL')
              : `${pageLimit} ${i18next.t(
                  'COMPONENT_LIST_PAGINATION_PER_PAGE'
                )}`}
          </Text>
          <Icon
            width={12}
            height={4}
            rotated={isDropDownPageLimitOpened}
            style={{ marginLeft: '16px', height: '4px' }}
            src={icons[theme.pagination?.icon].dropdown}
            alt="downdrop-icon-page-limit"
          />
        </Box>
        <Text grey micro style={{ margin: '0px 10px 0px 15px' }}>
          {getFirstElementOfPage()} - {getLastElementOfPage()}{' '}
          {i18next.t('COMPONENT_LIST_VIEW_PAGINATOR_ON')} {nbTotalElements}
        </Text>
      </div>
      <div style={{ flex: '1', display: 'flex', justifyContent: 'center' }}>
        <PaginationInfo>
          <Box
            className={currentPage <= 1 ? 'disabled' : undefined}
            onClick={previousPage}
            onKeyDown={previousPage}
            interactive
          >
            <Icon
              width={12}
              height={8}
              src={icons[theme.pagination?.icon].navigation}
              alt="pagination-previous-button"
            />
          </Box>
          <Box>{`${currentPage} / ${totalPage}`}</Box>
          <Box
            className={currentPage >= totalPage ? 'disabled' : undefined}
            onClick={nextPage}
            onKeyDown={nextPage}
            interactive
          >
            <Icon
              width={12}
              height={8}
              className="rotated"
              src={icons[theme.pagination?.icon].navigation}
              alt="pagination-previous-button"
            />
          </Box>
        </PaginationInfo>
      </div>
      <PaginationInfo hidden />
    </PaginationBar>
  );
}

function renderLoadingState(columns: Column[], theme) {
  return (
    <>
      {_.times(NB_EMPTY_ROWS_LOADING_STATE, (index) => (
        <Row
          style={
            index === 0
              ? {
                  borderTopLeftRadius: theme?.borderRadius,
                  borderTopRightRadius: theme?.borderRadius,
                }
              : {}
          }
          key={`row-${index}`}
        >
          {columns.map(({ name }) => (
            <Item key={`row-column-${name}`}>
              <LoadingRowItem opacity={1 - index / 10} />
            </Item>
          ))}
          {index > 0 && <RowSeparator />}
        </Row>
      ))}
    </>
  );
}

const TableView = (props: Props): JSX.Element | null => {
  const {
    data,
    columns,
    minWidth,
    maxPerPage,
    positionTop,
    positionBottom,
    languageCode,
    defaultOrderBy,
    defaultOrderType,
    highligthedRowsId,
    isLoading,
    displayColumnsFilters,
    labelEmptyState,
    theme,
  } = props;

  const tableContentRef = useRef<HTMLElement>(null);

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

  const [orderBy, setOrderBy] = useState(defaultOrderBy);
  const [orderType, setOrdertType] = useState(defaultOrderType);

  const [totalPage, setTotalPage] = useState(1);
  const [pageLimit, setPageLimit] = useState(maxPerPage);
  const [currentPage, setCurrentPage] = useState(1);

  const [selectedFilterId, setSelectedFilterId] = useState<
    string | number | object | null
  >(null);
  const [activeFilters, setActiveFilters] = useState<ActiveFilter[]>([]);

  const [isDropDownPageLimitOpened, setIsDropDownPageLimitOpened] =
    useState(false);

  const [isScrollingContent, setIsScrollingContent] = useState(false);

  useScrollListener(tableContentRef, (event) => {
    setIsScrollingContent(event.target.scrollTop !== 0);
  });

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

  useEffect(() => {
    if (!data) {
      return;
    }

    setActiveData(data);
  }, [data]);

  useEffect(() => {
    if (!activeData) {
      return;
    }

    const totalPerPage = pageLimit;

    setTotalPage(
      Math.max(
        Math.ceil(
          activeData.filter((item) => !highligthedRowsId.includes(item.id))
            .length / totalPerPage
        ),
        1
      )
    );
    setCurrentPage(1);
  }, [pageLimit, activeData, highligthedRowsId]);

  const previousPage = () => {
    if (currentPage <= 1) {
      return;
    }

    setCurrentPage(currentPage - 1);
  };

  const nextPage = () => {
    if (currentPage >= totalPage) {
      return;
    }

    setCurrentPage(currentPage + 1);
  };

  const setOrderColumn = (columnId) => {
    if (orderBy === columnId) {
      setOrdertType(
        orderType === ORDER_TYPE.ASCENDING
          ? ORDER_TYPE.DESCENDING
          : ORDER_TYPE.ASCENDING
      );

      return;
    }

    setOrderBy(columnId);
    setOrdertType(ORDER_TYPE.ASCENDING);
  };

  if (!columns || !columns.length) {
    return null;
  }

  const references = {};
  // create ref to attach to selector Filter using useOnClickOutside hook
  const GetOrCreateRef = (id) => {
    if (!Object.prototype.hasOwnProperty.call(references, id)) {
      references[id] = useOnClickOutside(() => {
        if (selectedFilterId === id) {
          setSelectedFilterId(null);
        }
      });
    }
    return references[id];
  };

  /** *************** */
  /* Handle Filters */
  /** *************** */

  const handleFilterSelection = (filterId) =>
    setSelectedFilterId(selectedFilterId === filterId ? null : filterId);

  const updateFilters = (filterId, propertyKey, doFilter, value) => {
    // deactivate filter when deselected or submitted incomplete or empty
    if (
      !doFilter ||
      value === null ||
      value.minValue === null ||
      value.maxValue === null ||
      (filterId.toString().startsWith('string-selector-filter') &&
        value.length === 0)
    ) {
      setActiveFilters(
        activeFilters.filter(
          (activeFilter) => activeFilter.filterId !== filterId
        )
      );

      return;
    }

    // remove overwritten filter
    const updatedActiveFilters = activeFilters.filter(
      (activeFilter) => activeFilter.propertyKey !== propertyKey
    );

    // activate new filter
    setActiveFilters([
      ...updatedActiveFilters,
      { filterId, propertyKey, doFilter, value },
    ]);
  };

  useEffect(() => {
    if (!activeFilters.length) {
      setActiveData(data);

      return;
    }

    let filteredData = data;

    activeFilters.forEach(({ propertyKey, doFilter, value }) => {
      filteredData = doFilter(filteredData, propertyKey, value);
    });

    setActiveData(filteredData);
  }, [activeFilters, data]);

  const sortedData = sortListBy(
    activeData,
    columns,
    orderBy,
    orderType,
    highligthedRowsId
  );

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

  const getFirstElementOfPage = () => {
    return (currentPage - 1) * maxPerPage + 1;
  };

  const getLastElementOfPage = () => {
    const itemsCount = activeData.filter(
      (item) => !highligthedRowsId.includes(item.id)
    ).length;

    if (currentPage * maxPerPage > itemsCount) {
      return itemsCount;
    }
    return currentPage * maxPerPage;
  };

  return (
    <ThemeProvider theme={updatedTheme}>
      <Container
        style={{ top: `${positionTop}px`, bottom: `${positionBottom}px` }}
      >
        {activeFilters.length !== 0 && (
          <CloseFilter onClick={() => setActiveFilters([])}>
            {i18next.t('GENERAL_CLEAR_FILTER')}
          </CloseFilter>
        )}
        <ContainerTable minWidth={minWidth}>
          <Headers>
            {renderHeader(
              columns,
              orderBy,
              orderType,
              setOrderColumn,
              selectedFilterId,
              handleFilterSelection,
              updateFilters,
              activeFilters,
              GetOrCreateRef,
              languageCode,
              activeData,
              isLoading ? [] : highligthedRowsId, // no need to display highlighted rows if data are being fetched
              isScrollingContent,
              displayColumnsFilters,
              labelEmptyState,
              updatedTheme
            )}
          </Headers>
          <Content ref={tableContentRef}>
            {isLoading && renderLoadingState(columns, updatedTheme)}
            {!isLoading &&
              renderContent(
                columns,
                getPaginatedData(
                  sortedData,
                  currentPage,
                  pageLimit,
                  highligthedRowsId
                ),
                highligthedRowsId,
                labelEmptyState,
                true,
                true
              )}
          </Content>
        </ContainerTable>
        {renderPagination(
          currentPage,
          totalPage,
          pageLimit,
          isDropDownPageLimitOpened,
          activeData.filter((item) => !highligthedRowsId.includes(item.id))
            .length,
          isLoading,
          previousPage,
          nextPage,
          setPageLimit,
          setIsDropDownPageLimitOpened,
          updatedTheme,
          getFirstElementOfPage,
          getLastElementOfPage
        )}
      </Container>
    </ThemeProvider>
  );
};

TableView.propTypes = {
  // eslint-disable-next-line react/forbid-prop-types
  theme: PropTypes.objectOf(PropTypes.any),
  isLoading: PropTypes.bool,
  minWidth: PropTypes.number,
  maxPerPage: PropTypes.number,
  positionTop: PropTypes.number,
  positionBottom: PropTypes.number,
  defaultOrderBy: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  defaultOrderType: PropTypes.string,
  columns: PropTypes.arrayOf(
    PropTypes.shape({
      render: PropTypes.func,
      decimals: PropTypes.number,
      tooltipText: PropTypes.string,
      name: PropTypes.string.isRequired,
      type: PropTypes.oneOf(Object.keys(TYPE_FIELD)),
      propertyKey: PropTypes.string.isRequired,
    })
  ).isRequired,
  // eslint-disable-next-line react/forbid-prop-types
  data: PropTypes.arrayOf(PropTypes.object).isRequired,
  // eslint-disable-next-line react/forbid-prop-types
  highligthedRowsId: PropTypes.arrayOf(PropTypes.any),
  displayColumnsFilters: PropTypes.bool,
  languageCode: PropTypes.string,
  labelEmptyState: PropTypes.string,
};

TableView.defaultProps = {
  isLoading: false,
  minWidth: 600,
  maxPerPage: 20,
  positionTop: 0,
  positionBottom: 0,
  defaultOrderBy: null,
  defaultOrderType: ORDER_TYPE.ASCENDING,
  highligthedRowsId: [],
  displayColumnsFilters: true,
  languageCode: 'fr',
  labelEmptyState: 'Aucun résultat ne correspond à votre recherche',
  theme: null,
};

export default TableView;
