import {
  Box,
  Button,
  Card,
  darken,
  Grid2 as Grid,
  InputAdornment,
  lighten,
  Slider,
  TextField,
  Theme,
  Tooltip,
  Typography
} from "@mui/material";
import { CheckCircleOutline, ImportExport } from "@mui/icons-material";
import Warning from "@mui/icons-material/Warning";
import HumidityIcon from "component/HumidityIcon";
import TemperatureIcon from "component/TemperatureIcon";
import { cloneDeep } from "lodash";
import { Bin, Component } from "models";
import { GrainCable } from "models/GrainCable";
import { UnitMeasurement } from "models/UnitMeasurement";
import Co2Icon from "products/CommonIcons/co2Icon";
import { pond } from "protobuf-ts/pond";
import { useBinAPI, useGlobalState, useSnackbar } from "providers";
import React, { useEffect, useState } from "react";
import { avg, getTemperatureUnit } from "utils";
import { makeStyles } from "@mui/styles";
import { Mark } from "@mui/material/Slider/useSlider.types";
import { CO2 } from "models/CO2";

const useStyles = makeStyles((theme: Theme) => {
  return ({
    card: {
      padding: 10,
      paddingLeft: 15,
      paddingRight: 15
    },
    displayBG: {
      marginTop: 10,
      background: darken(theme.palette.background.default, 0.05),
      borderRadius: 5,
      padding: 10
    },
    slider: {
      zIndex: 1,
      "&:hover": {
        zIndex: 3
      }
    },
    markContainer: {
      zIndex: 2
    },
    arrowDown: {
      width: 0,
      height: 0,
      borderLeft: "10px solid transparent",
      borderRight: "10px solid transparent",
      borderTop: "10px solid yellow"
    },
    co2Box: {
      background: lighten(theme.palette.background.default, 0.2),
      borderRadius: 10,
      padding: 10,
      margin: 10,
      marginLeft: 0,
      marginRight: 0
    },
    low: {
      fontWeight: 650,
      color: "#0575E6"
    },
    target: {
      fontWeight: 650,
      color: "green"
    },
    high: {
      fontWeight: 650,
      color: "#c42605"
    },
    sliderRoot: {},
    sliderThumb: {
      height: 15,
      width: 15,
      marginTop: 0,
      marginLeft: 0,
      backgroundColor: "yellow"
    },
    sliderTrack: {
      height: 3,
      backgroundColor: "white"
    },
    sliderRail: {
      height: 3,
      backgroundColor: "white"
    },
    sliderValLabel: {
      left: -20,
      top: 40,
      background: "transparent",
      "& *": {
        color: "#fff"
      }
    },
    sliderMark: {
      visibility: "hidden"
    },
    sliderMarked: {
      marginTop: 25,
      marginBottom: 0
    },
    sliderMarkLabel: {
      top: -25,
      left: -10
    }
  });
});

interface Props {
  bin: Bin;
  headspaceCO2: CO2[];
  cables: GrainCable[];
}

interface NodeCounts {
  below: number;
  onTarget: number;
  above: number;
  total: number;
}

