import {
  Accordion,
  AccordionDetails,
  AccordionSummary,
  Box,
  Button,
  darken,
  Grid,
  Slider,
  Theme,
  Typography
} from "@mui/material";
import { ExpandMore } from "@mui/icons-material";
import UnitMeasurementSummary from "component/UnitMeasurementSummary";
import { ExtractMoisture } from "grain";
import { cloneDeep } from "lodash";
import { Component, Interaction } from "models";
import { UnitMeasurement } from "models/UnitMeasurement";
import { interactionConditionText, interactionResultText } from "pbHelpers/Interaction";
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
import { pond, quack } from "protobuf-ts/pond";
import { useGlobalState, useInteractionsAPI, useSnackbar } from "providers";
import React, { useEffect, useState } from "react";
import { avg, fahrenheitToCelsius, getTemperatureUnit } from "utils";
import { makeStyles } from "@mui/styles";
import { Mark } from "@mui/material/Slider/useSlider.types";

const useStyles = makeStyles((theme: Theme) => {
  return ({
    displayBG: {
      marginTop: 10,
      background: darken(theme.palette.background.default, 0.05),
      borderRadius: 5,
      padding: 10
    },
    markContainer: {
      zIndex: 2
    },
    arrowDown: {
      width: 0,
      height: 0,
      borderLeft: "10px solid transparent",
      borderRight: "10px solid transparent"
      //borderTop: "10px solid yellow"
    },
    sliderRoot: {},
    sliderThumb: {
      height: 15,
      width: 15,
      marginTop: -6,
      marginLeft: -7.5,
      backgroundColor: "yellow"
    },
    sliderTrack: {
      height: 3,
      backgroundColor: "white"
    },
    sliderRail: {
      height: 3,
      backgroundColor: "white"
    },
    sliderValLabel: {
      left: "calc(-50%)",
      top: 22,
      "& *": {
        background: "transparent",
        color: "#fff"
      }
    },
    sliderMark: {
      visibility: "hidden"
    },
    sliderMarked: {
      marginTop: 25,
      marginBottom: 0
    },
    sliderMarkLabel: {
      top: -25
    }
  });
});

interface Props {
  deviceId: number;
  interaction: Interaction;
  source: Component;
  sink: Component;
  grain?: pond.Grain;
}

