import {
  Button,
  Checkbox,
  darken,
  DialogActions,
  DialogContent,
  DialogTitle,
  Divider,
  Grid2,
  IconButton,
  lighten,
  List,
  ListItem,
  ListItemSecondaryAction,
  ListItemText,
  Paper,
  Step,
  StepLabel,
  Stepper,
  Tab,
  Tabs,
  TextField,
  Theme,
  Typography
} from "@mui/material";
import { makeStyles } from "@mui/styles";
import CancelSubmit from "common/CancelSubmit";
import DeleteButton from "common/DeleteButton";
import Loader from "common/Loader";
import ResponsiveDialog from "common/ResponsiveDialog";
import SearchBar from "common/SearchBar";
import { useDeviceAPI, useGroupAPI, usePrevious, useSnackbar } from "hooks";
import { Device, Group } from "models";
import { filterDevices } from "pbHelpers/Device";
import { groupsAreEqual } from "pbHelpers/Group";
import { useGlobalState } from "providers";
import React, { useCallback, useEffect, useState } from "react";
import RemoveGroup from "./RemoveGroup";
import { pond } from "protobuf-ts/pond";
import { Add, Remove } from "@mui/icons-material";

const useStyles = makeStyles((theme: Theme) => {
  return ({
    deviceListContainer: {
      marginBottom: theme.spacing(1)
    },
    listTitle: {
      fontSize: 20,
      fontWeight: 650
    },
    listButton: {
      cursor: "pointer",
      "&:hover": {
        backgroundColor: lighten(theme.palette.background.default, 0.25)
      }
    },
    devicesList: {
      overflow: "auto",
      minHeight: "25vh",
      maxHeight: "35vh",
      height: "auto",
      background:
        theme.palette.mode === "dark"
          ? lighten(theme.palette.background.paper, 0.04)
          : darken(theme.palette.background.paper, 0.04)
    }
  });
});

interface Props {
  initialGroup?: Group;
  mode?: "add" | "update" | "remove" | undefined;
  isDialogOpen: boolean;
  closeDialogCallback: Function;
  refreshCallback: () => void;
  canEdit?: boolean;
  groupDevices?: Device[];
  removeGroupCallback?: Function;
  addGroupCallback?: (group: pond.GroupSettings) => void
}

