import { Box, Button, Grid2 as Grid } from "@mui/material";
// import { getTableIcons } from "common/ResponsiveTable";
import SearchSelect, { Option } from "common/SearchSelect";
// import MaterialTable, { MTableToolbar } from "material-table";
// import { Bin, BinYard, Field } from "models";
import { Bin } from "models";
// import { Gate } from "models/Gate";
// import { GrainBag } from "models/GrainBag";
// import { ObjectHeater } from "models/ObjectHeater";
import ObjectDescriber, { SearchableObjects } from "objects/ObjectDescriber";
import { pond } from "protobuf-ts/pond";
import {
  useBinAPI,
  useBinYardAPI,
  // useFieldAPI,
  useGateAPI,
  useGlobalState,
  // useGrainBagAPI,
  // useObjectHeaterAPI
} from "providers";
import { useCallback, useEffect, useState } from "react";
import TeamSearch from "teams/TeamSearch";
import BulkBinSettings from "./bulkEditForms/bulkBinSettings";
import ResponsiveTable from "common/ResponsiveTable";
import { cloneDeep } from "lodash";
//import BulkGateSettings from "./bulkEditForms/bulkGateSettings";

interface customButton {
  label: string;
  function: (selectedObjects: any[]) => void;
}

interface Props {
  startingObject?: pond.ObjectType;
  showControls?: boolean;
  preLoadedData?: any[];
  customButtons?: customButton[];
  rowClickFunction?: (rowData: any) => void;
}

