import { Box, Card, Checkbox, CircularProgress, Collapse, darken, Divider, Grid2, IconButton, InputAdornment, ListItemButton, ListItemIcon, ListItemText, Menu, MenuItem, Paper, SxProps, Table, TableBody, TableCell, TableContainer, TableHead, TablePagination, TableRow, TextField, Theme, Typography } from "@mui/material";
import { makeStyles } from "@mui/styles";
import { ChevronRight, Close, Search, ViewColumn } from "@mui/icons-material"
import { useEffect, useState } from "react";
import React from "react";
import { useMobile } from "hooks";
import classNames from "classnames";

const useStyles = makeStyles((theme: Theme) => {
  // const isMobile = useMobile()
  return ({
    gutter: {
      backgroundColor: darken(theme.palette.background.paper, 0.03),
      border: "none"
    },
    tableContainer: {
      // margin: theme.spacing(1),
      // [theme.breakpoints.up("sm")]: {
      //   margin: theme.spacing(2)
      // },
      // width: "auto"
    },
    title: {
      padding: theme.spacing(1),
    },
    subtitle: {
      padding: theme.spacing(1),
      marginTop: theme.spacing(-2),
    },
    titleComp: {
      padding: theme.spacing(1),
      marginLeft: theme.spacing(-1)
    },
    subtitleComp: {
      padding: theme.spacing(1),
      marginTop: theme.spacing(-2),
      marginLeft: theme.spacing(-1),
    },
    titleContainer: {
      border: "none",
    },
    searchContainer: {
      border: "none",
      textAlign: "right",
    },
    mobileTitleBox: {
      margin: theme.spacing(1),
      marginBottom: theme.spacing(1.5),
    },
    card: {
      // padding: theme.spacing(1),
      margin: theme.spacing(1),

    },
    rowHover: {
      cursor: "pointer",
      "&:hover": {
        backgroundColor: "rgba(150, 150, 150, 0.05)"
      }
    }
  })},
);

export interface Column<T> {
  title: string;
  cellStyle?: SxProps<Theme>;
  render: (row: T) => JSX.Element;
}

interface Props<T> {
  rows: T[],
  columns?: Column<T>[] | (() => Column<T>[]),
  title?: string | JSX.Element;
  subtitle?: string | JSX.Element | (() => string | JSX.Element);
  total: number,
  pageSize: number,
  page: number,
  setPage: (value: React.SetStateAction<number>) => void,
  handleRowsPerPageChange: (event: any) => void,
  renderGutter?: (row: T) => JSX.Element,
  setSearchText?: React.Dispatch<React.SetStateAction<string>>,
  isLoading?: boolean;
  multiGutter?: boolean;
  rowsPerPageOptions?: number[];
  noDataMessage?: string;
  renderMobile?: (row: T) => JSX.Element;
  hideKeys?: boolean;
  onRowClick?: (row: T) => void;
  actions?: string | JSX.Element;
}

