import {
  Box,
  Card,
  darken,
  Grid2 as Grid,
  IconButton,
  Theme,
  Typography,
} from "@mui/material";
import HumidityIcon from "component/HumidityIcon";
import TemperatureIcon from "component/TemperatureIcon";
import GrainDescriber from "grain/GrainDescriber";
import { cloneDeep } from "lodash";
import { Bin } from "models";
import { GrainCable } from "models/GrainCable";
import moment from "moment";
import { pond } from "protobuf-ts/pond";
import { useGlobalState } from "providers";
import React, { useEffect, useState } from "react";
import { celsiusToFahrenheit, getGrainUnit, or } from "utils";
//import { useHistory } from "react-router";
//import BinModeDot from "./BinModeDot";
import BinSVGV2 from "./BinSVGV2";
import { avg } from "utils";
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
import { quack } from "protobuf-ts/quack";
import AerationFanIcon from "products/AgIcons/AerationFanIcon";
import ObjectHeaterIcon from "products/Construction/ObjectHeaterIcon";
import { CheckCircle, Warning } from "@mui/icons-material";
import { makeStyles } from "@mui/styles";

const useStyles = makeStyles((theme: Theme) => {
  return ({
    displayBox: {
      background: darken(theme.palette.background.default, 0.05),
      borderRadius: 5,
      marginRight: -30, //negative margin to extend the box behind the bin svg
      paddingRight: 30, //padding to offset the text and ignore the extension
      marginTop: 5,
      cursor: "pointer",
    }
  });
});

interface Props {
  bin: Bin;
  duplicateBin: (bin: Bin) => void;
  dupHovered: (hovered: boolean) => void;
  valDisplay?: "high" | "low" | "average";
}

interface GrainDetails {
  temp: number;
  humid: number;
  emc: number | undefined;
}

