import {
  AppBar,
  Box,
  Button,
  Card,
  CardActionArea,
  Checkbox,
  DialogActions,
  DialogContent,
  DialogTitle,
  FormControlLabel,
  Grid,
  IconButton,
  Typography
} from "@mui/material";
import { ArrowBackIos, ArrowForwardIos } from "@mui/icons-material";
import HumidityIcon from "component/HumidityIcon";
import TemperatureIcon from "component/TemperatureIcon";
import { useMobile, useSnackbar } from "hooks";
import InteractionSettings from "interactions/InteractionSettings";
import InteractionsOverview from "interactions/InteractionsOverview";
import { clone } from "lodash";
import { Component, Device } from "models";
import { GrainCable } from "models/GrainCable";
import { Interaction } from "models/Interaction";
import moment from "moment";
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
import { canWrite } from "pbHelpers/Permission";
import { pond } from "protobuf-ts/pond";
import { quack } from "protobuf-ts/quack";
import { useBinAPI, useComponentAPI, useGlobalState, useInteractionsAPI } from "providers";
import AddIcon from "@mui/icons-material/AddCircle";
import React, { useEffect, useState } from "react";
// import { useHistory } from "react-router";
import { green } from "@mui/material/colors";
import ResponsiveDialog from "common/ResponsiveDialog";
import GraphIcon from "products/CommonIcons/graphIcon";
import { makeStyles } from "@mui/styles";
import { useNavigate } from "react-router-dom";

interface Props {
  open: boolean;
  device: Device;
  cable: GrainCable;
  binKey: string;
  selectedNode: number;
  close: () => void;
  grain: pond.Grain;
  interactionComponents: Component[];
  permissions: pond.Permission[];
  updateComponentCallback: (componentKey: string) => void;
}

const useStyles = makeStyles(() => {
  return ({
    dialog: {
      maxWidth: 350
    },
    appBar: {
      position: "static",
      borderRadius: 30
    },
    readingCard: {
      padding: 10
    },
    greenButton: {
      color: green["600"],
      padding: 0
    }
  });
});

