import {
  Avatar,
  Box,
  Card,
  CardContent,
  CardHeader,
  Grid2 as Grid,
  Theme,
  Typography,
  useTheme
} from "@mui/material";
import { useDeviceAPI, useGlobalState, useMutationAPI } from "providers";
import { Component, Device } from "models";
import moment from "moment";
import { pond } from "protobuf-ts/pond";
import { quack } from "protobuf-ts/quack";
import { GetDefaultDateRange } from "common/time/DateRange";
import React, { useEffect, useState } from "react";
import MultiLineGraph, { LineData } from "charts/measurementCharts/MultiLineGraph";
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
import DateSelect from "common/time/DateSelect";
import {
  Area,
  AreaChart,
  ReferenceArea,
  ResponsiveContainer,
  Tooltip,
  TooltipProps,
  XAxis,
  YAxis
} from "recharts";
import FuelDark from "assets/components/fuelDark.png";
import FuelLight from "assets/components/fuelLight.png";
import { useThemeType } from "hooks";
import { GetComponentIcon } from "pbHelpers/ComponentType";
import UnitMeasurementSummary from "component/UnitMeasurementSummary";
import { UnitMeasurement } from "models/UnitMeasurement";
import MaterialChartTooltip from "charts/MaterialChartTooltip";
import { roundTo } from "utils";
import { makeStyles } from "@mui/styles";
//import TimeBar from "common/time/TimeBar";

interface Props {
  device: pond.ComprehensiveDevice;
  linearMutations: pond.LinearMutation[];
  xDomain: string[] | number[];
  changeXDomain: (domain: string[] | number[]) => void;
  getFuelLevel?: (level: string) => void;
}

interface GraphData {
  time: number;
  value: number;
}

interface MutationData {
  mutation: pond.LinearMutation;
  data: pond.ValueAt[];
}

const useStyles = makeStyles((theme: Theme) => ({
    avatarIcon: {
      width: theme.spacing(3),
      height: theme.spacing(3)
    },
    card: {
      height: 550
    },
    cardHeader: {
      height: "20%"
    },
    cardContent: {
      height: "80%"
    }
  })
);

