import {
  Button,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  FormControl,
  FormControlLabel,
  Grid2 as Grid,
  List,
  ListItem,
  MenuItem,
  Switch,
  Tab,
  Tabs,
  TextField,
  Theme,
  Typography
} from "@mui/material";
import DeleteButton from "common/DeleteButton";
import PeriodSelect from "common/time/PeriodSelect";
import ResponsiveDialog from "common/ResponsiveDialog";
import { useDeviceAPI, usePrevious, useSnackbar } from "hooks";
import { Component, Device } from "models";
import { IsExtended, ListDeviceProductDescribers } from "products/DeviceProduct";
import { pond } from "protobuf-ts/pond";
import { useGlobalState } from "providers";
import React, { useEffect, useState } from "react";
import { quack } from "protobuf-ts/quack";
import LinearMutationBuilder from "common/LinearMutationBuilder";
import { makeStyles } from "@mui/styles";
import { useNavigate } from "react-router-dom";
import CancelSubmit from "common/CancelSubmit";

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((theme: Theme) => {
  return ({
    dialogContent: {
      minHeight: "25vh"
    },
    removeDeviceDialog: {
      zIndex: theme.zIndex.modal + 1
    },
    tabSmall: {
      width: "100%",
      boxSizing: "border-box",
      flexShrink: 1,
      minWidth: "48px",
      marginTop: 0,
      paddingTop: 0
    }
  })
});

interface Props {
  device: Device;
  isDialogOpen: boolean;
  closeDialogCallback: Function;
  canEdit: boolean;
  refreshCallback: Function;
  components?: Component[];
}

const deviceFromForm = (device: Device): Device => {
  if (device.settings.upgradeChannel === pond.UpgradeChannel.UPGRADE_CHANNEL_INVALID) {
    device.settings.upgradeChannel = pond.UpgradeChannel.UPGRADE_CHANNEL_STABLE;
  }
  return device;
};

