import React, { useEffect, useMemo, useRef, useState } from "react";
import {
  Animated,
  ActivityIndicator,
  FlatList,
  ScrollView,
} from "react-native";
import { useMediaQuery } from "react-responsive";
import {
  useFlexLayout,
  usePagination,
  useRowSelect,
  useTable,
} from "react-table";
import { useSticky } from "react-table-sticky";

import theme from "mazlo-theme";

import { default as useMazloPagination } from "../../hooks/usePagination";
import useRouterState from "../../hooks/useRouterState";
import { Checkbox } from "../Checkbox";
import { Image } from "../Image";
import { Pagination } from "../Pagination";
import { Text } from "../Text";
import { View } from "../View";
import TableRow from "./TableRow";
import styles from "./styles";

export type Column = {
  accessor: (() => string) | string;
  align?: string;
  id?: string;
  columns?: Column[];
  Header?: (() => string) | string | React.ReactNode;
  Cell?: (({ cell }) => string) | React.ReactNode;
  sort?: boolean;
  sticky?: "left" | "right";
  width?: number;
  minWidth?: number;
  maxWidth?: number;
};

type Props = {
  columns: Column[];
  count?: number;
  data: any[] | undefined;
  loading?: boolean;
  onTableChange?: (params: any) => void;
  onRowClick?: (rowData: any) => void;
  rowLinkProps?: (rowData: any) => Record<string, unknown>;
  selectedPageIndex?: number;
  selectRows?: boolean;
  total?: number;
};

const headerProps = (props, { column }) =>
  getStyles(props, column.align, column.width);

const cellProps = (props, { cell }) =>
  getStyles(props, cell.column.align, cell.column.width, cell.column.overflow);

const getStyles = (props, align = "left", width = 150, overflow = "hidden") => {
  return [
    props,
    {
      style: {
        justifyContent: "center",
        alignItems: align === "right" ? "flex-end" : "flex-start",
        flex: null,
        flexGrow: width,
        flexShrink: 0,
        flexBasis: "auto",
        overflow,
      },
    },
    {
      style: {
        ...styles.td,
      },
    },
  ];
};