export default function ObjectHeaterCharts(props: Props) {
  const { device, linearMutations, getFuelLevel, xDomain, changeXDomain } = props;
  const deviceAPI = useDeviceAPI();
  const mutationAPI = useMutationAPI();
  const [tempHum, setTempHum] = useState<Component>();
  const [ambientTemps, setAmbientTemps] = useState<LineData[]>([]);
  const [heaterTemps, setHeaterTemps] = useState<pond.ExportedUnitMeasurement[]>([]);
  const [heaterState, setHeaterState] = useState<pond.ExportedUnitMeasurement[]>([]);
  const [tempOnly, setTempOnly] = useState<Component>();
  const [heater, setHeater] = useState<Component>();
  const [start, setStart] = useState(GetDefaultDateRange().start);
  const [end, setEnd] = useState(GetDefaultDateRange().end);
  const now = moment();
  const theme = useTheme();
  const [mutationData, setMutationData] = useState<MutationData[]>([]);
  const [{ user, as }] = useGlobalState();
  const themeType = useThemeType();
  const classes = useStyles();
  const [mutationsLoading, setMutationsLoading] = useState(false);
  const [refLeft, setRefLeft] = useState<number | undefined>();
  const [refRight, setRefRight] = useState<number | undefined>();

  useEffect(() => {
    if (!mutationsLoading) {
      setMutationsLoading(true);
      //make call to the backend to get measurements for the mutation
      let md: MutationData[] = [];
      linearMutations.forEach(mut => {
        mutationAPI.linearMutation(mut, start.toISOString(), end.toISOString()).then(resp => {
          let d: pond.ValueAt[] = resp.data.values;
          if (mut.mutation === pond.Mutator.MUTATOR_FUEL_LEVEL) {
            d = [];
            resp.data.values.forEach(valAt => {
              d.push(
                pond.ValueAt.create({
                  time: valAt.time,
                  value: valAt.value / 10
                })
              );
            });
          }
          md.push({
            mutation: mut,
            data: d
          });
          if (md.length >= linearMutations.length) {
            setMutationsLoading(false);
          }
          setMutationData([...md]);
          if (getFuelLevel && mut.mutation === pond.Mutator.MUTATOR_FUEL_LEVEL) {
            if (resp.data.values.length > 0) {
              getFuelLevel(
                (resp.data.values[resp.data.values.length - 1].value / 10).toFixed(2) + "%"
              );
            }
          }
        });
      });
    }
  }, [linearMutations, start, end, getFuelLevel, mutationAPI]); //eslint-disable-line react-hooks/exhaustive-deps

  useEffect(() => {
    let tempHum: Component | undefined = undefined;
    let tempOnly: Component | undefined = undefined;
    let heater: Component | undefined = undefined;

    let dev = Device.any(device.device);
    let components = device.components;
    let measurementsFor: Component[] = [];
    components.forEach(comp => {
      let c = Component.any(comp);
      if (c.settings.type === quack.ComponentType.COMPONENT_TYPE_DHT) {
        tempHum = c;
        measurementsFor.push(c);
      }
      if (c.settings.type === quack.ComponentType.COMPONENT_TYPE_TEMPERATURE) {
        tempOnly = c;
        measurementsFor.push(c);
      }
      if (c.settings.type === quack.ComponentType.COMPONENT_TYPE_BOOLEAN_OUTPUT) {
        if (c.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_HEATER) {
          heater = c;
          measurementsFor.push(c);
        }
      }
    });
    setTempHum(tempHum);
    setTempOnly(tempOnly);
    setHeater(heater);
    deviceAPI
      .listJSONMeasurements(dev.id(), measurementsFor, start, end, 0, 0, "asc", "timestamp", as)
      .then(resp => {
        resp.data.components.forEach(comp => {
          if (comp.componentId === tempHum?.key()) {
            if (comp.measurementType === "Temperature") {
              let lineData: LineData[] = [];
              comp.exportedMeasurements.forEach(m => {
                lineData.push({
                  timestamp: moment(m.timestamp).valueOf(),
                  points: m.measurementValue.map((t, i) => ({ value: convertTemp(t), node: i }))
                });
              });
              setAmbientTemps(lineData);
            }
          }
          if (comp.componentId === tempOnly?.key()) {
            if (comp.measurementType === "Temperature") {
              setHeaterTemps(comp.exportedMeasurements);
            }
          }
          if (comp.componentId === heater?.key()) {
            if (comp.measurementType === "State") {
              setHeaterState(comp.exportedMeasurements);
            }
          }
        });
      });
  }, [device, deviceAPI, start, end, as]); //eslint-disable-line react-hooks/exhaustive-deps

  const updateDateRange = (newStartDate: any, newEndDate: any, live: boolean) => {
    setStart(newStartDate);
    setEnd(newEndDate);
    // setLive(live);
    // if (props.updateDate) {
    //   props.updateDate(newStartDate, newEndDate, live);
    // }
  };

  const convertTemp = (temp: number) => {
    let t = temp;
    if (user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
      t = temp * 1.8 + 32;
    }
    return t;
  };

  //had to make custom chart for this in order to show the heater state and the temp of the ds18
  const heaterStateWithTemps = (tempComponent: Component, heaterComponent: Component) => {
    let tempDescriber = describeMeasurement(
      quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE,
      tempComponent.type(),
      tempComponent.subType()
    );
    let heaterDescriber = describeMeasurement(
      quack.MeasurementType.MEASUREMENT_TYPE_BOOLEAN,
      heaterComponent.type(),
      heaterComponent.subType()
    );
    let tempData: LineData[] = [];
    heaterTemps.forEach(ht => {
      tempData.push({
        timestamp: moment(ht.timestamp).valueOf(),
        points: ht.measurementValue.map((t, i) => ({ value: convertTemp(t), node: i }))
      });
    });
    let stateData: LineData[] = [];
    heaterState.forEach(hs => {
      stateData.push({
        timestamp: moment(hs.timestamp).valueOf(),
        points: hs.measurementValue.map((s, i) => ({ value: s, node: i }))
      });
    });
    return (
      <React.Fragment>
        <MultiLineGraph
          describer={tempDescriber}
          lineData={tempData}
          numLines={1}
          customHeight={"70%"}
          tooltip
          newXDomain={xDomain}
          multiGraphZoom={domain => {
            changeXDomain(domain);
          }}
          multiGraphZoomOut
        />
        <MultiLineGraph
          describer={heaterDescriber}
          lineData={stateData}
          numLines={1}
          customHeight={"30%"}
          tooltip
          yMin={0}
          yMax={1}
          hideY
          newXDomain={xDomain}
          multiGraphZoom={domain => {
            changeXDomain(domain);
          }}
          multiGraphZoomOut
        />
      </React.Fragment>
    );
  };

  //// TODO-CS: Make a mutator describer to put the get functions into ////
  const getMutationIcon = (mutation: pond.Mutator, themeType: string) => {
    if (mutation === pond.Mutator.MUTATOR_FUEL_LEVEL) {
      return themeType === "light" ? FuelDark : FuelLight;
    }
    return "";
  };

  const getMutationLabel = (mutation: pond.Mutator) => {
    if (mutation === pond.Mutator.MUTATOR_FUEL_LEVEL) {
      return "Fuel Level";
    }
    return "";
  };

  const getMutationColour = (mutation: pond.Mutator) => {
    if (mutation === pond.Mutator.MUTATOR_FUEL_LEVEL) {
      return "lightBlue";
    }
    return "";
  };

  const getMutationUnit = (mutation: pond.Mutator) => {
    if (mutation === pond.Mutator.MUTATOR_FUEL_LEVEL) {
      return "%";
    }
    return "";
  };

  //// /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ ////

  const zoom = () => {
    let newDomain: number[] | string[] = ["dataMin", "dataMax"];
    if (refLeft && refRight && refLeft !== refRight) {
      refLeft < refRight ? (newDomain = [refLeft, refRight]) : (newDomain = [refRight, refLeft]);
      setRefLeft(undefined);
      setRefRight(undefined);
      changeXDomain(newDomain);
    }
  };

  const mutationGraphCard = (mutation: pond.LinearMutation, data: pond.ValueAt[], key: string) => {
    let graphData: GraphData[] = [];
    let lastReading: pond.ValueAt | undefined;
    data.forEach(d => {
      graphData.push({
        time: moment(d.time).valueOf(),
        value: d.value
      });
      lastReading = d;
    });
    return (
      <Grid size={{
        xs:12, 
        sm:12, 
        md:4   
      }}
      key={key}>
        <Card className={classes.card}>
          {graphData.length > 0 ? (
            <React.Fragment>
              <CardHeader
                className={classes.cardHeader}
                avatar={
                  <Avatar
                    variant="square"
                    src={getMutationIcon(mutation.mutation, themeType)}
                    className={classes.avatarIcon}
                    alt={"mutIcon"}
                  />
                }
                title={mutation.mutationName}
                titleTypographyProps={{ variant: "subtitle1" }}
                subheader={
                  <Grid container>
                    <Grid size={12}>
                      <Typography variant="caption" color="textPrimary">
                        <span>
                          {getMutationLabel(mutation.mutation) + ": "}{" "}
                          <span style={{ color: getMutationColour(mutation.mutation) }}>
                            {lastReading?.value.toFixed(2) + getMutationUnit(mutation.mutation)}
                          </span>{" "}
                          <br />
                        </span>
                      </Typography>
                    </Grid>
                    <Grid size={12}>
                      <Typography color="textSecondary" variant="caption">
                        {moment(lastReading?.time).fromNow()}
                      </Typography>
                    </Grid>
                  </Grid>
                }
              />
              <CardContent className={classes.cardContent}>
                <ResponsiveContainer width={"100%"} height={"100%"}>
                  <AreaChart
                    data={graphData}
                    onMouseDown={(e: any) => {
                      if (e) {
                        setRefLeft(e.activeLabel);
                      }
                    }}
                    onMouseMove={(e: any) => {
                      if (e) {
                        setRefRight(e.activeLabel);
                      }
                    }}
                    onMouseUp={() => {
                      setRefLeft(undefined);
                      setRefRight(undefined);
                      zoom();
                    }}>
                    <Tooltip
                      animationEasing="ease-out"
                      cursor={{ fill: theme.palette.text.primary, opacity: "0.15" }}
                      labelFormatter={timestamp => moment(timestamp).format("lll")}
                      content={(props: TooltipProps<any, any>) => {
                        return (
                          <MaterialChartTooltip
                            {...props}
                            valueFormatter={value =>
                              roundTo(parseFloat(String(value)), 2).toString()
                            }
                          />
                        );
                      }}
                    />
                    <XAxis
                      dataKey="time"
                      allowDataOverflow
                      domain={xDomain}
                      name="Time"
                      tickFormatter={timestamp => {
                        let t = moment(timestamp);
                        return now.isSame(t, "day") ? t.format("LT") : t.format("MMM DD");
                      }}
                      scale="time"
                      type="number"
                      tick={{ fill: theme.palette.text.primary }}
                      stroke={theme.palette.divider}
                      interval="preserveStartEnd"
                    />
                    <YAxis
                      type="number"
                      domain={
                        mutation.mutation === pond.Mutator.MUTATOR_FUEL_LEVEL
                          ? [0, 100]
                          : ["auto", "auto"]
                      }
                      label={{
                        value: mutation.mutationName,
                        position: "insideLeft",
                        angle: -90,
                        fill: theme.palette.text.primary
                      }}
                      tick={{ fill: theme.palette.text.primary }}
                    />
                    <Area dataKey={"value"} />
                    {refLeft && refRight ? (
                      <ReferenceArea x1={refLeft} x2={refRight} strokeOpacity={0.3} />
                    ) : null}
                  </AreaChart>
                </ResponsiveContainer>
              </CardContent>
            </React.Fragment>
          ) : (
            <Box display="flex" justifyContent="center" alignItems="center" height="100%">
              <Typography style={{ fontWeight: 650 }}>
                No measurements for selected timeframe
              </Typography>
            </Box>
          )}
        </Card>
      </Grid>
    );
  };

  const mutationGraphs = () => {
    let graphCards: JSX.Element[] = [];
    if (mutationData.length === 0) {
      return (
        <Grid size={{
          xs:12, 
          sm:12, 
          md:4
        }}>
          <Card className={classes.card}>
            <Box display="flex" justifyContent="center" alignItems="center" height={"100%"}>
              <Typography style={{ fontWeight: 650 }}>No Calculated Measurements Set</Typography>
            </Box>
          </Card>
        </Grid>
      );
    }
    mutationData.forEach((mutation, i) => {
      graphCards.push(mutationGraphCard(mutation.mutation, mutation.data, "mutation" + i));
    });
    return graphCards;
  };

  return (
    <React.Fragment>
      {/* <TimeBar updateDateRange={updateDateRange} startDate={start} endDate={end}/> */}
      <Grid container direction="row" spacing={1}>
        <Grid size={{
          xs:12, 
          sm:12, 
          md:4
        }}> 
          {tempOnly && heater && (
            <Card className={classes.card}>
              {(heaterTemps.length > 0 || heaterState.length > 0) ? (
                <React.Fragment>
                  <Grid container className={classes.cardHeader}>
                    {tempOnly && (
                      <Grid size={6}>
                        <CardHeader
                          avatar={
                            <Avatar
                              variant="square"
                              src={GetComponentIcon(tempOnly.type(), tempOnly.subType(), themeType)}
                              className={classes.avatarIcon}
                              alt={"tempIcon"}
                            />
                          }
                          title={tempOnly.name()}
                          subheader={
                            <UnitMeasurementSummary
                              component={tempOnly}
                              reading={UnitMeasurement.convertLastMeasurement(
                                tempOnly.lastMeasurement.map(um => UnitMeasurement.any(um, user))
                              )}
                            />
                          }
                          titleTypographyProps={{ variant: "subtitle1" }}
                        />
                      </Grid>
                    )}
                    {heater && (
                      <Grid size={6}>
                        <CardHeader
                          avatar={
                            <Avatar
                              variant="square"
                              src={GetComponentIcon(heater.type(), heater.subType(), themeType)}
                              className={classes.avatarIcon}
                              alt={"tempIcon"}
                            />
                          }
                          title={heater.name()}
                          subheader={
                            <UnitMeasurementSummary
                              component={heater}
                              reading={UnitMeasurement.convertLastMeasurement(
                                heater.lastMeasurement.map(um => UnitMeasurement.create(um, user))
                              )}
                            />
                          }
                          titleTypographyProps={{ variant: "subtitle1" }}
                        />
                      </Grid>
                    )}
                  </Grid>

                  <CardContent className={classes.cardContent}>
                    {heaterStateWithTemps(tempOnly, heater)}
                  </CardContent>
                </React.Fragment>
              ) : (
                <Box display="flex" justifyContent="center" alignItems="center" height={400}>
                  <Typography style={{ fontWeight: 650 }}>
                    No measurements for selected timeframe
                  </Typography>
                </Box>
              )}
            </Card>
          )}
        </Grid>
        <Grid size={{
          xs:12, 
          sm:12, 
          md:4
        }}> 
          {tempHum && (
            <Card className={classes.card}>
              {ambientTemps.length > 0 ? (
                <React.Fragment>
                  <CardHeader
                    className={classes.cardHeader}
                    avatar={
                      <Avatar
                        variant="square"
                        src={GetComponentIcon(tempHum.type(), tempHum.subType(), themeType)}
                        className={classes.avatarIcon}
                        alt={"tempIcon"}
                      />
                    }
                    title={tempHum.name()}
                    subheader={
                      <UnitMeasurementSummary
                        component={tempHum}
                        reading={UnitMeasurement.convertLastMeasurement(
                          tempHum.lastMeasurement
                            .filter(
                              um => um.type === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE
                            )
                            .map(um => UnitMeasurement.any(um, user))
                        )}
                      />
                    }
                    titleTypographyProps={{ variant: "subtitle1" }}
                  />
                  <CardContent className={classes.cardContent}>
                    <MultiLineGraph
                      describer={describeMeasurement(
                        quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE,
                        tempHum.type(),
                        tempHum.subType()
                      )}
                      lineData={ambientTemps}
                      numLines={1}
                      customHeight={"100%"}
                      tooltip
                      newXDomain={xDomain}
                      multiGraphZoom={domain => {
                        changeXDomain(domain);
                      }}
                      multiGraphZoomOut
                    />
                  </CardContent>
                </React.Fragment>
              ) : (
                <Box display="flex" justifyContent="center" alignItems="center" height={400}>
                  <Typography style={{ fontWeight: 650 }}>
                    No measurements for selected timeframe
                  </Typography>
                </Box>
              )}
            </Card>
          )}
        </Grid>
        {mutationGraphs()}
      </Grid>

      <DateSelect updateDateRange={updateDateRange} startDate={start} endDate={end} />
    </React.Fragment>
  );
}
