import {
  Accordion,
  AccordionDetails,
  AccordionSummary,
  Box,
  Checkbox,
  CircularProgress,
  Drawer,
  List,
  ListItem,
  ListItemText,
  Tab,
  Tabs,
  TextField
} from "@mui/material";
import { ExpandMore } from "@mui/icons-material";
import SearchBar from "common/SearchBar";
import { useDeviceAPI, useMobile } from "hooks";
import { Component, Device } from "models";
import { pond } from "protobuf-ts/pond";
import React, { useEffect, useState } from "react";
import { makeStyles } from "@mui/styles";
import { useGlobalState } from "providers";

interface TabPanelProps {
  children?: React.ReactNode;
  index: any;
  value: any;
}

function TabPanelMine(props: TabPanelProps) {
  const { children, value, index, ...other } = props;

  return (
    <div
      role="tabpanel"
      hidden={value !== index}
      aria-labelledby={`simple-tab-${index}`}
      {...other}>
      {value === index && <React.Fragment>{children}</React.Fragment>}
    </div>
  );
}

const useStyles = makeStyles(() => ({
    drawerPaperDesktop: {
      height: "100%",
      width: "40%"
    },
    drawerPaperMobile: {
      height: "60%",
      width: "100%"
    }
}));
interface Props {
  open: boolean;
  deviceTags?: string[];
  close: (reload?: boolean) => void;
  linkedDevices: Map<string, pond.ComprehensiveDevice>;
  updateLinkedDevices: (dev: pond.ComprehensiveDevice, linked: boolean) => void;
  linkedComponents?: Component[];
  updateLinkedComponents?: (
    deviceID: string | number,
    componen: Component,
    linked: boolean
  ) => void;
  //all of these need to be sent in for the device preference selector to work
  devicePrefMap?: Map<number, number>; //map using the device id as the key and its pref enums value as the value
  prefOptions?: JSX.Element[];
  devicePrefChanged?: (device: Device, newPref: number) => void;
}