export default function ObjectTable(props: Props) {
  const { startingObject, showControls, preLoadedData, customButtons, rowClickFunction } = props;
  const [currentType, setCurrentType] = useState(
    startingObject ?? pond.ObjectType.OBJECT_TYPE_UNKNOWN
  );
  const [selectedType, setSelectedType] = useState<Option | null>();
  const [objectTotal, setObjectTotal] = useState(0);
  const [pageSize, setPageSize] = useState(10); //offset would be determined by the pageSize * tablePage, limit is just pageSize
  const [tablePage, setTablePage] = useState(0);
  const [tableData, setTableData] = useState<any[]>([]);
  const [loadedData, setLoadedData] = useState<any[]>([]);
  //const [selectedObjects, setSelectedObjects] = useState<any[]>([]);
  const [selectedObjects, setSelectedObjects] = useState<Map<string, any>>(new Map());
  //const [currentDirection, setCurrentDirection] = useState<"asc" | "desc">("asc");
  const [currentSearchText, setCurrentSearchText] = useState<string>("");
  //const [currentOrder, setCurrentOrder] = useState<string | undefined>();
  //const [isNumerical, setIsNumerical] = useState<boolean | undefined>();
  const [{ user, as }] = useGlobalState();
  const [selectedUser, setSelectedUser] = useState<string>(as ?? user.id());
  const [selectedPageRows, setSelectedPageRows] = useState<Map<number, number[]>>(new Map())//key is the page value is an array of row indexes for that page
  const [tableLoading, setTableLoading] = useState(false);

  const objectExtension = ObjectDescriber(currentType);
  const columns = objectExtension.getTableColumns ? objectExtension.getTableColumns(user) : objectExtension.tableColumns;
  //the api's to load all the objects available to look at in this table
  const binAPI = useBinAPI();
  const binyardAPI = useBinYardAPI();
  // const fieldAPI = useFieldAPI();
  const gateAPI = useGateAPI();
  // const grainBagAPI = useGrainBagAPI();
  // const heaterAPI = useObjectHeaterAPI();

  const load = useCallback(() => {
    if (tableLoading) return;
    /**
     * switch case to use the correct api to load the object that was selected.
     * In the future create a generic object api that takes the object type as a variable along with
     * the rest that the backend then uses to load from the correct place
     */
    //add a new case for each object that will be viewable in the table
    let search;
    if (currentSearchText) {
      search = currentSearchText?.replace(/\s/g, "<->");
    }
    if (currentType !== pond.ObjectType.OBJECT_TYPE_UNKNOWN) {
      setTableLoading(true);
    }
    switch (currentType) {
      case pond.ObjectType.OBJECT_TYPE_BIN:
        binAPI
          .listBins(
            pageSize,
            pageSize * tablePage,
            "asc",
            "name",
            search,
            selectedUser,
            undefined
          )
          .then(resp => {
            //setLoadedData(resp.data.bins.map(b => Bin.create(b)))
            setTableData(resp.data.bins.map(b => Bin.create(b)));
            setObjectTotal(resp.data.total);
            setTableLoading(false);
          });
        break;
      // case pond.ObjectType.OBJECT_TYPE_BINYARD:
      //   binyardAPI
      //     .listBinYards(
      //       pageSize,
      //       pageSize * tablePage,
      //       currentDirection,
      //       currentOrder,
      //       search,
      //       undefined,
      //       selectedUser
      //     )
      //     .then(resp => {
      //       setTableData(resp.data.yard.map(b => BinYard.create(b)));
      //       setObjectTotal(resp.data.total);
      //       setTableLoading(false);
      //     });
      //   break;
      // case pond.ObjectType.OBJECT_TYPE_FIELD:
      //   fieldAPI
      //     .listFields(
      //       pageSize,
      //       pageSize * tablePage,
      //       currentDirection,
      //       currentOrder,
      //       search,
      //       selectedUser
      //     )
      //     .then(resp => {
      //       setTableData(resp.data.fields.map(f => Field.create(f)));
      //       setObjectTotal(resp.data.total);
      //       setTableLoading(false);
      //     });
      //   break;
      // case pond.ObjectType.OBJECT_TYPE_GATE:
      //   gateAPI
      //     .listGates(
      //       pageSize,
      //       pageSize * tablePage,
      //       currentDirection,
      //       currentOrder,
      //       search,
      //       undefined,
      //       undefined,
      //       undefined,
      //       isNumerical,
      //       selectedUser
      //     )
      //     .then(resp => {
      //       setTableData(resp.data.gates.map(g => Gate.create(g)));
      //       setObjectTotal(resp.data.total);
      //       setTableLoading(false);
      //     });
      //   break;
      // case pond.ObjectType.OBJECT_TYPE_GRAIN_BAG:
      //   grainBagAPI
      //     .listGrainBags(
      //       pageSize,
      //       pageSize * tablePage,
      //       currentDirection,
      //       currentOrder,
      //       search,
      //       undefined,
      //       undefined,
      //       isNumerical,
      //       selectedUser
      //     )
      //     .then(resp => {
      //       setTableData(resp.data.grainBags.map(gb => GrainBag.create(gb)));
      //       setObjectTotal(resp.data.total);
      //       setTableLoading(false);
      //     });
      //   break;
      // case pond.ObjectType.OBJECT_TYPE_HEATER:
      //   heaterAPI
      //     .listObjectHeaters(
      //       pageSize,
      //       pageSize * tablePage,
      //       currentDirection,
      //       currentOrder,
      //       search,
      //       undefined,
      //       selectedUser,
      //       undefined,
      //       undefined,
      //       isNumerical
      //     )
      //     .then(resp => {
      //       setTableData(resp.data.heaters.map(h => ObjectHeater.create(h)));
      //       setObjectTotal(resp.data.total);
      //       setTableLoading(false);
      //     });
        // break;
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [
    //currentDirection,
    //currentOrder,
    currentType,
    selectedUser,
    //isNumerical,
    pageSize,
    tablePage,
    currentSearchText,
    binAPI,
    binyardAPI,
    // fieldAPI,
    gateAPI,
    // grainBagAPI,
    // heaterAPI
  ]);

  useEffect(() => {
    if (preLoadedData && currentSearchText === "") {
      setLoadedData(preLoadedData);
      setObjectTotal(preLoadedData.length);
    } else {
      load();
    }
  }, [load, preLoadedData, currentSearchText]);

  useEffect(()=>{
    let start = pageSize * (tablePage)
    let end = pageSize * (tablePage + 1)
    let currentData: any[] = []
    loadedData.forEach((object,i) => {
      if (i >= start && i < end ) {
        currentData.push(object)
      }
    })
    setTableData(currentData)
  },[loadedData, pageSize, tablePage])

  const bulkEditForm = (type: pond.ObjectType) => {
    switch (type) {
      case pond.ObjectType.OBJECT_TYPE_BIN:
        return (
          <BulkBinSettings
            selectedBins={Array.from(selectedObjects.values()) as pond.Bin[]}
            extraButtons={customButtons?.map(b => (
              <Button variant="contained" onClick={() => {b.function(Array.from(selectedObjects.values()))}}>{b.label}</Button>
            ))}
            refreshCallback={reLoad => {
              //setSelectedObjects([])
              setSelectedObjects(new Map())
              setSelectedPageRows(new Map())
              if (reLoad) {
                load();
              }
            }}
          />
        );
      case pond.ObjectType.OBJECT_TYPE_GRAIN_BAG:
        return (
          // <BulkGrainBagSettings
          //   selectedBags={selectedObjects as GrainBag[]}
          //   refreshCallback={reLoad => {
          //     if (reLoad) {
          //       load();
          //     }
          //   }}
          // />
          undefined
        );
      default:
    }
  };

  // const actionToolbar = () => {
  //   return (
  //     <Box width="100%" padding={2}>
  //       <SearchBar
  //         value={currentSearchText ?? ""}
  //         onChange={val => {
  //           setCurrentSearchText(val);
  //         }}
  //       />
  //       {selectedObjects.length > 0 && <Box paddingTop={2}>{bulkEditForm(currentType)}</Box>}
  //       <Grid container direction="row" spacing={2} style={{ marginTop: 2 }}>
  //         {customButtons &&
  //           selectedObjects.length > 0 &&
  //           customButtons.map((button, i) => (
  //             <Grid size={{ xs: 3 }} key={i}>
  //               <Button
  //                 style={{ width: "100%" }}
  //                 variant="contained"
  //                 color="primary"
  //                 onClick={() => button.function(selectedObjects)}>
  //                 {button.label}
  //               </Button>
  //             </Grid>
  //           ))}
  //       </Grid>
  //     </Box>
  //   );
  // };

  const handleRowsPerPageChange = (event: any) => {
    const newRowsPerPage = parseInt(event.target.value, 10); // Get selected value
    setPageSize(newRowsPerPage);
    setTablePage(0)
  }

  const rowSelected = (row: any, index: number, checked: boolean) => {
    //when a row is selected add it to the map for the page
    let selected = cloneDeep(selectedObjects)
    let pageMap = cloneDeep(selectedPageRows)
    let pageSelect = pageMap.get(tablePage)
    if(checked){ //add the page/index to the map
      if(pageSelect){
        pageSelect.push(index)
      }else{
        pageMap.set(tablePage, [index])
      }
      //handle the row data to add it to a list of objects that are selected
      selected.set(tablePage + ":" + index, row)
    }else{ //remove the page/index from the map
      if(pageSelect){
        pageSelect.splice(pageSelect.indexOf(index))
      }
      //remove the selected object from the array
      selected.delete(tablePage + ":" + index)
    }
    setSelectedObjects(selected)
    setSelectedPageRows(pageMap)
  }

  return (
    <Box padding={2}>
      {showControls && (
        <Grid container direction="row" spacing={2} style={{ padding: 10 }}>
          <Grid>
            <SearchSelect
              style={{ width: 200 }}
              label="From"
              selected={selectedType}
              options={SearchableObjects()}
              changeSelection={op => {
                //setSelectedObjects([]);
                setSelectedObjects(new Map());
                setSelectedType(op);
                if (op?.value) {
                  setCurrentType(op.value);
                  setTablePage(0);
                  setCurrentSearchText("");
                }
              }}
            />
          </Grid>
          <Grid style={{ width: 300 }}>
            <TeamSearch
              label="User/Team"
              loadUsers
              setTeamCallback={user => {
                setSelectedUser(user);
                //dispatch({ key: "as", value: user.toString() });
              }}
            />
          </Grid>
        </Grid>
      )}
      
      <ResponsiveTable
        rows={tableData}
        columns={columns}
        onRowClick={rowClickFunction}
        total={objectTotal}
        pageSize={pageSize}
        page={tablePage}
        setPage={setTablePage}
        handleRowsPerPageChange={handleRowsPerPageChange}
        setSearchText={setCurrentSearchText}
        customElement={bulkEditForm(currentType)}
        rowSelect={rowSelected}
        selectedRows={selectedPageRows.get(tablePage)}
      />
      {/* <MaterialTable
        isLoading={tableLoading}
        key="objectList"
        title={ObjectDescriber(currentType).name}
        icons={getTableIcons()}
        columns={ObjectDescriber(currentType, user).tableColumns}
        data={tableData}
        page={tablePage}
        totalCount={objectTotal}
        onRowClick={
          rowClickFunction
            ? (_, data) => {
                rowClickFunction(data);
              }
            : undefined
        }
        onChangePage={page => {
          setTablePage(page);
        }}
        onOrderChange={(by, direction) => {
          if (by !== -1) {
            let colName = ObjectDescriber(currentType, user).tableColumns[by].title?.toString();
            let order;
            if (colName) {
              order = ObjectDescriber(currentType, user).tableSort.get(colName);
              setCurrentOrder(order?.order);
              setCurrentDirection(direction);
              setIsNumerical(order?.numerical);
            }
          }
        }}
        options={{
          paging: true,
          pageSize: pageSize,
          pageSizeOptions: [5, 10, 20],
          padding: "dense",
          showTitle: true,
          toolbar: true,
          sorting: true,
          columnsButton: true,
          search: false,
          selection: true
        }}
        onSelectionChange={(data, rowData) => {
          setSelectedObjects(data);
        }}
        actions={[
          {
            tooltip: "Update Selected Objects",
            icon: "edit",
            onClick: (evt, data) => {console.log(data)}
          }
        ]}
        onChangeRowsPerPage={pageSize => {
          setPageSize(pageSize);
        }}
        components={{
          Toolbar: props => (
            <React.Fragment>
              <MTableToolbar {...props} />
              {actionToolbar()}
            </React.Fragment>
          )
        }}
      /> */}
    </Box>
  );
}