export default function GrainNodeInteractions(props: Props) {
  const {
    open,
    selectedNode,
    device,
    cable,
    close,
    binKey,
    grain,
    interactionComponents,
    permissions,
    updateComponentCallback
  } = props;
  const [nodeTemp, setNodeTemp] = useState<number>();
  const [nodeHum, setNodeHum] = useState<number>();
  const [nodeMoist, setNodeMoist] = useState<number | undefined>();
  const [{ user, as }] = useGlobalState();
  const tempDescriber = describeMeasurement(
    quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE, 
    quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE,
    undefined,
    undefined,
    user);
  const humDescriber = describeMeasurement(
    quack.MeasurementType.MEASUREMENT_TYPE_PERCENT, 
    quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE,
    undefined,
    undefined,
    user);
  const moistureDescriber = describeMeasurement(
    quack.MeasurementType.MEASUREMENT_TYPE_GRAIN_EMC, 
    quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE,
    undefined,
    undefined,
    user);
  const [topNode, setTopNode] = useState(false);
  const [excluded, setExcluded] = useState(false);
  const componentAPI = useComponentAPI();
  const interactionAPI = useInteractionsAPI();
  const binAPI = useBinAPI();
  const [displayNode, setDisplayNode] = useState(0);
  const classes = useStyles();
  const isMobile = useMobile();
  // const history = useHistory();
  const navigate = useNavigate()
  const [cableInteractions, setCableInteractions] = useState<Interaction[]>([]);
  const { openSnack } = useSnackbar();
  const [newInteraction, setNewInteraction] = useState(false);

  useEffect(() => {
    setDisplayNode(selectedNode);
  }, [selectedNode]);

  useEffect(() => {
    interactionAPI.listInteractionsByComponent(device.id(), cable.location(), undefined, as).then(resp => {
      setCableInteractions(resp);
    });
  }, [cable, device, interactionAPI, as]);

  useEffect(() => {
    //do the reversing to make the selected node match the array in the
    let revTemps = clone(cable.temperatures);
    revTemps.reverse();
    setNodeTemp(revTemps[displayNode - 1]);
    let revHums = clone(cable.humidities);
    revHums.reverse();
    setNodeHum(revHums[displayNode - 1]);
    let revMoist = clone(cable.grainMoistures);
    revMoist.reverse();
    setNodeMoist(revMoist[displayNode - 1]);

    //determine whether the node being looked at is the active "top fill" node
    if (displayNode === cable.settings.grainFilledTo) {
      setTopNode(true);
    } else {
      setTopNode(false);
    }

    //determine if the node is excluded
    if (cable.excludedNodes.includes(displayNode - 1)){
      setExcluded(true)
    }else{
      setExcluded(false)
    }
  }, [cable, displayNode]);

  const goToComponent = () => {
    navigate("/devices/" + device.id() + "/components/" + cable.key());
  };

  const closeDialog = () => {
    setDisplayNode(selectedNode);
    close();
  };

  const setEMC = () => {
    let settings = cable.settings;
    settings.defaultMutations = [pond.Mutator.MUTATOR_EMC];
    settings.grainType = grain;
    componentAPI
      .update(device.id(), settings, [binKey], ["bin"], as)
      .then(resp => {
        openSnack("EMC set on cable " + cable.name());
        updateComponentCallback(cable.key());
      })
      .catch(err => {});
  };

  const submit = () => {
    componentAPI
      .update(device.id(), cable.settings, [binKey], ["bin"], as)
      .then(resp => {
        //after updating the component update the interactions with the new subtypes
        //TODO-CS: make function to update multiple interactions at once to avoid this loop if it becomes a problem
        cableInteractions.forEach(interaction => {
          interactionAPI
            .updateInteraction(device.id(), interaction.settings, as)
            .then(resp => {})
            .catch(err => {});
        });
        updateComponentCallback(cable.key())
      })
      .catch(err => {
        openSnack("Failed to update component");
      });
    closeDialog();
  };

  const updateTopNode = (checked: boolean) => {
    if (topNode && !checked) {
      cable.settings.grainFilledTo = 0;
      cableInteractions.forEach(interaction => {
        interaction.settings.subtype = 0;
        interaction.settings.nodeOne = 0;
        interaction.settings.nodeTwo = 0;
      });
    } else if (checked) {
      cable.settings.grainFilledTo = displayNode;
      cableInteractions.forEach(interaction => {
        interaction.settings.subtype = Interaction.upToSubtype(displayNode);
        interaction.settings.nodeOne = displayNode;
      });
    }
    let pref = pond.BinComponentPreferences.create({
      type: pond.BinComponent.BIN_COMPONENT_GRAIN_CABLE,
      node: cable.settings.grainFilledTo
    });
    //update the bins preferences to have the new top node for the cable
    binAPI
      .updateComponentPreferences(binKey, cable.key(), pref, as)
      .then(resp => {
        openSnack("Updated cables top node");
        updateComponentCallback(cable.key());
      })
      .catch(err => {});
    setTopNode(checked);
  };

  const updateNodeExclusion = (checked: boolean) => {
    if (checked) {
      cable.settings.excludedNodes.push(selectedNode-1)
    } else {
      cable.settings.excludedNodes.splice(cable.settings.excludedNodes.indexOf(selectedNode - 1), 1)
    }
    setExcluded(checked)
  }

  const displayTemp = () => {
    let tempFinal = nodeTemp ?? 0;
    if (user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
      tempFinal = tempFinal * 1.8 + 32;
    }
    return tempFinal.toFixed(2);
  };

  const displayNext = () => {
    let i = displayNode;
    if (i === cable.temperatures.length) {
      i = 1;
    } else {
      i = i + 1;
    }
    setDisplayNode(i);
  };

  const displayPrev = () => {
    let i = displayNode;
    if (i === 1) {
      i = cable.temperatures.length;
    } else {
      i = i - 1;
    }
    setDisplayNode(i);
  };

  const latestReading = () => {
    return (
      <Box>
        <Typography style={{ fontSize: 20, fontWeight: 650 }}>Latest Reading</Typography>
        <Typography style={{ fontSize: 10 }}> {moment(cable.lastReading).fromNow()}</Typography>
        <Grid container direction="row" spacing={2} style={{ padding: 10 }}>
          <Grid item xs={6}>
            <Card className={classes.readingCard}>
              <Box display="flex" justifyContent="center" alignItems="center">
                <TemperatureIcon />
                <Typography style={{ color: tempDescriber.colour(), fontWeight: 650 }}>
                  {displayTemp() + tempDescriber.GetUnit()}
                </Typography>
              </Box>
              <Typography style={{ textAlign: "center", color: "grey", fontWeight: 650 }}>
                Temperature
              </Typography>
            </Card>
          </Grid>
          <Grid item xs={6}>
            <Card className={classes.readingCard}>
              <Box display="flex" justifyContent="center" alignItems="center">
                <HumidityIcon />
                <Typography style={{ color: humDescriber.colour(), fontWeight: 650 }}>
                  {nodeHum + humDescriber.GetUnit()}
                </Typography>
              </Box>
              <Typography style={{ textAlign: "center", color: "grey", fontWeight: 650 }}>
                Humidity
              </Typography>
            </Card>
          </Grid>
          <Grid item xs={6}>
            {/* only show the emc if the bin grain type and the cable grain type match */}
            {nodeMoist && cable.settings.grainType === grain ? (
              <Card className={classes.readingCard}>
                <Box display="flex" justifyContent="center" alignItems="center">
                  <HumidityIcon />
                  <Typography style={{ color: moistureDescriber.colour(), fontWeight: 650 }}>
                    {nodeMoist + moistureDescriber.GetUnit()}
                  </Typography>
                </Box>
                <Typography style={{ textAlign: "center", color: "grey", fontWeight: 650 }}>
                  Grain EMC
                </Typography>
              </Card>
            ) : (
              <Card>
                <CardActionArea
                  className={classes.readingCard}
                  onClick={() => {
                    setEMC();
                  }}>
                  <Box display="flex" justifyContent="center" alignItems="center">
                    <HumidityIcon />
                  </Box>
                  <Typography style={{ textAlign: "center", color: "grey", fontWeight: 650 }}>
                    Set EMC
                  </Typography>
                </CardActionArea>
              </Card>
            )}
          </Grid>
          <Grid item xs={6}>
            <Card>
              <CardActionArea className={classes.readingCard} onClick={() => goToComponent()}>
                <Box display="flex" justifyContent="center" alignItems="center">
                  <GraphIcon />
                </Box>
                <Typography style={{ textAlign: "center", color: "grey", fontWeight: 650 }}>
                  See Graphs
                </Typography>
              </CardActionArea>
            </Card>
          </Grid>
        </Grid>
      </Box>
    );
  };

  const nodeControl = () => {
    return (
      <Box style={{ marginTop: 10 }}>
        <Typography style={{ fontSize: 20, fontWeight: 650 }}>Node Control</Typography>
          <FormControlLabel
          style={{ marginTop: 10 }}
          control={
            <Checkbox
              checked={excluded}
              onChange={e => {
                updateNodeExclusion(e.target.checked);
              }}
              name="excludedNode"
            />
          }
          label={
            <React.Fragment>
              <Typography style={{ fontSize: 15, fontWeight: 650 }}>
                Exclude Node
              </Typography>
              <Typography style={{ fontSize: 15 }}>
                If checked this will set the node to be disabled and will not be used for any calculations done for the bin
              </Typography>
            </React.Fragment>
          }
        />
        <FormControlLabel
          style={{ marginTop: 10 }}
          control={
            <Checkbox
              checked={topNode}
              disabled={excluded}
              onChange={e => {
                updateTopNode(e.target.checked);
              }}
              name="topNode"
            />
          }
          label={
            <React.Fragment>
              <Typography style={{ fontSize: 15, fontWeight: 650 }}>
                Set as top node in grain
              </Typography>
              <Typography style={{ fontSize: 15 }}>
                If checked this will set interactions to ignore all nodes above it. Interactions can
                still be adjusted manually to use all nodes
              </Typography>
            </React.Fragment>
          }
        />
      </Box>
    );
  };

  const interactionDisplay = () => {
    let component = Component.create();
    component.settings = cable.settings;
    component.status = cable.status;
    return (
      <Box marginTop={2}>
        <Typography style={{ fontSize: 20, fontWeight: 650 }}>Interactions</Typography>
        <Box marginTop={1} marginBottom={1}>
          <InteractionsOverview
            component={component}
            components={interactionComponents}
            device={device}
            interactions={cableInteractions}
            permissions={permissions}
            refreshCallback={() => {
              interactionAPI
                .listInteractionsByComponent(device.id(), cable.location(), undefined, as)
                .then(resp => {
                  setCableInteractions(resp);
                });
            }}
          />
        </Box>
        <Box display="flex" justifyContent="center">
          <IconButton
            color="primary"
            disabled={!canWrite(permissions)}
            aria-label="Add Interaction"
            onClick={() => {
              setNewInteraction(true);
            }}
            className={classes.greenButton}>
            <AddIcon />
          </IconButton>
        </Box>
      </Box>
    );
  };

  return (
    <React.Fragment>
      <ResponsiveDialog open={open} onClose={() => closeDialog()} fullWidth={isMobile}>
        <DialogTitle style={{ padding: 10 }}>
          <Box display="flex" justifyContent="center" marginBottom={2}>
            <Typography style={{ fontSize: 30, fontWeight: 650 }}>{cable.name()}</Typography>
          </Box>
          <AppBar className={classes.appBar} color="secondary">
            <Grid
              container
              direction="row"
              justifyContent="space-between"
              alignItems="center"
              wrap="nowrap">
              <Grid item>
                <Button onClick={() => displayPrev()}>
                  <ArrowBackIos style={{ color: "black" }} />
                </Button>
              </Grid>
              <Grid item>
                <Box>
                  <Typography align="center" variant="h5">
                    Node {displayNode}
                  </Typography>
                </Box>
              </Grid>
              <Grid item>
                <Button onClick={() => displayNext()}>
                  <ArrowForwardIos style={{ color: "black" }} />
                </Button>
              </Grid>
            </Grid>
          </AppBar>
        </DialogTitle>
        <DialogContent>
          {latestReading()}
          {nodeControl()}
          {interactionDisplay()}
        </DialogContent>
        <DialogActions>
          <Button onClick={closeDialog} color="primary">
            Cancel
          </Button>
          <Button onClick={submit} color="primary">
            Submit
          </Button>
        </DialogActions>
      </ResponsiveDialog>
      <InteractionSettings
        isDialogOpen={newInteraction}
        initialComponent={cable.asComponent()}
        mode="add"
        device={device}
        components={interactionComponents}
        closeDialogCallback={() => {
          setNewInteraction(false);
        }}
        refreshCallback={() => {
          interactionAPI.listInteractionsByComponent(device.id(), cable.location(), undefined, as).then(resp => {
            setCableInteractions(resp);
          });
        }}
        canEdit={canWrite(permissions)}
      />
    </React.Fragment>
  );
}