export default function BinStorageConditions(props: Props) {
  const classes = useStyles();
  const binAPI = useBinAPI();
  const { openSnack } = useSnackbar();
  const { bin, headspaceCO2, cables } = props;
  const [{as}] = useGlobalState()
  const [sliderTemps, setSliderTemps] = useState<number[]>([-40, 40]);
  //boolean that if a cable is missing its filled to display an icon or something to let the user know not all of the cables have their fill set
  const [missingTopNodeWarning, setMissingTopNodeWarning] = useState(false);
  const [targetTemp, setTargetTemp] = useState("");
  const [targetEMC, setTargetEMC] = useState("");
  const [emcDeviation, setEmcDeviation] = useState("");
  const [averageTemp, setAverageTemp] = useState<number | undefined>(10);
  const [averageEMC, setAverageEMC] = useState<number | undefined>(10);

  //the number of nodes in the grain that fall into each category
  const [tempTargets, setTempTargets] = useState<NodeCounts>({
    below: 0,
    onTarget: 0,
    above: 0,
    total: 0
  });
  const [emcTargets, setEmcTargets] = useState<NodeCounts>({
    below: 0,
    onTarget: 0,
    above: 0,
    total: 0
  });

  useEffect(() => {
    setSliderTemps([bin.settings.lowTemp, bin.settings.highTemp]);
    let cableTempAvgs: number[] = [];
    let tempCounts: NodeCounts = {
      below: 0,
      onTarget: 0,
      above: 0,
      total: 0
    };
    let emcCounts: NodeCounts = {
      below: 0,
      onTarget: 0,
      above: 0,
      total: 0
    };
    let cableEMCAvgs: number[] = [];
    cables.forEach(cable => {
      //if a cable is missing the top node being set
      if (cable.topNode === 0) {
        setMissingTopNodeWarning(true);
      } else {
        //with the way the measurements are stored the node closest to the device (highest on the cable) is the first position of the array
        //so we will need to flip the array so that node 1 is the beginning of the array
        //let temps = cloneDeep(cable.temperatures).reverse();
        //let emcs = cloneDeep(cable.grainMoistures).reverse();
        let filteredNodes = cable.filteredNodes(true)
        //only use the nodes up to the top node
        let nodeTemps: number[] = [];
        filteredNodes.temps.forEach((temp, i) => {
          // if (i < cable.topNode) { //no longer need to check if it is below the top node as the filteredNodes function does it already and only returns active nodes
            nodeTemps.push(temp);
            tempCounts.total++;
            if (temp > bin.settings.highTemp) {
              tempCounts.above++;
            } else if (temp < bin.settings.lowTemp) {
              tempCounts.below++;
            } else {
              tempCounts.onTarget++;
            }
          // }
        });

        let nodeEMCs: number[] = [];

        filteredNodes.moistures.forEach((emc, i) => {
          if (bin.settings.inventory?.targetMoisture) {
            // if (i < cable.topNode) {
              nodeEMCs.push(emc);
              emcCounts.total++;
              if (
                emc >
                bin.settings.inventory.targetMoisture +
                  bin.settings.inventory.moistureTargetDeviation
              ) {
                emcCounts.above++;
              } else if (
                emc <
                bin.settings.inventory.targetMoisture -
                  bin.settings.inventory.moistureTargetDeviation
              ) {
                emcCounts.below++;
              } else {
                emcCounts.onTarget++;
              }
            // }
          }
        });
        cableTempAvgs.push(avg(nodeTemps));
        cableEMCAvgs.push(avg(nodeEMCs));
      }
    });
    if (cableTempAvgs.length > 0) {
      setAverageTemp(avg(cableTempAvgs));
    }
    setTempTargets(tempCounts);
    let tempVal = bin.settings.inventory?.targetTemperature ?? 0;
    if (getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
      tempVal = tempVal * 1.8 + 32;
    }
    setTargetTemp(tempVal.toFixed(1));

    if (cableEMCAvgs.length > 0) {
      setAverageEMC(avg(cableEMCAvgs));
    }
    setEmcTargets(emcCounts);
    setTargetEMC(bin.settings.inventory?.targetMoisture.toFixed(1) ?? "");
    setEmcDeviation(bin.settings.inventory?.moistureTargetDeviation.toFixed(1) ?? "");
  }, [bin, cables]);

  //useEffect that watches for changes in the target emc and deviation to update the targets
  useEffect(() => {
    let target = parseFloat(targetEMC);
    let deviation = parseFloat(emcDeviation);
    if (!isNaN(target) && !isNaN(deviation)) {
      let emcCounts: NodeCounts = {
        below: 0,
        onTarget: 0,
        above: 0,
        total: 0
      };

      cables.forEach(cable => {
        let emcs = cable.filteredNodes(true).moistures
        //let nodeEMCs: number[] = [];

        emcs.forEach((emc, i) => {
          if (bin.settings.inventory?.targetMoisture) {
            if (i < cable.topNode) {
              //nodeEMCs.push(emc);
              emcCounts.total++;
              if (emc > target + deviation) {
                emcCounts.above++;
              } else if (emc < target - deviation) {
                emcCounts.below++;
              } else {
                emcCounts.onTarget++;
              }
            }
          }
        });
      });
      setEmcTargets(emcCounts);
    }
  }, [targetEMC, emcDeviation, bin, cables]);

  const isErrors = () => {
    let errors = false;
    if (
      isNaN(parseFloat(targetTemp)) ||
      isNaN(parseFloat(targetEMC)) ||
      isNaN(parseFloat(emcDeviation))
    ) {
      errors = true;
    }
    return errors;
  };

  const updateBinThresholds = () => {
    //update the bin settings with the new thresholds
    let settings = bin.settings;
    settings.lowTemp = sliderTemps[0];
    settings.highTemp = sliderTemps[1];
    if (settings.inventory) {
      let tempVal = parseFloat(targetTemp);
      if (getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
        tempVal = Math.fround(((tempVal - 32) * 5) / 9);
      }
      settings.inventory.targetTemperature = tempVal;
      settings.inventory.targetMoisture = parseFloat(targetEMC);
      settings.inventory.moistureTargetDeviation = parseFloat(emcDeviation);
    }

    binAPI
      .updateBin(bin.key(), settings, as)
      .then(_resp => {
        openSnack("Updated bin thresholds");
      })
      .catch(_err => {
        openSnack("Failed to update bin thresholds");
      });
  };

  const updateSliderTemps = (newVals: number[]) => {
    //the slider always returns the array with the lower value first
    setSliderTemps(newVals);

    //re-calc the node counts
    let tempCounts: NodeCounts = {
      below: 0,
      onTarget: 0,
      above: 0,
      total: 0
    };
    cables.forEach(cable => {
      let temps = cable.filteredNodes(true).temps
      let nodeTemps: number[] = [];
      temps.forEach((temp, i) => {
        if (i < cable.topNode) {
          nodeTemps.push(temp);
          tempCounts.total++;
          if (temp > newVals[1]) {
            tempCounts.above++;
          } else if (temp < newVals[0]) {
            tempCounts.below++;
          } else {
            tempCounts.onTarget++;
          }
        }
      });
    });
    setTempTargets(tempCounts);
  };

  const customMark = (val: string, sub: string) => {
    return (
      <Box className={classes.markContainer}>
        <Grid container direction="column" alignContent="center" alignItems="center">
          <Grid>
            <Typography style={{ fontSize: 15, fontWeight: 650, lineHeight: 1 }}>{val}</Typography>
          </Grid>
          <Grid>
            <Typography style={{ fontSize: 8 }}>{sub}</Typography>
          </Grid>
          <Grid>
            <Box className={classes.arrowDown} />
          </Grid>
        </Grid>
      </Box>
    );
  };

  //this will display at all times
  const tempDisplay = () => {
    // console.log(tempTargets)
    let sliderEdge = 40;

    let mark: Mark[] = [];
    if (averageTemp) {
      let temp = averageTemp;
      if (getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
        temp = temp * 1.8 + 32;
      }
      mark = [
        {
          label: customMark(
            temp.toFixed(1) +
              (getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT
                ? "°F"
                : "°C"),
            "AVG"
          ),
          value: averageTemp
        }
      ];
    }

    return (
      <Box className={classes.displayBG}>
        <Grid
          container
          direction="row"
          alignContent="center"
          alignItems="center"
          justifyContent="space-between">
          <Grid size={{ xs: 8 }}>
            <Box display="flex" alignContent="center" alignItems="center">
              <TemperatureIcon />
              <Typography noWrap style={{ fontWeight: 650 }}>
                Target Temp =
              </Typography>
            </Box>
          </Grid>
          <Grid size={{ xs: 4 }}>
            <TextField
              variant="outlined"
              margin="dense"
              value={targetTemp}
              error={isNaN(parseFloat(targetTemp))}
              helperText={isNaN(parseFloat(targetTemp)) && "Must be a valid number"}
              onChange={e => {
                setTargetTemp(e.target.value);
              }}
              InputProps={{
                endAdornment: (
                  <InputAdornment position="end">
                    {getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT
                      ? "°F"
                      : "°C"}
                  </InputAdornment>
                )
              }}
            />
          </Grid>
        </Grid>
        <Slider
          className={classes.slider}
          classes={{
            root: classes.sliderRoot,
            rail: classes.sliderRail,
            track: classes.sliderTrack,
            thumb: classes.sliderThumb,
            valueLabel: classes.sliderValLabel,
            marked: classes.sliderMarked,
            mark: classes.sliderMark,
            markLabel: classes.sliderMarkLabel
          }}
          
          marks={mark}
          step={0.1}
          value={sliderTemps}
          min={-sliderEdge}
          max={sliderEdge}
          valueLabelDisplay="on"
          valueLabelFormat={value => {
            if (getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
              return ((value * 9) / 5 + 32).toFixed(1) + "°F";
            }
            return value.toFixed(1) + "°C";
          }}
          onChange={(_, newVals) => {
            updateSliderTemps(newVals as number[]);
          }}
        />
        {tempTargets.total > 0 && (
          <Box style={{ marginLeft: 10, marginRight: 10, marginTop: 5 }}>
            <Grid container direction="row" justifyContent="space-between">
              <Grid>
                <Typography className={classes.low} align="center">
                  {((tempTargets.below / tempTargets.total) * 100).toFixed(2)}%
                </Typography>
                <Typography align="center">Below</Typography>
              </Grid>
              <Grid>
                <Typography className={classes.target} align="center">
                  {((tempTargets.onTarget / tempTargets.total) * 100).toFixed(2)}%
                </Typography>
                <Typography align="center">On Target</Typography>
              </Grid>
              <Grid>
                <Typography className={classes.high} align="center">
                  {((tempTargets.above / tempTargets.total) * 100).toFixed(2)}%
                </Typography>
                <Typography align="center">Above</Typography>
              </Grid>
            </Grid>
          </Box>
        )}
      </Box>
    );
  };

  //this will only display if the bin has the moisture target set
  const moistureDisplay = () => {
    const targetVal = parseFloat(targetEMC);
    const devVal = parseFloat(emcDeviation);
    let low = targetVal - devVal;
    let high = targetVal + devVal;
    const min = 0;
    const max = 25;
    if (isNaN(low) || isNaN(high)) {
      low = 0;
      high = 0;
    }

    let mark: Mark[] = [];
    if (averageEMC) {
      mark = [
        {
          label: customMark(averageEMC.toFixed(1) + "%", "AVG"),
          value: averageEMC
        }
      ];
    }
    return (
      <Box className={classes.displayBG}>
        <Grid
          container
          direction="row"
          alignContent="center"
          alignItems="center"
          justifyContent="space-between">
          <Grid size={{ xs: 6 }}>
            <Box display="flex" alignContent="center" alignItems="center">
              <Box style={{ width: 24, paddingLeft: 3 }}>
                <HumidityIcon height={24} width={15} />
              </Box>
              <Typography noWrap style={{ fontWeight: 650 }}>
                Target EMC =
              </Typography>
            </Box>
          </Grid>
          <Grid size={{ xs: 4 }}>
            <TextField
              variant="outlined"
              margin="dense"
              value={targetEMC}
              error={isNaN(parseFloat(targetEMC))}
              helperText={isNaN(parseFloat(targetEMC)) && "Must be a valid number"}
              onChange={e => {
                setTargetEMC(e.target.value);
              }}
              InputProps={{
                endAdornment: <InputAdornment position="end">%</InputAdornment>
              }}
            />
          </Grid>
        </Grid>
        <Grid
          container
          direction="row"
          alignContent="center"
          alignItems="center"
          justifyContent="space-between">
          <Grid size={{ xs: 6 }}>
            <Box display="flex" alignContent="center" alignItems="center">
              <ImportExport />
              <Typography noWrap style={{ fontWeight: 650 }}>
                Deviation =
              </Typography>
            </Box>
          </Grid>
          <Grid size={{ xs: 4 }}>
            <TextField
              variant="outlined"
              margin="dense"
              value={emcDeviation}
              error={isNaN(parseFloat(emcDeviation))}
              helperText={isNaN(parseFloat(emcDeviation)) && "Must be a valid number"}
              onChange={e => {
                setEmcDeviation(e.target.value);
              }}
              InputProps={{
                endAdornment: <InputAdornment position="end">%</InputAdornment>
              }}
            />
          </Grid>
        </Grid>
        <Slider
          style={{ cursor: "default" }}
          marks={mark}
          classes={{
            root: classes.sliderRoot,
            rail: classes.sliderRail,
            track: classes.sliderTrack,
            thumb: classes.sliderThumb,
            valueLabel: classes.sliderValLabel,
            marked: classes.sliderMarked,
            mark: classes.sliderMark,
            markLabel: classes.sliderMarkLabel
          }}
          valueLabelDisplay="on"
          valueLabelFormat={value => {
            return value.toFixed(1) + "%";
          }}
          step={0.5}
          min={min}
          max={max}
          value={[low, high]}
        />
        {emcTargets.total > 0 && (
          <Box style={{ marginLeft: 10, marginRight: 10, marginTop: 5 }}>
            <Grid container direction="row" justifyContent="space-between">
              <Grid>
                <Typography className={classes.low} align="center">
                  {((emcTargets.below / emcTargets.total) * 100).toFixed(2)}%
                </Typography>
                <Typography align="center">Below</Typography>
              </Grid>
              <Grid>
                <Typography className={classes.target} align="center">
                  {((emcTargets.onTarget / emcTargets.total) * 100).toFixed(2)}%
                </Typography>
                <Typography align="center">On Target</Typography>
              </Grid>
              <Grid>
                <Typography className={classes.high} align="center">
                  {((emcTargets.above / emcTargets.total) * 100).toFixed(2)}%
                </Typography>
                <Typography align="center">Above</Typography>
              </Grid>
            </Grid>
          </Box>
        )}
      </Box>
    );
  };

  //this will only appear when there is a CO2 sensor
  const co2Display = () => {
    if (headspaceCO2.length > 0) {
      return (
        <React.Fragment>
          <Box className={classes.displayBG}>
            <Box display="flex">
              <Co2Icon size={24} />
              <Typography style={{ fontWeight: 650, marginLeft: 5 }}>
                CO2 Spoilage Detection
              </Typography>
            </Box>
            {headspaceCO2.map(co2 => {
              let measurement = UnitMeasurement.any(co2.status.lastGoodMeasurement[0] ?? UnitMeasurement.create());
              let reading: number = 0;
              if (measurement.values[0] && measurement.values[0].values[0]) {
                reading = measurement.values[0].values[0];
              }
              let mark: Mark[] = [];
              if (reading > 0) {
                mark = [
                  {
                    label: customMark(reading + "ppm", "last"),
                    value: reading
                  }
                ];
              }
              return (
                <Grid
                  key={co2.key()}
                  container
                  direction="row"
                  alignContent="center"
                  alignItems="center">
                  <Grid size={{ xs: 4 }}>
                    <Box className={classes.co2Box}>
                      <Grid container direction="column" alignContent="center" alignItems="center">
                        <Grid >
                          <Typography style={{ fontSize: 15, textAlign: "center" }}>
                            Current CO2
                          </Typography>
                        </Grid>
                        <Grid >
                          <Typography
                            style={{
                              fontSize: 15,
                              fontWeight: 650,
                              textAlign: "center",
                              color: measurement.colour
                            }}>
                            {reading} ppm
                          </Typography>
                        </Grid>
                        <Grid >
                          <CheckCircleOutline style={{ color: reading < 1100 ? "green" : "red" }} />
                        </Grid>
                      </Grid>
                    </Box>
                  </Grid>
                  <Grid size={{ xs: 8 }}>
                    <Box marginLeft={2}>
                      <Box>
                        <Slider
                          style={{ cursor: "default" }}
                          min={200}
                          max={1300}
                          value={[400, 1100]}
                          marks={mark}
                          classes={{
                            root: classes.sliderRoot,
                            rail: classes.sliderRail,
                            track: classes.sliderTrack,
                            thumb: classes.sliderThumb,
                            valueLabel: classes.sliderValLabel,
                            marked: classes.sliderMarked,
                            mark: classes.sliderMark,
                            markLabel: classes.sliderMarkLabel
                          }}
                          valueLabelDisplay="on"
                          valueLabelFormat={value => {
                            return value + "ppm";
                          }}
                        />
                      </Box>
                    </Box>
                  </Grid>
                </Grid>
              );
            })}
          </Box>
        </React.Fragment>
      );
    }
    return;
  };

  //made up of the co2, moisture and temp displays
  const storageConditions = () => {
    return (
      <Box>
        <Box display="flex" justifyContent={"space-between"}>
          <Typography style={{ fontWeight: 650 }}>Storage Conditions</Typography>
          {missingTopNodeWarning && (
            <Tooltip
              title={`
              One or more cables do not have their fill level set. 
              Check to make sure all top nodes are set for your bin cables
            `}>
              <Warning />
            </Tooltip>
          )}
        </Box>
        {tempDisplay()}
        {moistureDisplay()}
        {co2Display()}
        <Box display="flex" marginTop={1} justifyContent="center">
          <Button
            variant="contained"
            color="primary"
            disabled={isErrors()}
            onClick={() => {
              updateBinThresholds();
            }}>
            Update Thresholds
          </Button>
        </Box>
      </Box>
    );
  };

  return (
    <Card raised className={classes.card}>
      {storageConditions()}
    </Card>
  );
}