export default function ResponsiveTable<T extends Object>(props: Props<T>) {
  const { 
    rows, 
    total, 
    page, 
    pageSize, 
    setPage, 
    handleRowsPerPageChange, 
    title, 
    // subtitle,
    renderGutter,
    setSearchText, 
    isLoading,
    multiGutter,
    // columns,
    rowsPerPageOptions,
    noDataMessage,
    renderMobile,
    hideKeys,
    onRowClick,
    actions
  } = props
  const classes = useStyles();
  const columns = typeof(props.columns) === "function" ? props.columns() : props.columns
  const subtitle = typeof(props.subtitle) === "function" ? props.subtitle() : props.subtitle

  const isMobile = useMobile()

  const [inputSearchText, setInputSearchText] = useState("");
  const [openGutters, setOpenGutters] = useState<number[]>([])
  const [handler, setHandler] = useState<NodeJS.Timeout | undefined>(undefined);
  const [columnAnchor, setColumnAnchor] = useState<any>(null)

  const filterKey = window.location.pathname.split('/').pop() + "-table-filter-list";

  const [filterList, setFilterList] = useState<string[]>(() => {
    const saved = localStorage.getItem(filterKey);
    return saved ? JSON.parse(saved) : [];
  });

  useEffect(() => {
    localStorage.setItem(filterKey, JSON.stringify(filterList));
  }, [filterList])

  const openColumnMenu = (event: any) => {
    setColumnAnchor(event.currentTarget);
    // setUserMenuIsOpen(true);
  };

  const closeColumnMenu = () => {
    setColumnAnchor(null);
    // setUserMenuIsOpen(true);
  };

  useEffect(() => {
    if (!setSearchText) return;
    if (inputSearchText === undefined) return;
    setHandler(setTimeout(() => {
      setSearchText(inputSearchText);
    }, 750)); // Delay of 750ms

    return () => clearTimeout(handler); // Cleanup on change
  }, [inputSearchText]);

  const handleCollapse = (index: number, event: React.MouseEvent<HTMLDivElement | HTMLButtonElement, MouseEvent>) => {
    event.stopPropagation();
    if (multiGutter) {
      if (openGutters.includes(index)) {
        setOpenGutters(openGutters => openGutters.filter(num => num !== index));
      } else {
        setOpenGutters(openGutters => [...openGutters, index]);
      }
    } else {
      if (openGutters.includes(index)) {
        setOpenGutters([])
      } else {
        setOpenGutters([index])
      }
    }
  }

  const handleKeyDown = (e: any) => {
    if (e.key === 'Enter') {
      if (setSearchText) setSearchText(inputSearchText);
      clearTimeout(handler)
    }
  };

  const handlePageChange = (page: number) => {
    setOpenGutters([])
    setPage(page)
  }

  const clearSearch = () => {
    setInputSearchText("")
  }

  const searchBar = () => {
    return (
      <TextField 
        fullWidth
        variant="standard"
        placeholder="Search..."
        className={classes.title}
        value={inputSearchText}
        onKeyDown={handleKeyDown}
        onChange={e => setInputSearchText(e.currentTarget.value)}
        InputProps={{
          startAdornment: (
            <InputAdornment position="start">
              <Search />
            </InputAdornment>
          ),
          endAdornment: (
            <InputAdornment position="end">
              <IconButton onClick={clearSearch}>
                <Close />
              </IconButton>
            </InputAdornment>
          ),
        }}
      />
    )
  }

  const renderTitle = () => {
    if (typeof(title) === "string" ) return (
      <Typography variant="h5" className={classes.title}>
        {title}
      </Typography>
    )
    if (title) return (
      <Box className={classes.titleComp}>
        {title}
      </Box>
    )
    return null
  }

  const renderSubtitle = () => {
    if (typeof(subtitle) === "string" ) return (
      <Typography variant="body2" color="textSecondary" className={classes.subtitle}>
        {subtitle}
      </Typography>
    )
    if (subtitle) return (
      <Box className={classes.subtitleComp}>
        {subtitle}
      </Box>
    )
    return null
  }

  function capitalize(val: any) {
    return String(val).charAt(0).toUpperCase() + String(val).slice(1);
  }

  const renderMobileRow = (row: T) => {
    if (renderMobile) return (
      renderMobile(row)
    )
    if (columns) {
      return (
        <Grid2 container direction={"row"} spacing={1} >
          {columns.map((column, index) => {
            return (
              <React.Fragment key={"mobile-row-column-"+index}>
                { !hideKeys && <Grid2 size={{ xs: 3 }} alignContent={"center"}>
                  <Typography fontWeight={"bold"} >
                    {column.title+": "}
                  </Typography>
                </Grid2>}
                <Grid2 size={{ xs: hideKeys ? 12 : 9 }} alignContent={"center"} >
                  {column.render(row)}
                </Grid2>
              </React.Fragment>
            )
          })}
        </Grid2>
      )
    }
    return (
      Object.keys(row).map((key, index) => {
        let value = row[key as keyof typeof row]
        
        return (
          <Grid2 container direction={"row"} key={"mobile-list-"+index} spacing={1} padding={1}>
            <Grid2 size={{ xs: 4 }} alignContent={"center"} >
              <Typography fontWeight={"bold"} >
                {capitalize(key)+": "}
              </Typography>
            </Grid2>
            <Grid2 size={{ xs: 8 }}>
              <Typography>
                {value+""}
              </Typography>
            </Grid2>
          </Grid2>
        )
      })
    )
  }


  if (isMobile) return (
    <Box>
      <Box className={classes.mobileTitleBox}>
        {renderTitle()}
        {renderSubtitle()}
        {setSearchText && searchBar()}
      </Box>
      {rows.map((row, index) => {
        return (
          <Card className={classNames(classes.card, onRowClick&&classes.rowHover)} key={"mobile-card-"+index} onClick={() => onRowClick&&onRowClick(row)}>
            {renderMobileRow(row)}
            {renderGutter &&
              <>
              <Divider/>
              <Collapse className={classes.gutter} in={openGutters.includes(index)} >
                {renderGutter(row)}
              </Collapse>
              {openGutters.includes(index) && <Divider/>}
              <ListItemButton 
                sx={{ 
                  width: "100%", 
                  display: "flex", 
                  justifyContent: "center"
                }} 
                onClick={(event) => handleCollapse(index, event)} 
                // style={{pre}}
              >
                <IconButton 
                  sx={{
                    transform: openGutters.includes(index) ? 
                                'rotate(-90deg)' : 'rotate(90deg)',
                    transition: 'transform 0.3s ease',
                    alignContent: "center"
                  }}
                >
                  <ChevronRight/>
                </IconButton>
              </ListItemButton>
              </>
            }
          </Card>
        )
      })}
      {rows.length < 1 &&
        (!isLoading ?
          <Typography sx={{ textAlign: "center" }} color="textSecondary">
            { noDataMessage ? noDataMessage : "No data."}
          </Typography>
        :
          <Box sx={{ width: "100%", textAlign: "center" }} >
            <CircularProgress/>
          </Box>
        )
      }
    </Box>
  )

  const filter = (colName: string) => {
    let newFilterList = [...filterList]
    if (filterList.includes(colName)) {
      newFilterList = newFilterList.filter(name => name !== colName)
    } else {
      newFilterList.push(colName)
    }
    setFilterList(newFilterList)
  }


  return (
    <TableContainer className={classes.tableContainer} component={Paper}>
      <Table>
        <TableHead>
          <TableRow sx={ !title && !setSearchText ? { display: "none" } : undefined}>
            <TableCell colSpan={10000} sx={{ border: "none" }}>
              <Grid2 container justifyContent="space-between">
                <Grid2>
                  <Box sx={{ marginTop: -1 }}>
                    {renderTitle()}
                    {renderSubtitle()}
                  </Box>
                </Grid2>
                <Grid2>
                  <Grid2 container alignItems={"center"} spacing={1}>
                    <Grid2>
                      {actions && actions}
                    </Grid2>
                    <Grid2>
                      {setSearchText &&<Box className={classes.searchContainer}>
                        {searchBar()}
                      </Box>}
                    </Grid2>
                    <Grid2>
                      { columns && 
                        <IconButton onClick={openColumnMenu} >
                          <ViewColumn />
                        </IconButton>
                      }
                    </Grid2>
                  </Grid2>
                </Grid2>
              </Grid2>
            </TableCell>
          </TableRow>
          <TableRow sx={ hideKeys ? { display: "none" } : undefined } >
            { renderGutter && <TableCell></TableCell>}
            { columns ?
              columns.map((column, index) => {
                if (filterList.includes(column.title)) return null
                return (
                  <TableCell key={"header-cell-"+index}>
                    {column.title}
                  </TableCell>
                )
              })
            : rows.length > 0 &&
              Object.keys(rows[0]).map((value, index) => {
                return(
                  <TableCell key={"header-cell-"+index}>
                    {value.toString()}
                  </TableCell>
                )
              })
            }
          </TableRow>
        </TableHead>
        <TableBody>
          { isLoading ? 
            <TableRow>
              { isLoading &&
                <TableCell colSpan={6} sx={{ textAlign: "center"}}>
                  <CircularProgress />
                </TableCell>
              }
            </TableRow>
          :
            rows.map((row, index) => {
              // if (filterList.includes()) return null
              return (
                <React.Fragment key={"row-"+index}>
                  <TableRow onClick={() => onRowClick&&onRowClick(row)} className={onRowClick ? classes.rowHover : undefined}>
                    {renderGutter && 
                      <TableCell width={1}>
                        <IconButton sx={{
                          transform: openGutters.includes(index) ? 
                                      'rotate(90deg)' : 'rotate(0deg)',
                          transition: 'transform 0.3s ease',
                          marginRight: isMobile ? -1 : 0
                        }}
                        onClick={(event) => handleCollapse(index, event)}>
                          <ChevronRight/>
                        </IconButton>
                      </TableCell>
                    }
                    {columns ? 
                      columns.map((column, j) => {
                        if (filterList.includes(column.title)) return null
                        if (column.render) return (
                          <TableCell key={"row-"+index+"cell-"+j} sx={{ padding: 0}} >
                            {column.render(row)}
                          </TableCell>
                        )
                        return (
                          <TableCell key={"row-"+index+"cell-"+j}>
                            {Object.values(row)[j].toString()}
                          </TableCell>
                        )
                      })
                    :
                      Object.values(row).map((value, j) => {
                        return (
                          <TableCell key={"row-"+index+"cell-"+j}>
                            {value ? value.toString() : ""}
                          </TableCell>
                        )
                      })
                    }
                  </TableRow>
                  {renderGutter && 
                    <TableRow className={classes.gutter}>
                      <TableCell colSpan={6} sx={{padding:0, margin: 0, border: "none"}}>
                        <Collapse in={openGutters.includes(index)} timeout="auto" unmountOnExit>
                          {renderGutter(row)}
                        </Collapse>
                      </TableCell>
                    </TableRow>
                  }
                </React.Fragment>
              )
            })
          }
          {rows.length < 1 && !isLoading &&
            <TableRow>
              <TableCell colSpan={6} sx={{textAlign: "center" }}>
                <Typography variant="body2" color="textSecondary">
                  {noDataMessage ? noDataMessage : "No data found"}  
                </Typography>  
              </TableCell>
            </TableRow>
          }
        </TableBody>
      </Table>
      <TablePagination
        rowsPerPageOptions={rowsPerPageOptions ? rowsPerPageOptions : [5, 10, 20]}
        component="div"
        count={total ? total : 0}
        rowsPerPage={pageSize}
        page={page}
        onPageChange={(_event, page) => handlePageChange(page)}
        onRowsPerPageChange={handleRowsPerPageChange}
      />
      <Menu
        id="userMenu"
        anchorEl={columnAnchor}
        open={columnAnchor !== null}
        onClose={closeColumnMenu}
        disableAutoFocusItem>
          {columns?.map((column, index) => {
            return (
              <MenuItem 
                onClick={() => filter(column.title)}
                key={"column-list-"+index} 
                aria-label="Open User Settings" 
                dense 
                sx={{ marginLeft: -0.75 }}
              >
                <ListItemIcon>
                  <Checkbox 
                    checked={!filterList.includes(column.title)}
                  />
                </ListItemIcon>
                <ListItemText primary={column.title} />
              </MenuItem>
            )
          })}
      </Menu>
    </TableContainer>
  )
}