export default function DeviceSettings(props: Props) {
  const classes = useStyles();
  const [{ user, as }] = useGlobalState();
  const navigate = useNavigate();
  const { success, error } = useSnackbar();
  const deviceAPI = useDeviceAPI();
  const { device, isDialogOpen, closeDialogCallback, canEdit, refreshCallback, components } = props;
  const prevDevice = usePrevious(device);
  const [deviceForm, setDeviceForm] = useState<Device>(deviceFromForm(Device.clone(device)));
  const [isRemoveDeviceDialogOpen, setIsRemoveDeviceDialogOpen] = useState<boolean>(false);
  const [sleeps, setSleeps] = useState<boolean>(device.settings.sleepDurationS > 0);
  const [compExtOne, setCompExtOne] = useState("");
  const [compExtTwo, setCompExtTwo] = useState("");
  const [compExtThree, setCompExtThree] = useState("");
  const [currentTab, setCurrentTab] = useState(0);
  const [linearMutations, setLinearMutations] = useState<pond.LinearMutation[]>([]);
  const [componentsByDevice, setComponentsByDevice] = useState<Map<string, Component[]>>(
    new Map<string, Component[]>()
  );
  const [newMutationDialog, setNewMutationDialog] = useState(false);
  const [existingMutation, setExistingMutation] = useState<pond.LinearMutation>();

  useEffect(() => {
    if (prevDevice !== device) {
      setDeviceForm(deviceFromForm(Device.clone(props.device)));
      if (device.settings.extensionComponents[0]) {
        setCompExtOne(device.settings.extensionComponents[0]);
      }
      if (device.settings.extensionComponents[1]) {
        setCompExtTwo(device.settings.extensionComponents[1]);
      }
      if (device.settings.extensionComponents[2]) {
        setCompExtThree(device.settings.extensionComponents[2]);
      }
      if (device.settings.mutations) {
        setLinearMutations(device.settings.mutations);
      }
      if (components) {
        let cbd = new Map<string, Component[]>();
        cbd.set(device.id().toString(), components);
        setComponentsByDevice(cbd);
      }
    }
  }, [device, prevDevice, props.device, components]);

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

  const isSleepDurationValid = (device: Device): boolean => {
    return (
      !sleeps ||
      (sleeps && device.settings.sleepDurationS >= 10 && device.settings.sleepDelayMs >= 1000)
    );
  };

  const minCheckPeriodS = (): number => {
    let defaultPeriod = 60;
    switch (device.settings.platform) {
      case pond.DevicePlatform.DEVICE_PLATFORM_ELECTRON:
        return user.hasFeature("admin") ? defaultPeriod : 300;
      default:
        return defaultPeriod;
    }
  };

  const minCheckPeriodDescription = (): string => {
    let min = minCheckPeriodS() / 60;
    return min.toString() + " minute" + (min !== 1 ? "s" : "");
  };

  const isCheckPeriodValid = (device: Device): boolean => {
    return (
      device.settings.pondCheckPeriodS >= minCheckPeriodS() &&
      device.settings.pondCheckPeriodS <= 3600
    );
  };

  const isFormValid = (): boolean => {
    return isSleepDurationValid(deviceForm) && isCheckPeriodValid(deviceForm);
  };

  const changeName = (event: any) => {
    let updatedForm = Device.clone(deviceForm);
    updatedForm.settings.name = String(event.target.value);
    setDeviceForm(updatedForm);
  };

  const changeDescription = (event: any) => {
    let updatedForm = Device.clone(deviceForm);
    updatedForm.settings.description = String(event.target.value);
    setDeviceForm(updatedForm);
  };

  const changeSleepDuration = (ms: number) => {
    let updatedForm = Device.clone(deviceForm);
    updatedForm.settings.sleepDurationS = ms / 1000;
    setDeviceForm(updatedForm);
  };

  const changeSleepDelay = (ms: number) => {
    let updatedForm = Device.clone(deviceForm);
    updatedForm.settings.sleepDelayMs = ms;
    setDeviceForm(updatedForm);
  };

  const changePondCheckPeriod = (ms: number) => {
    let updatedForm = Device.clone(deviceForm);
    updatedForm.settings.pondCheckPeriodS = ms / 1000;
    setDeviceForm(updatedForm);
  };

  const changeUpgradeChannel = (event: any) => {
    let updatedForm = Device.clone(deviceForm);
    updatedForm.settings.upgradeChannel = event.target.value;
    setDeviceForm(updatedForm);
  };

  const changeProduct = (event: any) => {
    let updatedForm = Device.clone(deviceForm);
    updatedForm.settings.product = event.target.value;
    setDeviceForm(updatedForm);
  };

  const upgradeChannelHelper = (): string => {
    switch (deviceForm.settings.upgradeChannel) {
      case pond.UpgradeChannel.UPGRADE_CHANNEL_STABLE: {
        return "";
      }
      case pond.UpgradeChannel.UPGRADE_CHANNEL_BETA: {
        return "The newest field tested features as they become available";
      }
      case pond.UpgradeChannel.UPGRADE_CHANNEL_ALPHA: {
        return "The newest lab tested features as they become available";
      }
      case pond.UpgradeChannel.UPGRADE_CHANNEL_DEVELOPMENT: {
        return "Features as they're written, expect things to break";
      }
    }
    return "";
  };

  const toggleAutomaticallyUpgrade = (event: any) => {
    let updatedForm = Device.clone(deviceForm);
    updatedForm.settings.automaticallyUpgrade = event.target.checked;
    setDeviceForm(updatedForm);
  };

  const toggleSleeps = (event: any) => {
    let updatedForm = Device.clone(deviceForm);
    let updatedSleeps = event.target.checked;
    updatedSleeps
      ? (updatedForm.settings.sleepType = quack.SleepType.SLEEP_TYPE_NAP)
      : (updatedForm.settings.sleepType = quack.SleepType.SLEEP_TYPE_NONE);
    setSleeps(updatedSleeps);
    setDeviceForm(updatedForm);
  };

  const submitSettings = () => {
    const deviceName = deviceForm.name();
    const deviceID = device.id();
    if (!sleeps) {
      deviceForm.settings.sleepDurationS = 0;
    }
    deviceForm.settings.extensionComponents = [compExtOne, compExtTwo, compExtThree];
    deviceForm.settings.mutations = linearMutations;
    deviceAPI
      .update(deviceID, deviceForm.settings, as)
      .then((_response: any) => {
        success(deviceName + " was successfully updated!");
        close();
        refreshCallback();
      })
      .catch((_err: any) => {
        error("Error occured while configuring " + deviceName);
        close();
      });
  };

  const confirmRemoveDevice = () => {
    setIsRemoveDeviceDialogOpen(true);
  };

  const closeConfirmRemoveDeviceDialog = () => {
    setIsRemoveDeviceDialogOpen(false);
  };

  const removeDevice = () => {
    const deviceName = deviceForm.name();
    deviceAPI
      .remove(device.id(), as)
      .then((_response: any) => {
        success(deviceName + " was successfully deleted!");
        navigate("/");
      })
      .catch((_err: any) => {
        error("Error occured while deleting " + deviceName + ".");
        closeConfirmRemoveDeviceDialog();
        close();
      });
  };

  const canRemove = (): boolean => {
    return user.allowedTo("remove-devices");
  };

  const title = () => {
    return (
      <React.Fragment>
        Device Settings
        <Typography variant="body2" color="textSecondary">
          {deviceForm.name()} - ID: {deviceForm.id()}
        </Typography>
      </React.Fragment>
    );
  };

  const content = () => {
    return (
      <React.Fragment>
        <TextField
          id="name"
          name="name"
          label="Name"
          defaultValue={deviceForm.settings.name}
          onChange={changeName}
          margin="normal"
          variant="outlined"
          type="text"
          fullWidth
          InputLabelProps={{ shrink: true }}
          disabled={!canEdit}
        />
        <TextField
          id="description"
          name="description"
          label="Description"
          defaultValue={deviceForm.settings.description}
          onChange={changeDescription}
          multiline
          rows={2}
          // rowsMax={4}
          type="text"
          margin="normal"
          variant="outlined"
          fullWidth
          InputLabelProps={{ shrink: true }}
          disabled={!canEdit}
        />
        <Grid container direction="row" justifyContent="space-between" alignItems="center" spacing={1}>
          <Grid size={{ xs: 12, sm: 12 }}>
            <PeriodSelect
              id="pondCheckPeriod"
              label="Check-in Period"
              isError={!isCheckPeriodValid(deviceForm)}
              helperText={
                isCheckPeriodValid(deviceForm)
                  ? "How often the device checks for updates"
                  : "Value must be between " + minCheckPeriodDescription() + " and 1 hour"
              }
              initialMs={deviceForm.settings.pondCheckPeriodS * 1000}
              isDisabled={!canEdit}
              onChange={changePondCheckPeriod}
              units={["seconds", "minutes", "hours"]}
            />
          </Grid>
          {user.hasFeature("sleep") && (
            <Grid container direction="row" justifyContent="space-between" alignItems="center" spacing={1}>
              <Grid container size={{ xs: 4, sm: 3 }} justifyContent="center">
                <FormControlLabel
                  control={
                    <Switch
                      checked={sleeps}
                      onChange={toggleSleeps}
                      name="sleeps"
                      aria-label="sleeps"
                      color="secondary"
                    />
                  }
                  label={<Typography variant="caption">Sleeps</Typography>}
                  labelPlacement="top"
                  disabled={!canEdit}
                />
              </Grid>
              <Grid size={{ xs: 8, sm: 9 }}>
                <FormControl fullWidth>
                  <PeriodSelect
                    id="sleepDuration"
                    label="Sleep Duration"
                    isError={!isSleepDurationValid(deviceForm)}
                    helperText={
                      !isSleepDurationValid(deviceForm) ? "Must be at least 10 seconds" : undefined
                    }
                    isDisabled={!canEdit || !sleeps}
                    initialMs={deviceForm.settings.sleepDurationS * 1000}
                    onChange={changeSleepDuration}
                    units={["seconds", "minutes", "hours"]}
                  />
                </FormControl>
                <FormControl fullWidth>
                  <PeriodSelect
                    id="awakeDuration"
                    label="Awake Duration"
                    isError={!isSleepDurationValid(deviceForm)}
                    helperText={
                      !isSleepDurationValid(deviceForm) ? "Must be at least 1 second" : undefined
                    }
                    isDisabled={!canEdit || !sleeps}
                    initialMs={deviceForm.settings.sleepDelayMs}
                    onChange={changeSleepDelay}
                    units={["seconds", "minutes", "hours"]}
                  />
                </FormControl>
              </Grid>
            </Grid>
          )}
          <Grid size={{ xs: 12, sm: 12 }}>
            <TextField
              id="upgradeChannel"
              label="Upgrade Channel"
              select
              value={deviceForm.settings.upgradeChannel}
              onChange={changeUpgradeChannel}
              helperText={upgradeChannelHelper()}
              margin="normal"
              fullWidth
              variant="outlined"
              disabled={!canEdit}>
              <MenuItem value={pond.UpgradeChannel.UPGRADE_CHANNEL_STABLE}>Stable</MenuItem>
              <MenuItem value={pond.UpgradeChannel.UPGRADE_CHANNEL_BETA}>Beta</MenuItem>
              <MenuItem value={pond.UpgradeChannel.UPGRADE_CHANNEL_ALPHA}>Alpha</MenuItem>
              {user.hasFeature("dev-channel") && (
                <MenuItem value={pond.UpgradeChannel.UPGRADE_CHANNEL_DEVELOPMENT}>
                  Development
                </MenuItem>
              )}
              {user.hasFeature("dev-channel") && (
                <MenuItem value={pond.UpgradeChannel.UPGRADE_CHANNEL_RECOVERY}>Recovery</MenuItem>
              )}
            </TextField>
          </Grid>
          {user.hasFeature("admin") && (
            <TextField
              id="deviceProduct"
              label="Device Product"
              select
              value={deviceForm.settings.product}
              onChange={event => changeProduct(event)}
              margin="normal"
              fullWidth
              variant="outlined"
              disabled={!canEdit}>
              {ListDeviceProductDescribers().map(describer => (
                <MenuItem key={describer.product} value={describer.product}>
                  {describer.label}
                </MenuItem>
              ))}
            </TextField>
          )}
          {IsExtended(deviceForm.settings.product) && components && (
            <Grid size={{ xs: 12 }}>
              Select components to display on card
              <TextField
                id="component1"
                label="Component 1"
                select
                value={compExtOne}
                onChange={event => setCompExtOne(event.target.value)}
                margin="normal"
                fullWidth
                variant="outlined"
                disabled={!canEdit}>
                {components.map(comp => (
                  <MenuItem key={comp.key()} value={comp.locationString()}>
                    {comp.name()}
                  </MenuItem>
                ))}
              </TextField>
              <TextField
                id="component1"
                label="Component 2"
                select
                value={compExtTwo}
                onChange={event => setCompExtTwo(event.target.value)}
                margin="normal"
                fullWidth
                variant="outlined"
                disabled={!canEdit}>
                {components.map(comp => (
                  <MenuItem key={comp.key()} value={comp.locationString()}>
                    {comp.name()}
                  </MenuItem>
                ))}
              </TextField>
              <TextField
                id="component1"
                label="Component 3"
                select
                value={compExtThree}
                onChange={event => setCompExtThree(event.target.value)}
                margin="normal"
                fullWidth
                variant="outlined"
                disabled={!canEdit}>
                {components.map(comp => (
                  <MenuItem key={comp.key()} value={comp.locationString()}>
                    {comp.name()}
                  </MenuItem>
                ))}
              </TextField>
            </Grid>
          )}
          {user.hasAdmin() && (
            <Grid size={{ xs: 12, sm: 12 }}>
              <FormControlLabel
                control={
                  <Switch
                    checked={deviceForm.settings.automaticallyUpgrade}
                    onChange={toggleAutomaticallyUpgrade}
                    name="automaticallyUpgrade"
                    aria-label="automaticallyUpgrade"
                    color="secondary"
                  />
                }
                disabled={!canEdit}
                label="Automatically Upgrade"
                labelPlacement="end"
              />
            </Grid>
          )}
        </Grid>
      </React.Fragment>
    );
  };

  const mutationsContent = () => {
    let mutations: JSX.Element[] = [];
    if (device.settings.mutations) {
      device.settings.mutations.forEach((mut, i) => {
        mutations.push(
          <ListItem key={"mutation" + i}>
            <Grid
              container
              direction="row"
              alignContent="center"
              alignItems="center"
              justifyContent="space-between">
              <Grid size={{ xs: 5 }}>
                <Typography>{mut.mutationName}</Typography>
              </Grid>
              <Grid>
                <Button
                  variant="contained"
                  style={{ backgroundColor: "red" }}
                  onClick={() => {
                    let lm = linearMutations;
                    lm.splice(i, 1);
                    setLinearMutations([...lm]);
                  }}>
                  Remove
                </Button>
                <Button
                  style={{ marginLeft: 10 }}
                  variant="contained"
                  color="primary"
                  onClick={() => {
                    setExistingMutation(mut);
                    setNewMutationDialog(true);
                  }}>
                  Edit
                </Button>
              </Grid>
            </Grid>
          </ListItem>
        );
      });
    }
    let mutationList: JSX.Element = <List>{mutations}</List>;
    return (
      <React.Fragment>
        <Button
          variant="contained"
          color="primary"
          onClick={() => {
            setExistingMutation(undefined);
            setNewMutationDialog(true);
          }}>
          Add New Mutation
        </Button>
        <List>{mutationList}</List>
      </React.Fragment>
    );
  };

  const actions = () => {
    return (
      <Grid container justifyContent="space-between" direction="row" width={"100%"}>
        <Grid>
          {canRemove() && <DeleteButton onClick={confirmRemoveDevice}>Delete</DeleteButton>}
        </Grid>
        <Grid container justifyContent="flex-end">
          <CancelSubmit onSubmit={submitSettings} onCancel={close}/>
          {/* <Button onClick={close} color="primary">
            Cancel
          </Button>
          <Button onClick={submitSettings} color="primary" disabled={!isFormValid() || !canEdit}>
            Submit
          </Button> */}
        </Grid>
      </Grid>
    );
  };

  return (
    <React.Fragment>
      <ResponsiveDialog
        maxWidth="sm"
        fullWidth
        open={isDialogOpen}
        onClose={close}
        aria-labelledby="device-config-title">
        <DialogTitle id="device-config-title">{title()}</DialogTitle>
        <DialogContent className={classes.dialogContent}>
          {components ? (
            <React.Fragment>
              <Tabs
                value={currentTab}
                onChange={(_, value) => setCurrentTab(value)}
                indicatorColor="primary"
                textColor="primary"
                variant="fullWidth"
                aria-label="device tabs"
                classes={{ root: classes.tabSmall }}>
                <Tab label={"General"} className={classes.tabSmall} />
                {user.hasFeature("beta") && (
                  <Tab label={"Mutations"} className={classes.tabSmall} />
                )}
              </Tabs>
              <TabPanelMine value={currentTab} index={0}>
                {content()}
              </TabPanelMine>
              <TabPanelMine value={currentTab} index={1}>
                {mutationsContent()}
              </TabPanelMine>
            </React.Fragment>
          ) : (
            content()
          )}
        </DialogContent>
        <DialogActions>{actions()}</DialogActions>
      </ResponsiveDialog>
      <Dialog
        open={isRemoveDeviceDialogOpen}
        onClose={closeConfirmRemoveDeviceDialog}
        aria-labelledby="confirm-remove-device-label"
        aria-describedby="confirm-remove-device-description"
        className={classes.removeDeviceDialog}>
        <DialogTitle id="confirm-remove-device-title">Delete {deviceForm.name()}?</DialogTitle>
        <DialogContent>
          <Typography variant="subtitle1" color="error" align="left">
            WARNING:
          </Typography>
          <Typography variant="subtitle1" color="textSecondary" align="left">
            Clicking 'Accept' will remove the device from all users and groups associated with it.
          </Typography>
        </DialogContent>
        <DialogActions>
          <Button onClick={closeConfirmRemoveDeviceDialog} color="primary">
            Cancel
          </Button>
          <Button onClick={removeDevice} color="primary">
            Accept
          </Button>
        </DialogActions>
      </Dialog>
      <LinearMutationBuilder
        existingMutation={existingMutation}
        componentsByDevice={componentsByDevice}
        open={newMutationDialog}
        onClose={() => {
          setNewMutationDialog(false);
        }}
        onSubmit={newMutation => {
          let lm = linearMutations;
          lm.push(newMutation);
          setLinearMutations(lm);
        }}
      />
    </React.Fragment>
  );
}