export default function GroupSettings(props: Props) {
  const classes = useStyles();
  const { success, error, warning } = useSnackbar();
  const {
    initialGroup,
    mode,
    isDialogOpen,
    closeDialogCallback,
    refreshCallback,
    canEdit,
    groupDevices,
    removeGroupCallback,
    addGroupCallback,
  } = props;
  const prevInitialGroup = usePrevious(initialGroup);
  const [{as}] = useGlobalState();
  const groupAPI = useGroupAPI();
  const deviceAPI = useDeviceAPI();
  const snackbar = useSnackbar()
  const [devices, setDevices] = useState<Device[]>([]);
  const [nonGroupDevices, setNonGroupDevices] = useState<Device[]>([]);
  const [group, setGroup] = useState<Group>(initialGroup ? Group.clone(initialGroup) : new Group());
  const [isRemoveGroupOpen, setIsRemoveGroupOpen] = useState<boolean>(
    mode === "remove" ? true : false
  );
  const [tabIndex, setTabIndex] = useState<number>(0);
  const prevTabIndex = usePrevious(tabIndex);
  const [steps] = useState<string[]>(["General group information", "Select devices for the group"]);
  const [deviceSearch, setDeviceSearch] = useState<string>("");
  const [loadingDevices, setLoadingDevices] = useState<boolean>(false);

  const [groupDeviceNumbers, setGroupDeviceNumbers] = useState<number[]>([])

  useEffect(() => {
    let newNumbers: number[] = []
    groupDevices?.forEach(device => {
      newNumbers.push(device.id())
    })
    setGroupDeviceNumbers(newNumbers)
  }, [groupDevices])

  const loadDevices = useCallback(() => {
    setLoadingDevices(true);
    //this lists the devices for the user/team viewing
    deviceAPI
      .list(1000000, 0, "asc")
      .then((response: any) => {
        let rDevices: Device[] = response.data.devices
          ? response.data.devices.map((device: any) => Device.any(device))
          : [];
        //remove duplicates from the options
        setDevices(rDevices);
      })
      .catch((_error: any) => {
        setDevices([]);
      })
      .finally(() => setLoadingDevices(false));
  }, [deviceAPI, as]);

  useEffect(()=>{
    let ungrouped: Device[] = []
    devices.forEach(device => {
      if(!groupDeviceNumbers.includes(device.id())){
        ungrouped.push(device)
      }
    })
    setNonGroupDevices(ungrouped)
  },[groupDeviceNumbers, devices])

  useEffect(() => {
    if (prevInitialGroup !== initialGroup) {
      setGroup(initialGroup ? Group.clone(initialGroup) : new Group());
      //setDevices([]);
    }

    if (prevTabIndex !== 1 && tabIndex === 1) {
      loadDevices();
    }
  }, [initialGroup, loadDevices, prevInitialGroup, prevTabIndex, props, tabIndex]);

  const close = () => {
    closeDialogCallback();
    setGroup(initialGroup ? Group.clone(initialGroup) : new Group());
    setDevices([]);
    setTabIndex(0);
  };

  const submit = () => {
    const groupName = group.name();
    switch (mode) {
      case "add":
        groupAPI
          .addGroup(group.settings)
          .then((response: any) => {
            success(groupName + " was successfully created");
            close();
            group.settings.groupId = parseInt(response.data.id)
            if (addGroupCallback) addGroupCallback(group.settings)
            else refreshCallback();
          })
          .catch((err: any) => {
            err.response.data.error
              ? warning(err.response.data.error)
              : error("Error occured when creating a group");
            close();
          })
          .finally(() => setTabIndex(0));
        break;
      default:
        groupAPI
          .updateGroup(group.id(), group.settings)
          .then((_response: any) => {
            success(groupName + " was successfully updated");
            close();
            refreshCallback();
          })
          .catch((err: any) => {
            err.response.data.error
              ? warning(err.response.data.error)
              : error("Error occured when updating " + groupName);
            close();
          });
        break;
    }
  };

  const openRemoveGroup = () => {
    setIsRemoveGroupOpen(true);
  };

  const closeRemoveGroup = () => {
    setIsRemoveGroupOpen(false);
    if (mode === "remove") {
      close();
    }
  };

  const isFormValid = () => {
    const validGroup: boolean = group !== undefined && group !== null;
    const validUpdate: boolean =
      mode !== "update" || !groupsAreEqual(Group.clone(initialGroup), group);

    return validGroup && validUpdate;
  };

  const changeTab = (value: number) => {
    setTabIndex(value);
  };

  const nextStep = () => {
    let nextStepIndex = tabIndex + 1;
    setTabIndex(nextStepIndex);
  };

  const backStep = () => {
    let backStepIndex = tabIndex - 1;
    setTabIndex(backStepIndex);
  };

  const changeDeviceSearch = (value: string) => {
    setDeviceSearch(value);
  };

  const changeName = (event: any) => {
    let updatedGroup = Group.clone(group);
    updatedGroup.settings.name = event.target.value;
    setGroup(updatedGroup);
  };

  const changeDescription = (event: any) => {
    let updatedGroup = Group.clone(group);
    updatedGroup.settings.description = event.target.value;
    setGroup(updatedGroup);
  };

  const changeDevices = (device: number) => {
    console.log("change")
    let updatedGroup = Group.clone(group);
    const exists = updatedGroup.settings.devices.includes(device);
    if (exists) {
      console.log("exists")
      updatedGroup.settings.devices = updatedGroup.settings.devices.filter(
        groupDevice => groupDevice !== device
      );
    } else {
      updatedGroup.settings.devices.push(device);
    }
    setGroup(updatedGroup);
  };

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UI BEGINS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  const title = () => {
    switch (mode) {
      case "add":
        return (
          <React.Fragment>
            Group Creation
            <Typography variant="body2" color="textSecondary" gutterBottom>
              Groups are a great way to organize your devices
            </Typography>
          </React.Fragment>
        );
      case "update":
        return (
          <React.Fragment>
            Group Settings
            <Typography variant="body2" color="textSecondary">
              {group.name()}
            </Typography>
          </React.Fragment>
        );
      default:
        return <React.Fragment />;
    }
  };

  const generalGroupContent = () => {
    return (
      <React.Fragment>
        <TextField
          id="group-name"
          label="Name"
          value={group.settings.name}
          onChange={changeName}
          margin="normal"
          variant="outlined"
          disabled={!canEdit}
          fullWidth
        />

        <TextField
          id="group-description"
          label="Description"
          multiline
          rows={4}
          // rowsMax="4"
          value={group.settings.description}
          onChange={changeDescription}
          margin="normal"
          variant="outlined"
          disabled={!canEdit}
          fullWidth
        />
      </React.Fragment>
    );
  };

  const devicesGroupContent = () => {
    if (mode !== "add") return devicesTab();
    return (
      <Paper elevation={0} className={classes.deviceListContainer}>
        <SearchBar value={deviceSearch} onChange={changeDeviceSearch} shape="square" />
        {loadingDevices ? (
          <List className={classes.devicesList}>
            <ListItem>
              <Grid2 container justifyContent="center">
                <Loader size="small" />
              </Grid2>
            </ListItem>
          </List>
        ) : (
          <React.Fragment>
            {groupDevices &&
            <List dense className={classes.devicesList}>
              {filterDevices(deviceSearch, groupDevices, [])
                .sort((a, b: Device) => a.name().localeCompare(b.name()))
                .map(device => {
                  const label = `checkbox-list-secondary-label-${device.id()}`;
                  return (
                    <ListItem className={classes.listButton} key={device.id()} onClick={() =>  {
                      removeDevice(device.id())
                    }}>
                      <ListItemText id={label} primary={device.name()} />
                      <ListItemSecondaryAction>
                        {/* <Checkbox
                          edge="end"
                          onChange={() => changeDevices(device.id())}
                          checked={group.settings.devices.includes(device.id())}
                          inputProps={{ "aria-labelledby": label }}
                          disabled={!canEdit}
                          /> */}
                            -
                      </ListItemSecondaryAction>
                    </ListItem>
                  );
                })}
            </List>
            }
            <List dense className={classes.devicesList}>
              {filterDevices(deviceSearch, nonGroupDevices, [])
                .sort((a, b: Device) => a.name().localeCompare(b.name()))
                .map(device => {
                  const label = `checkbox-list-secondary-label-${device.id()}`;
                  return (
                    <ListItem className={classes.listButton} key={device.id()} onClick={() =>  {
                      addDevice(device.id())
                    }}>
                      <ListItemText id={label} primary={device.name()} />
                      <ListItemSecondaryAction>
                        {/* <Checkbox
                          edge="end"
                          onChange={() => changeDevices(device.id())}
                          checked={group.settings.devices.includes(device.id())}
                          inputProps={{ "aria-labelledby": label }}
                          disabled={!canEdit}
                          /> */}
                            +
                      </ListItemSecondaryAction>
                    </ListItem>
                  );
                })}
            </List>
          </React.Fragment>
        )}
      </Paper>
    );
  };

  //useEffect(() => {
  //  if (tabIndex === 1) loadDevices()
  //}, [groupDevices])

  const addDevice = (device: number) => {
    groupAPI.addDevice(group.id(), device).then(() => {
      let newDevices = [...groupDeviceNumbers];
      newDevices.push(device)
      setGroupDeviceNumbers(newDevices)
      snackbar.success("Device " + device + " successfully added to group")
      refreshCallback();
    }).catch(() => {
      snackbar.error("Failed to add device")
    });
  };

  const removeDevice = (device: number) => {
    groupAPI.removeDevice(group.id(), device).then(() => {
      let newDevices = groupDeviceNumbers.filter(dev => dev !== device)
      setGroupDeviceNumbers(newDevices)
      snackbar.success("Device " + device + " successfully removed from group")
      refreshCallback();
    }).catch(() => {
      snackbar.error("Failed to remove device")
    })
  };

  const devicesTab = () => {
    return (
      <Paper elevation={0} className={classes.deviceListContainer}>
        <SearchBar value={deviceSearch} onChange={changeDeviceSearch} shape="square" />
        {loadingDevices ? (
          <List className={classes.devicesList}>
            <ListItem>
              <Grid2 container justifyContent="center">
                <Loader size="small" />
              </Grid2>
            </ListItem>
          </List>
        ) : (
          <React.Fragment>
          {groupDevices &&
            <React.Fragment>
              <Typography className={classes.listTitle}>Grouped Devices</Typography>
              <List dense className={classes.devicesList}>
                {filterDevices(deviceSearch, groupDevices, [])
                  .sort((a, b: Device) => a.name().localeCompare(b.name()))
                  .map(device => {
                    const label = `checkbox-list-secondary-label-${device.id()}`;
                    return (
                      <ListItem className={classes.listButton} key={device.id()} onClick={() =>  {
                        removeDevice(device.id())
                      }}>
                        <ListItemText id={label} primary={device.name()} />
                        <ListItemSecondaryAction>
                          {/* <Checkbox
                            edge="end"
                            onChange={() => changeDevices(device.id())}
                            checked={group.settings.devices.includes(device.id())}
                            inputProps={{ "aria-labelledby": label }}
                            disabled={!canEdit}
                            /> */}
                            <Remove />
                        </ListItemSecondaryAction>
                      </ListItem>
                    );
                  })}
              </List>
            </React.Fragment>
          }
          <Typography className={classes.listTitle}>Devices You Can Add</Typography>
          <List dense className={classes.devicesList}>
            {filterDevices(deviceSearch, nonGroupDevices, [])
              .sort((a, b: Device) => a.name().localeCompare(b.name()))
              .map(device => {
                const label = `checkbox-list-secondary-label-${device.id()}`;
                return (
                  <ListItem className={classes.listButton} key={device.id()} onClick={() =>  {
                    addDevice(device.id())
                  }}>
                    <ListItemText id={label} primary={device.name()} />
                    <ListItemSecondaryAction>
                      {/* <Checkbox
                        edge="end"
                        onChange={(_, checked) => {
                          if (checked) addDevice(device.id());
                          else removeDevice(device.id());
                        }}
                        // checked={Boolean(groupDevices?.find(dev => dev.id() === device.id()))}
                        checked={groupDeviceNumbers.includes(device.id())}
                        inputProps={{ "aria-labelledby": label }}
                        disabled={!canEdit}
                      /> */}
                        <Add />
                    </ListItemSecondaryAction>
                  </ListItem>
                );
              })}
          </List>
          </React.Fragment>
        )}
      </Paper>
    );
  };

  const content = () => {
    if (mode === "add") {
      return (
        <React.Fragment>
          {tabIndex === 0 && generalGroupContent()}
          {tabIndex === 1 && devicesGroupContent()}
          <Stepper activeStep={tabIndex} alternativeLabel>
            {steps.map(label => (
              <Step key={label}>
                <StepLabel>{label}</StepLabel>
              </Step>
            ))}
          </Stepper>
          <Divider />
        </React.Fragment>
      );
    } else if (mode === "update") {
      return (
        <React.Fragment>
          {tabIndex === 0 && generalGroupContent()}
          {tabIndex === 1 && devicesGroupContent()}
        </React.Fragment>
      );
    }

    return <React.Fragment />;
  };

  const actions = () => {
    return (
      <Grid2 container justifyContent="space-between" direction="row" width={"100%"}>
        <Grid2 size={{ xs: 5 }}>
          {mode === "add" ? (
            <Button onClick={backStep} color="primary" disabled={tabIndex === 0}>
              Back
            </Button>
          ) : (
            mode === "update" &&
            canEdit && <DeleteButton onClick={openRemoveGroup}>Delete</DeleteButton>
          )}
        </Grid2>
        <Grid2 size={{ xs: 7 }} container justifyContent="flex-end">
        <CancelSubmit 
          onSubmit={(mode === "add" && tabIndex === steps.length - 1) || mode === "update" ? submit : nextStep}
          submitText={(mode === "add" && tabIndex === steps.length - 1) || mode === "update" ? "Submit" : "Next"}
          onCancel={close} 
          submitDisabled={!isFormValid()}/>
          {/* <Button onClick={close} color="primary">
            Cancel
          </Button>
          {(mode === "add" && tabIndex === steps.length - 1) || mode === "update"
            ? canEdit && (
                <Button onClick={submit} color="primary" disabled={!isFormValid()}>
                  Submit
                </Button>
              )
            : mode === "add" && (
                <Button onClick={nextStep} color="primary">
                  Next
                </Button>
              )} */}
        </Grid2>
      </Grid2>
    );
  };

  const dialogs = () => {
    return (
      <RemoveGroup
        group={group}
        isDialogOpen={isRemoveGroupOpen}
        closeDialogCallback={closeRemoveGroup}
        removeCallback={removeGroupCallback}
      />
    );
  };

  return (
    <ResponsiveDialog
      open={isDialogOpen}
      onClose={close}
      aria-labelledby="group-settings-dialog"
      maxWidth="sm"
      fullWidth>
      <DialogTitle id="group-settings-title">{title()}</DialogTitle>
      {mode === "update" && (
        <Tabs
          value={tabIndex}
          onChange={(_event, value) => {
            changeTab(value);
          }}
          indicatorColor="secondary"
          textColor="inherit"
          variant="fullWidth">
          <Tab label="General" />
          <Tab label="Devices" />
        </Tabs>
      )}
      <Divider />
      <DialogContent>{content()}</DialogContent>
      <DialogActions>{actions()}</DialogActions>
      {dialogs()}
    </ResponsiveDialog>
  );
}