export default function BinConditioningInteraction(props: Props) {
  const { interaction, source, sink, grain, deviceId } = props;
  const [sliderVals, setSliderVals] = useState<Map<quack.MeasurementType, number>>(new Map());
  const [sliderMarks, setSliderMarks] = useState<Map<quack.MeasurementType, number>>(new Map());
  //this is the emc value calculated from the interactions temp and humidity conditions
  const [baseEMC, setBaseEMC] = useState<number | undefined>();
  const [{ user, as }] = useGlobalState();
  const classes = useStyles();
  const interactionAPI = useInteractionsAPI();
  const { openSnack } = useSnackbar();

  useEffect(() => {
    let passedInteraction = interaction;
    let sliderVals: Map<quack.MeasurementType, number> = new Map();
    let sliderMarks: Map<quack.MeasurementType, number> = new Map();
    passedInteraction.conditions().forEach(condition => {
      let describer = describeMeasurement(condition.measurementType, source.type());
      //NOTE: toDisplay will convert the temp value to fahrenheit
      sliderVals.set(condition.measurementType, describer.toDisplay(condition.value));
    });

    source.status.lastGoodMeasurement.forEach(measurement => {
      let m = pond.UnitMeasurementsForComponent.fromObject(measurement);
      if (m.values[0]) {
        let markVal = avg(m.values[0].values);
        //do this since this is how interactions handle the values so that the slider can use the toDisplay method of the describer for the marks
        if (m.type === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE) markVal = markVal * 10;
        if (m.type === quack.MeasurementType.MEASUREMENT_TYPE_PERCENT) markVal = markVal * 100;
        sliderMarks.set(m.type, markVal);
      }
    });
    setSliderVals(sliderVals);
    setSliderMarks(sliderMarks);
  }, [interaction, source]);

  useEffect(() => {
    let temp = sliderVals.get(quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE);
    let hum = sliderVals.get(quack.MeasurementType.MEASUREMENT_TYPE_PERCENT);
    if (
      grain !== undefined &&
      grain !== pond.Grain.GRAIN_INVALID &&
      grain !== pond.Grain.GRAIN_CUSTOM &&
      temp &&
      hum
    ) {
      if (getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
        //the emc calc needs the temp to be in celsius
        temp = fahrenheitToCelsius(temp);
      }
      let emc = ExtractMoisture(grain, temp, hum);
      setBaseEMC(emc);
    }
  }, [sliderVals, grain]);

  const updateInteraction = () => {
    interactionAPI
      .updateInteraction(deviceId, interaction.settings, as)
      .then(resp => {
        openSnack("Updated Interaction Conditions");
      })
      .catch(err => {
        openSnack("Failed to Update Interaction Conditions");
      });
  };

  const customMark = (val: string, arrowColor: string) => {
    return (
      <Box className={classes.markContainer}>
        <Grid container direction="column" alignContent="center" alignItems="center" spacing={1}>
          <Grid item>
            <Typography style={{ color: "white", fontSize: 15, fontWeight: 650, lineHeight: 1 }}>
              {val}
            </Typography>
          </Grid>
          <Grid item>
            <Box className={classes.arrowDown} style={{ borderTop: "10px solid " + arrowColor }} />
          </Grid>
        </Grid>
      </Box>
    );
  };

  const conditionDisplay = () => {
    return (
      <Grid container>
        {interaction.conditions().map((condition, i) => {
          let describer = describeMeasurement(condition.measurementType, source?.type());
          let labelTail = "";
          let marks: Mark[] = [];

          if (condition.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE) {
            if (getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
              labelTail = "°F";
            } else {
              labelTail = "°C";
            }
          }
          if (condition.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_PERCENT) {
            labelTail = "%";
          }

          let mark = sliderMarks.get(condition.measurementType);
          if (mark !== undefined) {
            marks.push({
              label: customMark(describer.toDisplay(mark) + labelTail, describer.colour()),
              value: describer.toDisplay(mark)
            });
          }
          return (
            <Grid key={i} container item xs={12} alignContent="center" alignItems="center">
              <Grid item xs={12}>
                {interactionConditionText(source, condition, false)}
              </Grid>
              <Grid item xs={12} style={{ marginBottom: 20 }}>
                <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
                  }}
                  step={0.1}
                  valueLabelDisplay="on"
                  valueLabelFormat={value => {
                    if (
                      condition.measurementType ===
                      quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE
                    ) {
                      if (
                        getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT
                      ) {
                        return value.toFixed(1) + "°F";
                      } else {
                        return value.toFixed(1) + "°C";
                      }
                    }
                    if (
                      condition.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_PERCENT
                    ) {
                      return value.toFixed(1) + "%";
                    }
                  }}
                  marks={marks}
                  min={describer.min()}
                  max={describer.max()}
                  value={sliderVals.get(condition.measurementType) ?? describer.min()}
                  onChange={(_, val) => {
                    condition.value = Math.round(describer.toStored(val as number));
                    let sliders = cloneDeep(sliderVals);
                    sliders.set(condition.measurementType, val as number);
                    setSliderVals(sliders);
                  }}
                />
              </Grid>
            </Grid>
          );
        })}
        <Grid item xs={12}>
          <Button
            variant="contained"
            color="primary"
            onClick={() => {
              updateInteraction();
            }}>
            Update Conditions
          </Button>
        </Grid>
      </Grid>
    );
  };

  const determineEMCRelation = () => {
    let relation = "Approximately:";
    let tempRelation: quack.RelationalOperator | undefined = undefined;
    let humidRelation: quack.RelationalOperator | undefined = undefined;
    interaction.conditions().forEach(condition => {
      if (condition.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE)
        tempRelation = condition.comparison;
      if (condition.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_PERCENT)
        humidRelation = condition.comparison;
    });

    if (
      (tempRelation === quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN ||
        tempRelation === quack.RelationalOperator.RELATIONAL_OPERATOR_EQUAL_TO) &&
      (humidRelation === quack.RelationalOperator.RELATIONAL_OPERATOR_LESS_THAN ||
        humidRelation === quack.RelationalOperator.RELATIONAL_OPERATOR_EQUAL_TO)
    ) {
      relation = "Less Than: ";
    }
    if (
      (tempRelation === quack.RelationalOperator.RELATIONAL_OPERATOR_LESS_THAN ||
        tempRelation === quack.RelationalOperator.RELATIONAL_OPERATOR_EQUAL_TO) &&
      (humidRelation === quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN ||
        humidRelation === quack.RelationalOperator.RELATIONAL_OPERATOR_EQUAL_TO)
    ) {
      relation = "Greater Than: ";
    }

    return relation;
  };

  return (
    <Grid key={interaction.key()} container className={classes.displayBG}>
      <Grid item xs={6}>
        <Typography style={{ fontWeight: 650 }}>{source.name()}</Typography>
        <UnitMeasurementSummary
          component={source}
          reading={UnitMeasurement.convertLastMeasurement(
            source.status.lastGoodMeasurement.map(um => UnitMeasurement.any(um, user))
          )}
        />
      </Grid>
      <Grid item xs={6}>
        <Typography style={{ fontWeight: 650 }}>{sink.name()}</Typography>
        <UnitMeasurementSummary
          component={sink}
          reading={UnitMeasurement.convertLastMeasurement(
            sink.status.lastGoodMeasurement.map(um => UnitMeasurement.any(um, user))
          )}
        />
      </Grid>
      <Grid item xs={12}>
        <Typography align="center" style={{ margin: 5, marginBottom: 10, fontWeight: 650 }}>
          {interactionResultText(interaction, sink)}
        </Typography>
        {baseEMC !== undefined ? (
          <Accordion>
            <AccordionSummary expandIcon={<ExpandMore />}>
              <Box display="flex">
                <Typography style={{ fontWeight: 650 }}>EMC {determineEMCRelation()}</Typography>
                <Typography
                  style={{
                    marginLeft: 5,
                    fontWeight: 650,
                    color: describeMeasurement(
                      quack.MeasurementType.MEASUREMENT_TYPE_GRAIN_EMC
                    ).colour()
                  }}>
                  {baseEMC.toFixed(1)}%
                </Typography>
              </Box>
            </AccordionSummary>
            <AccordionDetails>{conditionDisplay()}</AccordionDetails>
          </Accordion>
        ) : (
          <React.Fragment>{conditionDisplay()}</React.Fragment>
        )}
      </Grid>
    </Grid>
  );
}