export default function BinCard(props: Props) {
  const { bin, valDisplay } = props;
  const [cables, setCables] = useState<GrainCable[]>();
  //const [modeDetails, setModeDetails] = useState("");
  const [lidarPercentage, setLidarPercentage] = useState<number | undefined>();
  //const [lidarBushels, setLidarBushels] = useState<number | undefined>();
  const [{ user }] = useGlobalState();
  const classes = useStyles();
  const [average, setAverage] = useState<GrainDetails>();
  const [coldNode, setColdNode] = useState<GrainDetails>();
  const [hotNode, setHotNode] = useState<GrainDetails>();
  const [mostMissed, setMostMissed] = useState(0);
  const warningThreshold = 6;
  const tempColour = describeMeasurement(
    quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE
  ).colour();
  const humColour = describeMeasurement(quack.MeasurementType.MEASUREMENT_TYPE_PERCENT).colour();
  const emcColour = describeMeasurement(quack.MeasurementType.MEASUREMENT_TYPE_GRAIN_EMC).colour();

  useEffect(() => {
    let newGrainCables: GrainCable[] = [];
    if (bin.status.grainCables === undefined) return;
    let mostMissedReadings = 0;
    let allTemps: number[] = [];
    let allHums: number[] = [];
    let allMoistures: number[] = [];
    let coldestNode: GrainDetails | undefined;
    let hottestNode: GrainDetails | undefined;
    let now = moment();
    //loop through the cables in the bin status to prep for display
    bin.status.grainCables.forEach(cable => {
      let c: GrainCable = new GrainCable();
      //flip the cables so that for display node 1 is at the bottom, 
      let cableTemps = cloneDeep(cable.celcius);
      let cableHums = cloneDeep(cable.relativeHumidity);
      let cableEMC = cloneDeep(cable.moisture);

      c.temperatures = cableTemps.reverse();
      c.humidities = cableHums.reverse();
      c.grainMoistures = cableEMC.reverse();
      c.topNode = cable.topNode;
      c.excludedNodes = cable.excludedNodes;
      newGrainCables.push(c);

      //filter out the excluded nodes and any nodes above the top node
      let filteredNodes = c.filteredNodes(true)
      
      allTemps.push(...filteredNodes.temps);
      if(avg(c.humidities) !== 0){ //if the average of the humidities is 0 that means that all the readings are 0 and it is a temp only cable
        allHums.push(...filteredNodes.humids);
        allMoistures.push(...filteredNodes.moistures);
      }

      //set the hottest and coldest nodes
      if (coldestNode === undefined || Math.min(...filteredNodes.temps) < coldestNode.temp) {
        let lowTempIndex =
          filteredNodes.temps.indexOf(Math.min(...filteredNodes.temps)) === -1
            ? 0
            : filteredNodes.temps.indexOf(Math.min(...filteredNodes.temps));
        coldestNode = {
          temp: filteredNodes.temps[lowTempIndex],
          humid: filteredNodes.humids[lowTempIndex],
          emc: filteredNodes.moistures[lowTempIndex]
        };
      }

      if (hottestNode === undefined || Math.max(...filteredNodes.temps) > hottestNode.temp) {
        let highTempIndex =
          filteredNodes.temps.indexOf(Math.max(...filteredNodes.temps)) === -1
            ? 0
            : filteredNodes.temps.indexOf(Math.max(...filteredNodes.temps));

        hottestNode = {
          temp: filteredNodes.temps[highTempIndex],
          humid: filteredNodes.humids[highTempIndex],
          emc: filteredNodes.moistures[highTempIndex]
        };
      }

      //check cables missed readings
      let lastCableRead = moment(cable.lastRead);
      let elapsedMS = now.diff(lastCableRead);

      if (elapsedMS > cable.measurementInterval) {
        let missedReadings = Math.floor(elapsedMS / cable.measurementInterval);
        if (missedReadings > mostMissedReadings) {
          mostMissedReadings = missedReadings;
        }
      }
    });
    //loop through the CO2's to check for missed readings
    bin.status.co2.forEach(co2 => {
      let lastRead = moment(co2.lastRead);
      let elapsedMS = now.diff(lastRead);
      if (elapsedMS > co2.measurementInterval) {
        let missedReadings = Math.floor(elapsedMS / co2.measurementInterval);
        if (missedReadings > mostMissedReadings) {
          mostMissedReadings = missedReadings;
        }
      }
    })

    setCables(newGrainCables);
    setMostMissed(mostMissedReadings);
    setColdNode(coldestNode);
    setHotNode(hottestNode);
    setAverage({
      temp: avg(allTemps),
      humid: avg(allHums),
      emc: avg(allMoistures)
    });

    let cm = 0;
    if (bin.status.distance && bin.status.distance > 0) {
      //note that no conversions are happening as the unit measurement model is not being used so the value will be in cm
      cm = bin.status.distance;
      let height = or(bin.settings.specs?.heightCm, 0);
      let ratio = 1 - cm / height;
      let capacity = or(bin.settings.specs?.bushelCapacity, 0);
      let lidarEstimate = Math.round(capacity * ratio);
      //setLidarBushels(lidarEstimate);
      setLidarPercentage(Math.round((lidarEstimate / capacity) * 100));
    }
  }, [bin]);

  // useEffect(() => {
  //   let now = moment();
  //   let duration = moment.duration(moment(bin.status.lastModeChange).diff(now));
  //   let days = duration.asDays();
  //   days = Math.abs(days);
  //   duration.subtract(moment.duration(days, "days"));
  //   let hours = duration.hours();
  //   hours = Math.abs(hours);

  //   if (days > 50 && bin.settings.mode === pond.BinMode.BIN_MODE_DRYING) {
  //     setModeDetails("Calculating...");
  //   } else if (bin.settings.mode === pond.BinMode.BIN_MODE_NONE) {
  //     setModeDetails("No Mode");
  //   } else {
  //     setModeDetails(Math.floor(days) + "d " + hours + "h");
  //   }
  // }, [bin]);

  const typeDisplay = () => {
    let grainType = bin.settings.inventory?.grainType ?? pond.Grain.GRAIN_NONE;
    if (bin.storage() === pond.BinStorage.BIN_STORAGE_SUPPORTED_GRAIN) {
      return (
        <Box>
          <Typography variant="subtitle2" style={{ fontSize: "0.7rem", fontWeight: 700 }}>
            {GrainDescriber(grainType).name}
          </Typography>
          <Typography variant="subtitle2" style={{ fontSize: "0.7rem", fontWeight: 700 }}>
            {bin.settings.inventory?.grainSubtype}
          </Typography>
        </Box>
      );
    }
    return (
      <Box>
        <Typography variant="subtitle2" style={{ fontSize: "0.7rem", fontWeight: 700 }}>
          {bin.settings.inventory?.customTypeName}
        </Typography>
      </Box>
    );
  };

  const tempDisplay = () => {
    let display = "--";
    let val;
    switch (valDisplay) {
      case "average":
        val = average?.temp;
        break;
      case "low":
        val = coldNode?.temp;
        break;
      case "high":
        val = hotNode?.temp;
        break;
    }

    if (val !== undefined && !isNaN(val)) {
      if (user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
        val = celsiusToFahrenheit(val);
      }
      display = val.toFixed(2);
    }

    return (
      <Typography
        align="center"
        noWrap
        style={{ fontSize: "0.85rem", fontWeight: 650, color: tempColour }}>
        {display +
          (user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT
            ? "°F"
            : "°C")}
      </Typography>
    );
  };

  const percentDisplay = () => {
    let display = "--";
    let val;
    let useEMC = false;

    switch (valDisplay) {
      case "average":
        if (average?.emc) {
          useEMC = true;
          val = average.emc;
        } else {
          val = average?.humid;
        }
        break;
      case "low":
        if (coldNode?.emc) {
          useEMC = true;
          val = coldNode.emc;
        } else {
          val = coldNode?.humid;
        }
        break;
      case "high":
        if (hotNode?.emc) {
          useEMC = true;
          val = hotNode.emc;
        } else {
          val = hotNode?.humid;
        }
        break;
    }

    if (val !== undefined && !isNaN(val)) {
      display = val.toFixed(2);
    }

    return (
      <Typography
        align="center"
        noWrap
        style={{ fontSize: "0.85rem", fontWeight: 650, color: useEMC ? emcColour : humColour }}>
        {display}%
      </Typography>
    );
  };

  const cableBox = () => {
    return (
      <Box className={classes.displayBox}>
        <Grid container alignContent="center" alignItems="center" direction="row">
          <Grid size={{ xs: 3 }}>
            <TemperatureIcon heightWidth={20} />
          </Grid>
          <Grid size={{ xs: 9 }}>
            <Box>
              {tempDisplay()}
              <Typography
                align="center"
                color="textSecondary"
                noWrap
                style={{ fontSize: "0.5rem" }}>
                {valDisplay === "high" ? "High" : valDisplay === "low" ? "Low" : "Average"}
              </Typography>
            </Box>
          </Grid>
          <Grid size={{ xs: 3 }} style={{ paddingLeft: 5 }}>
            <HumidityIcon height={20} />
          </Grid>
          <Grid size={{ xs: 9 }}>
            <Box>
              {percentDisplay()}
              <Typography
                align="center"
                color="textSecondary"
                noWrap
                style={{ fontSize: "0.5rem" }}>
                {valDisplay === "high" ? "High" : valDisplay === "low" ? "Low" : "Average"}
              </Typography>
            </Box>
          </Grid>
        </Grid>
      </Box>
    );
  };

  const fanBox = (controller: pond.BinFan) => {
    return (
      <Box className={classes.displayBox} key={controller.key}>
        <Grid container direction="row" alignItems="center" alignContent="center">
          <Grid size={{ xs: 3 }} style={{ paddingLeft: 5, paddingTop: 5 }}>
            <AerationFanIcon size={18} />
          </Grid>
          <Grid size={{ xs: 9 }}>
            <Box>
              <Typography
                align="center"
                noWrap
                style={{
                  fontSize: "0.7rem",
                  fontWeight: 650,
                  color: controller.state ? "green" : "orange"
                }}>
                {controller.state ? "ON" : "OFF"}
              </Typography>
            </Box>
          </Grid>
        </Grid>
      </Box>
    );
  };

  const heaterBox = (controller: pond.BinHeater) => {
    return (
      <Box className={classes.displayBox} key={controller.key}>
        <Grid container direction="row" alignItems="center" alignContent="center">
          <Grid size={{ xs: 3 }} style={{ paddingLeft: 5, paddingTop: 3 }}>
            {/* <ObjectHeaterIcon height={18} width={18} /> */}
            <ObjectHeaterIcon />
          </Grid>
          <Grid size={{ xs: 9 }}>
            <Box>
              <Typography
                align="center"
                noWrap
                style={{
                  fontSize: "0.7rem",
                  fontWeight: 650,
                  color: controller.state ? "green" : "orange"
                }}>
                {controller.state ? "ON" : "OFF"}
              </Typography>
            </Box>
          </Grid>
        </Grid>
      </Box>
    );
  };

  const binGraphic = () => {
    return (
      <Box display="flex" justifyContent="center" paddingTop={0}>
        <Box padding={0} style={{ flex: 1, textAlign: "left", flexDirection: "column" }}>
          {typeDisplay()}
          {cableBox()}
          {bin.status.fans.map(controller => fanBox(controller))}
          {bin.status.heaters.map(controller => heaterBox(controller))}
        </Box>
        <Box style={{ flex: 1, textAlign: "center", flexDirection: "column" }}>
          <BinSVGV2
            cableEstimate={bin.settings.autoGrainNode}
            height={160}
            binShape={bin.settings.specs?.shape}
            fillPercentage={bin.fillPercent()}
            lidarEstimate={lidarPercentage}
            cables={cables}
            co2Sensors={bin.status.co2}
            highTemp={bin.settings.highTemp}
            lowTemp={bin.settings.lowTemp}
            hottestNodeTemp={valDisplay === "high" ? hotNode?.temp : undefined}
            coldestNodeTemp={valDisplay === "low" ? coldNode?.temp : undefined}
            inventoryControl={bin.inventoryControl()}
          />
        </Box>
      </Box>
    );
  };

  const inventoryDisplay = (current: number, capacity: number) => {
    if (bin.storage() === pond.BinStorage.BIN_STORAGE_FERTILIZER) {
      return (
        Math.round(current * 35.239).toLocaleString() +
        "/" +
        Math.round(capacity * 35.239).toLocaleString() +
        " L"
      );
    }
    if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_WEIGHT && bin.bushelsPerTonne() > 1) {
      return (
        bin.grainTonnes().toLocaleString() +
        " mT " +
        bin.fillPercent() +
        "%"
      );
    }
    return (
      current.toLocaleString() +
      "/" +
      capacity.toLocaleString() +
      " bu"
    );
  };

  const componentStateBanner = () => {
    const hasCables = bin.status.grainCables.length > 0;
    const hasCO2 = bin.status.co2.length > 0;
    return (
      <Box display="flex" justifyContent="space-between">
        {hasCables || hasCO2 ? (
          <React.Fragment>
            <Typography style={{ fontSize: 15, fontWeight: 650 }}>
              {mostMissed < 3 ? "Active" : mostMissed <= warningThreshold ? "Missing" : "Inactive"}
            </Typography>
            {mostMissed < 3 ? (
              <CheckCircle style={{ color: "green" }} />
            ) : mostMissed <= warningThreshold ? (
              <Warning style={{ color: "yellow" }} />
            ) : (
              <Warning style={{ color: "red" }} />
            )}
          </React.Fragment>
        ) : (
          <Typography style={{ fontSize: 15, fontWeight: 650 }}>Unmonitored</Typography>
        )}
      </Box>
    );
  };

  const info = () => {
    const inv = bin.settings.inventory;
    let bushelAmount = bin.bushels();
    let bushelCapacity = bin.settings.specs?.bushelCapacity;
    const empty =
      !inv || inv.empty || !bushelCapacity || bushelCapacity <= 0 || bushelAmount <= 0;
    return (
      <Box width={1} marginTop={0}>
        <Typography align="center" color="textSecondary" noWrap style={{ fontSize: "0.65rem" }}>
          {empty || !bushelAmount || !bushelCapacity
            ? "empty"
            : inventoryDisplay(bushelAmount, bushelCapacity)}
        </Typography>
      </Box>
    );
  };

  return (
    <Card style={{ cursor: "pointer" }}>
      {user.hasFeature("installer") && (
        <IconButton
          style={{
            height: 35,
            width: 35,
            position: "absolute",
            zIndex: 2,
            right: 1,
            top: 1
          }}
          onClick={e => props.duplicateBin(bin)}
          onMouseEnter={() => props.dupHovered(true)}
          onMouseLeave={() => props.dupHovered(false)}>
          +
        </IconButton>
      )}
      <Box padding={1}>
        <Typography
          align="left"
          variant="body1"
          color="textPrimary"
          noWrap
          style={{ fontSize: "0.8rem", fontWeight: 700 }}>
          {bin.name()}
        </Typography>
        {binGraphic()}
        {info()}
        {componentStateBanner()}
      </Box>
    </Card>
  );
}