const Table = ({
  columns,
  count,
  data,
  loading,
  onRowClick,
  onTableChange,
  rowLinkProps,
  selectedPageIndex = 0,
  selectRows = false,
  total,
}: Props) => {
  const defaultColumn = useMemo(
    () => ({
      width: 150,
      Cell: ({ cell: { value } }) => <Text>{value}</Text>,
    }),
    []
  );

  const { onPageChange } = useMazloPagination();

  const isTablet = useMediaQuery({
    minWidth: theme.breakpoints.sm,
  });
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    prepareRow,
    page,
    pageCount,
    gotoPage,
    selectedFlatRows,
    totalColumnsWidth,
    state: { pageIndex, selectedRowIds },
  } = useTable(
    {
      columns,
      data,
      defaultColumn,
      initialState: { pageIndex: selectedPageIndex },
      manualPagination: true,
      pageCount: count ? Math.ceil(total / count) : 0,
    },
    useFlexLayout,
    usePagination,
    useRowSelect,
    useSticky,
    (hooks) => {
      if (selectRows) {
        hooks.visibleColumns.push((initialColumns) => [
          {
            id: "selection",
            Header: <View style={{ width: 28 }} />,
            sort: false,
            sticky: "left",
            width: 28,
            Cell: ({ row }) => {
              const { onChange, ...rest } = row.getToggleRowSelectedProps();
              return <Checkbox {...rest} onPress={onChange} />;
            },
          },
          ...initialColumns,
        ]);
      }
    }
  );
  const [sort, setSort] = useRouterState<string>("sort");
  useEffect(() => {
    if (onTableChange) {
      onTableChange({ count, selectedRows: selectedFlatRows, sort });
    }
  }, [count, onTableChange, selectedFlatRows, selectedRowIds, sort]);
  useEffect(() => {
    onPageChange(pageIndex);
  }, [pageIndex]);
  const [tableWidth, setTableWidth] = useState(0);
  const scrollOffset = useRef(new Animated.Value(0));
  const scrollEvent = useRef(
    Animated.event(
      [
        {
          nativeEvent: {
            contentOffset: { x: scrollOffset.current },
          },
        },
      ],
      { useNativeDriver: false }
    )
  );

  const { style: tableStyle, ...tableProps } = getTableProps();
  const { style: tbodyStyle = null, ...tbodyProps } = getTableBodyProps();
  if (loading) {
    return <ActivityIndicator />;
  }
  return (
    <>
      <ScrollView
        horizontal
        contentContainerStyle={
          tableWidth > totalColumnsWidth ? { flex: 1 } : null
        }
        style={styles.tableContainer}
        scrollEventThrottle={16}
        onLayout={(e) => setTableWidth(e.nativeEvent.layout.width)}
        onScroll={scrollEvent.current}
      >
        <View {...tableProps} style={[tableStyle, styles.table]}>
          <View>
            {headerGroups.map((headerGroup, headerGroupIndex) => {
              const {
                style: headerGroupStyle,
                ...headerGroupProps
              } = headerGroup.getHeaderGroupProps();
              return (
                <View
                  key={`table-header-group${headerGroupIndex}`}
                  {...headerGroupProps}
                  style={[headerGroupStyle, styles.tr]}
                >
                  {headerGroup.headers.map((column, columnIndex) => {
                    const {
                      style: headerStyle,
                      ...props
                    } = column.getHeaderProps(headerProps);
                    return (
                      <View
                        key={`column-${columnIndex}`}
                        {...props}
                        onClick={
                          column.sort
                            ? () => {
                                if (sort === column.id) {
                                  setSort(`-${column.id}`);
                                } else if (sort === `-${column.id}`) {
                                  setSort("");
                                } else {
                                  setSort(column.id);
                                }
                                gotoPage(0); // if you sort, go back to first page
                              }
                            : null
                        }
                        style={[
                          headerStyle,
                          styles.th,
                          column.align === "right"
                            ? { justifyContent: "flex-end" }
                            : null,
                        ]}
                      >
                        <Text
                          size="xxs"
                          numberOfLines={1}
                          style={{ textTransform: "uppercase" }}
                        >
                          {column.render("Header")}
                        </Text>
                        {!!column.sort && (
                          <Image
                            source={theme.images.sortArrows}
                            style={styles.sortArrow}
                          />
                        )}
                      </View>
                    );
                  })}
                </View>
              );
            })}
          </View>
          <FlatList
            {...tbodyProps}
            data={page}
            keyExtractor={(item) => item.original?.id || item.id}
            extraData={isTablet}
            style={[tbodyStyle, styles.tbody]}
            renderItem={({ item: row, index: rowIndex }) => {
              prepareRow(row);
              const { key: rowKey, ...rowProps } = row.getRowProps();

              return (
                <TableRow
                  rowData={row.original}
                  rowLinkProps={rowLinkProps}
                  rowProps={rowProps}
                  clickHandler={onRowClick}
                >
                  {row.cells.map((cell, cellIndex) => {
                    const value = cell.render("Cell");
                    const { style: cellStyle, ...props } = cell.getCellProps(
                      cellProps
                    );

                    return (
                      <View
                        key={`table-cell-${rowIndex}-${cellIndex}`}
                        {...props}
                        style={[cellStyle, styles.td]}
                      >
                        {value}
                        {!!props["data-sticky-last-left-td"] && (
                          <Animated.View
                            style={[
                              styles.stickyLeftCell,
                              {
                                opacity: scrollOffset.current,
                              },
                            ]}
                          />
                        )}
                        {!!props["data-sticky-first-right-td"] && (
                          <Animated.View
                            style={[
                              styles.stickyRightCell,
                              {
                                opacity: Animated.subtract(
                                  totalColumnsWidth - tableWidth,
                                  scrollOffset.current
                                ),
                              },
                            ]}
                          />
                        )}
                      </View>
                    );
                  })}
                </TableRow>
              );
            }}
          />
        </View>
      </ScrollView>
      {!!count && (
        <Pagination
          count={pageCount}
          selected={selectedPageIndex}
          rangeSize={4}
          onPageChange={(i) => {
            gotoPage(i);
          }}
        />
      )}
    </>
  );
};

export default Table;