export default function DeviceLinkDrawer(props: Props) {
  const {
    open,
    close,
    linkedDevices,
    linkedComponents,
    updateLinkedDevices,
    updateLinkedComponents,
    deviceTags,
    devicePrefMap,
    prefOptions,
    devicePrefChanged
  } = props;
  const [value, setValue] = useState(0);
  const [{as}] = useGlobalState();
  const deviceAPI = useDeviceAPI();
  const classes = useStyles();
  const isMobile = useMobile();
  const [deviceList, setDeviceList] = useState<Map<string, pond.ComprehensiveDevice>>(
    new Map<string, pond.ComprehensiveDevice>()
  );
  const [searchVal, setSearchVal] = useState("");
  const [searchedDevices, setSearchedDevices] = useState<Map<string, pond.ComprehensiveDevice>>(
    new Map<string, pond.ComprehensiveDevice>()
  );
  const [accordionController, setAccordionController] = useState<boolean[]>([]);
  const [loadingDevices, setLoadingDevices] = useState(false);

  const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
    setValue(newValue);
  };

  useEffect(() => {
    if (loadingDevices) return;
    setLoadingDevices(true);
    deviceAPI
      .list(
        500,
        0,
        "asc",
        "key",
        deviceTags?.toString(),
        undefined,
        undefined,
        undefined,
        undefined,
        true,
        undefined,
        undefined,
        undefined,
        undefined,
        undefined,
        as
      )
      .then(resp => {
        let devMap = new Map<string, pond.ComprehensiveDevice>();
        resp.data.comprehensiveDevices.forEach(compDev => {
          if (compDev.device?.settings?.deviceId) {
            devMap.set(compDev.device?.settings?.deviceId.toString(), compDev);
          }
        });
        setDeviceList(devMap);
        setSearchedDevices(devMap);
      })
      .catch(err => {})
      .finally(() => {
        setLoadingDevices(false);
      });
    //eslint-disable-next-line react-hooks/exhaustive-deps
  }, [deviceAPI, as]);

  useEffect(() => {
    let searchedDevices: Map<string, pond.ComprehensiveDevice> = new Map();
    if (searchVal !== "") {
      deviceList.forEach(dev => {
        if (dev.device?.settings?.name) {
          if (
            dev.device?.settings?.name.toLowerCase().includes(searchVal.toLowerCase()) ||
            dev.device?.settings?.deviceId.toString().includes(searchVal)
          ) {
            searchedDevices.set(dev.device.settings.deviceId.toString(), dev);
          }
        } else {
          if (dev.device?.settings?.deviceId.toString().includes(searchVal)) {
            searchedDevices.set(dev.device.settings.deviceId.toString(), dev);
          }
        }
      });
      setSearchedDevices(searchedDevices);
    } else {
      setSearchedDevices(deviceList);
    }
  }, [searchVal, deviceList]);

  //sets the length of the array of booleans to control opening each of the accordions
  useEffect(() => {
    let accordionControls: boolean[] = [];
    linkedDevices.forEach(() => {
      accordionControls.push(false);
    });
    deviceList.forEach(dev => {
      if (
        dev.device?.settings?.deviceId &&
        !linkedDevices.get(dev.device?.settings?.deviceId.toString())
      ) {
        accordionControls.push(false);
      }
    });
    setAccordionController(accordionControls);
  }, [linkedDevices, deviceList]);

  const closeDrawer = () => {
    close();
  };

  const devicesTab = () => {
    return (
      <Box>
        Select Devices to link
        <List>
          <SearchBar
            value={searchVal}
            onChange={val => {
              setSearchVal(val);
            }}
          />
          {Array.from(searchedDevices.values()).map(dev => {
            let defCheck = linkedDevices.has(dev.device?.settings?.deviceId.toString() ?? "");
            return (
              <ListItem key={dev.device?.settings?.deviceId}>
                <ListItemText
                  primary={dev.device?.settings?.name ?? "Device " + dev.device?.settings?.deviceId}
                />
                <Checkbox
                  onChange={(_, checked) => {
                    if (dev.device?.settings?.deviceId) {
                      updateLinkedDevices(dev, checked);
                    }
                  }}
                  edge="end"
                  defaultChecked={defCheck}
                  inputProps={{ "aria-labelledby": dev.device?.settings?.name }}
                />
              </ListItem>
            );
          })}
        </List>
      </Box>
    );
  };

  const linkedDevicesTab = () => {
    let linkOptions: pond.ComprehensiveDevice[] = [];
    Array.from(linkedDevices.keys()).forEach(key => {
      let dev = deviceList.get(key);
      let linkedDev = linkedDevices.get(key);
      if (dev) {
        linkOptions.push(dev);
      } else if (linkedDev) {
        linkOptions.push(linkedDev);
      }
    });

    return (
      <Box>
        From the linked Devices select which components to use
        {linkOptions.map((op, i) => {
          let device = Device.any(op.device);
          let pref = 0;
          if (devicePrefMap) {
            pref = devicePrefMap.get(device.id()) ?? 0;
          }
          return (
            <Accordion
              key={device.id()}
              expanded={accordionController[i]}
              onChange={(_, expanded) => {
                let accordionControls = accordionController;
                accordionControls[i] = expanded;
                setAccordionController([...accordionControls]);
              }}>
              <AccordionSummary expandIcon={<ExpandMore />}>{device.name()}</AccordionSummary>
              <AccordionDetails>
                <List>
                  {op.components.map(comp => {
                    let component = Component.any(comp);
                    return (
                      <ListItem key={component.key()}>
                        <ListItemText primary={component.name()} />
                        <Checkbox
                          onChange={(_, checked) => {
                            if (component.key() && device.id() && updateLinkedComponents) {
                              updateLinkedComponents(device.id(), component, checked);
                            }
                          }}
                          edge="end"
                          checked={
                            linkedComponents &&
                            linkedComponents.some(el => el.key() === component.key())
                          }
                          inputProps={{ "aria-labelledby": component.name() }}
                        />
                      </ListItem>
                    );
                  })}
                </List>
                {devicePrefMap && prefOptions && devicePrefChanged && (
                  <TextField
                    title="Gate Device Type"
                    select
                    value={pref}
                    variant="outlined"
                    onChange={e => {
                      pref = parseInt(e.target.value);
                      devicePrefChanged(device, pref);
                    }}
                    fullWidth>
                    {prefOptions}
                  </TextField>
                )}
              </AccordionDetails>
            </Accordion>
          );
        })}
      </Box>
    );
  };

  return (
    <Drawer
      open={open}
      onClose={closeDrawer}
      classes={{ paper: isMobile ? classes.drawerPaperMobile : classes.drawerPaperDesktop }}
      anchor={isMobile ? "bottom" : "right"}>
      {open && (
        <React.Fragment>
          <Tabs value={value} indicatorColor="primary" textColor="primary" onChange={handleChange}>
            <Tab label="Devices" />
            {linkedComponents && <Tab label="Linked Devices" />}
          </Tabs>
          <TabPanelMine value={value} index={0}>
            {loadingDevices ? (
              <Box style={{ textAlign: "center", paddingTop: 20 }}>
                <CircularProgress />
              </Box>
            ) : (
              devicesTab()
            )}
          </TabPanelMine>
          {linkedComponents && (
            <TabPanelMine value={value} index={1}>
              {linkedDevicesTab()}
            </TabPanelMine>
          )}
        </React.Fragment>
      )}
    </Drawer>
  );
}
