import {
  Box,
  Button,
  Card,
  DialogActions,
  DialogContent,
  DialogTitle,
  Grid2 as Grid,
  Tooltip,
  Typography
} from "@mui/material";
import DeviceLinkDrawer from "common/DeviceLinkDrawer";
import ObjectControls from "common/ObjectControls";
import ObjectHeaterActions from "objectHeater/ObjectHeaterActions";
import { useDeviceAPI, useGlobalState, useObjectHeaterAPI, useUserAPI } from "providers";
import React, { useEffect, useState } from "react";
import { useLocation } from "react-router";
import PageContainer from "./PageContainer";
import { ObjectHeater } from "models/ObjectHeater";
//import { MatchParams } from "navigation/Routes";
import { pond } from "protobuf-ts/pond";
import { Component, Device, Scope } from "models";
import { useSnackbar, useThemeType } from "hooks";
import { quack } from "protobuf-ts/quack";
import ObjectHeaterCharts from "objectHeater/ObjectHeaterCharts";
import HeaterDarkIcon from "assets/components/heaterDark.png";
import HeaterLightIcon from "assets/components/heaterLight.png";
import Link from "@mui/icons-material/Link";
import { LinkOff } from "@mui/icons-material";
import { UnitMeasurement } from "models/UnitMeasurement";
import TemperatureIcon from "component/TemperatureIcon";
import FuelIcon from "products/CommonIcons/fuelIcon";
import moment from "moment";
import Warning from "@mui/icons-material/Warning";
import { getTemperatureUnit } from "utils";
import ResponsiveDialog from "common/ResponsiveDialog";
import { useParams } from "react-router-dom";

export default function Heater() {
  const [openLinkDrawer, setOpenLinkDrawer] = useState(false);
  const { openSnack } = useSnackbar();
  const userAPI = useUserAPI();
  const heaterAPI = useObjectHeaterAPI();
  const deviceAPI = useDeviceAPI();
  const location = useLocation();
  //const match = useRouteMatch<MatchParams>();
  //const heaterID = match.params.heaterID;
  const heaterID = useParams<{ heaterKey: string }>()?.heaterKey ?? "";
  const [{ as, user }] = useGlobalState();
  const [permissions, setPermissions] = useState<pond.Permission[]>([]);
  const [heater, setHeater] = useState<ObjectHeater>(ObjectHeater.create());
  const [loadingDevs, setLoadingDevs] = useState(false);
  const [devices, setDevices] = useState<Map<string, pond.ComprehensiveDevice>>(
    new Map<string, pond.ComprehensiveDevice>()
  );
  const [displayDevice, setDisplayDevice] = useState<pond.ComprehensiveDevice>();
  const [componentsByDevice, setComponentsByDevice] = useState<Map<string, Component[]>>();
  const [displayedMutations, setDisplayedMutations] = useState<pond.LinearMutation[]>([]);
  const [heaterComp, setHeaterComp] = useState<Component>();
  const [heaterTempComp, setHeaterTempComp] = useState<Component>();
  const [siteTempComp, setSiteTempComp] = useState<Component>();
  const [fuelLevel, setFuelLevel] = useState<string>("Unknown");
  const themeType = useThemeType();
  const [heaterStateUpToDate, setHeaterStateUpToDate] = useState(true);
  const [heaterTempUpToDate, setHeaterTempUpToDate] = useState(true);
  const [siteTempUpToDate, setSiteTempUpToDate] = useState(true);
  const [xDomain, setXDomain] = useState<string[] | number[]>(["dataMin", "dataMax"]);
  const [unlinkDialogOpen, setUnlinkDialogOpen] = useState(false);

  const zoomOut = () => {
    setXDomain(["dataMin", "dataMax"]);
  };

  const setComponents = (compDev: pond.ComprehensiveDevice) => {
    compDev.components.forEach(component => {
      let c = Component.any(component);
      if (
        c.settings.type === quack.ComponentType.COMPONENT_TYPE_BOOLEAN_OUTPUT &&
        c.settings.subtype === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_HEATER
      ) {
        setHeaterComp(c);
        if (c.lastMeasurement[0] && c.lastMeasurement[0].timestamps[0]) {
          setHeaterStateUpToDate(
            moment().diff(moment(c.lastMeasurement[0].timestamps[0]), "milliseconds") <=
              c.settings.reportPeriodMs
          );
        }
      }
      if (c.settings.type === quack.ComponentType.COMPONENT_TYPE_TEMPERATURE) {
        setHeaterTempComp(c);
        if (c.lastMeasurement[0] && c.lastMeasurement[0].timestamps[0]) {
          setHeaterTempUpToDate(
            moment().diff(moment(c.lastMeasurement[0].timestamps[0]), "milliseconds") <=
              c.settings.reportPeriodMs
          );
        }
      }
      if (c.settings.type === quack.ComponentType.COMPONENT_TYPE_DHT) {
        setSiteTempComp(c);
        if (c.lastMeasurement[0] && c.lastMeasurement[0].timestamps[0]) {
          setSiteTempUpToDate(
            moment().diff(moment(c.lastMeasurement[0].timestamps[0]), "milliseconds") <=
              c.settings.reportPeriodMs
          );
        }
      }
    });
  };

  useEffect(() => {
    let key = heaterID;
    let kind = "objectHeater";
    if (as) {
      key = as;
      kind = "team";
    }
    userAPI.getUser(user.id(), { key: key, kind: kind } as Scope).then(resp => {
      setPermissions(resp.permissions);
    });
  }, [as, heaterID, userAPI, user]);

  useEffect(() => {
    if (location.state && location.state.heater) {
      setHeater(location.state.heater);
      if (location.state.devices) {
        let map: Map<string, Component[]> = new Map<string, Component[]>();
        let devMap = new Map<string, pond.ComprehensiveDevice>();
        location.state.devices.forEach((compDev: pond.ComprehensiveDevice) => {
          let device = Device.any(compDev.device);
          let components = compDev.components.map(c => Component.any(c));
          devMap.set(device.id().toString(), pond.ComprehensiveDevice.fromObject(compDev));
          map.set(device.id().toString(), components);
          setDisplayDevice(compDev);
          setComponents(compDev);
        });
        setDevices(devMap);
        setComponentsByDevice(map);
        setDisplayedMutations(location.state.heater.settings.mutations ?? []);
      }
    } else {
      //if the heater was not carried from the heaters page, ie. page was reloaded, we will need to load the heater from the backend
      heaterAPI
        .getObjectHeater(heaterID, as)
        .then(resp => {
          let h = ObjectHeater.any(resp.data.heater);
          setHeater(h);
          setDisplayedMutations(h.settings.mutations ?? []);
        })
        .catch(err => {

        });

      if (!loadingDevs) {
        setLoadingDevs(true);
        deviceAPI
          .list(
            100,
            0,
            undefined,
            undefined,
            undefined,
            undefined,
            undefined,
            undefined,
            undefined,
            true,
            true,
            [heaterID],
            ["objectHeater"],
            undefined,
            undefined,
            as
          )
          .then(resp => {
            setLoadingDevs(false);
            let map: Map<string, Component[]> = new Map<string, Component[]>();
            let devMap = new Map<string, pond.ComprehensiveDevice>();
            resp.data.comprehensiveDevices.forEach(compDev => {
              let device = Device.any(compDev.device);
              let components = compDev.components.map(c => Component.any(c));
              devMap.set(device.id().toString(), pond.ComprehensiveDevice.fromObject(compDev));
              map.set(device.id().toString(), components);
              setDisplayDevice(compDev);
              setComponents(compDev);
            });
            setDevices(devMap);
            setComponentsByDevice(map);
          });
      }
    }
  }, [location, heaterAPI, heaterID, deviceAPI, as]); //eslint-disable-line react-hooks/exhaustive-deps

  const disconnectDisplayDevice = () => {
    if (displayDevice) {
      let deviceID = displayDevice.device?.settings?.deviceId;
      if (deviceID) {
        heaterAPI
          .updateLink(heater.key, "objectHeater", deviceID.toString(), "device", [], as)
          .then(resp => {
            if (deviceID) {
              devices.delete(deviceID.toString());
              setDisplayDevice(undefined);
              setComponentsByDevice(undefined);
              setHeaterComp(undefined);
              setHeaterTempComp(undefined);
              setSiteTempComp(undefined);
              setFuelLevel("Unknown");
              let updatedHeater = heater;
              updatedHeater.settings.mutations = [];
              heaterAPI
                .updateObjectHeater(heater.key, heater.name, updatedHeater.settings, as)
                .then(resp => {
                  setHeater(updatedHeater);
                  setDisplayedMutations([...updatedHeater.settings.mutations]);
                });
            }
          });
      }
    }
  };

  const deviceDrawer = () => {
    return (
      <DeviceLinkDrawer
        open={openLinkDrawer}
        close={() => {
          setOpenLinkDrawer(false);
        }}
        linkedDevices={devices}
        updateLinkedDevices={(dev, linked) => {
          let deviceID = dev.device?.settings?.deviceId;
          if (deviceID) {
            if (linked) {
              heaterAPI
                .updateLink(heater.key, "objectHeater", deviceID.toString(), "device", [
                  "read",
                  "write",
                  "grant",
                  "revoke"
                ], as)
                .then(resp => {
                  if (deviceID) {
                    devices.set(deviceID.toString(), dev);
                    setDisplayDevice(dev);
                    setOpenLinkDrawer(false);
                    openSnack("Device Linked to Heater");
                    setComponents(dev);
                    let components: Map<string, Component[]> = new Map<string, Component[]>();
                    components.set(
                      deviceID.toString(),
                      dev.components.map(c => Component.any(c))
                    );
                    setComponentsByDevice(components);
                  }
                });
            }
          }
        }}
      />
    );
  };

  const displayHeaterState = () => {
    let state = "Unknown";
    if (heaterComp) {
      let lastReading = heaterComp.lastMeasurement[0];
      if (lastReading && lastReading.values.length > 0) {
        if (lastReading.values[0].values[0] === 0) {
          state = "Off";
        } else if (lastReading.values[0].values[0] === 1) {
          state = "On";
        }
      }
    }
    return (
      <React.Fragment>
        <Typography variant="body1">{state}</Typography>
        {!heaterStateUpToDate && (
          <Tooltip title="Component Not Reporting">
            <Warning style={{ color: "orange" }} />
          </Tooltip>
        )}
      </React.Fragment>
    );
  };

  const tempUnit = () => {
    let unit = "°C";
    if (getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
      unit = "°F";
    }
    return unit;
  };

  const displayHeaterTemp = () => {
    let temp = "Unknown";
    if (heaterTempComp) {
      let lastReading = UnitMeasurement.any(heaterTempComp.lastMeasurement[0], user);
      if (lastReading && lastReading.values[0] && lastReading.values[0].values[0]) {
        temp = lastReading.values[0].values[0].toFixed(2) + tempUnit();
      }
    }
    return (
      <React.Fragment>
        <TemperatureIcon heightWidth={20} />
        <Typography variant="body1" style={{ paddingLeft: 5, fontWeight: 650 }}>
          {temp}
        </Typography>
        {!heaterTempUpToDate && (
          <Tooltip title="Component Not Reporting">
            <Warning style={{ color: "orange" }} />
          </Tooltip>
        )}
      </React.Fragment>
    );
  };

  const displaySiteTemp = () => {
    let temp = "Unknown";
    if (siteTempComp) {
      siteTempComp.lastMeasurement.forEach(um => {
        if (um.type === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE) {
          let lastReading = UnitMeasurement.any(um, user);
          if (lastReading && lastReading.values[0] && lastReading.values[0].values[0]) {
            temp = lastReading.values[0].values[0].toFixed(2) + tempUnit();
          }
        }
      });
    }
    return (
      <React.Fragment>
        <TemperatureIcon heightWidth={20} />
        <Typography variant="body1" style={{ paddingLeft: 5, fontWeight: 650 }}>
          {temp}
        </Typography>
        {!siteTempUpToDate && (
          <Tooltip title="Component Not Reporting">
            <Warning style={{ color: "orange" }} />
          </Tooltip>
        )}
      </React.Fragment>
    );
  };

  const unlinkDialog = () => {
    return (
      <ResponsiveDialog
        open={unlinkDialogOpen}
        onClose={() => {
          setUnlinkDialogOpen(false);
        }}>
        <DialogTitle>Unlink Device</DialogTitle>
        <DialogContent>Remove device from heater?</DialogContent>
        <DialogActions>
          <Button>Cancel</Button>
          <Button
            variant="contained"
            color="primary"
            onClick={() => {
              disconnectDisplayDevice();
              setUnlinkDialogOpen(false);
            }}>
            Confirm
          </Button>
        </DialogActions>
      </ResponsiveDialog>
    );
  };

  const heaterInformation = () => {
    return (
      <Box marginBottom={2}>
        <Grid
          container
          direction="row"
          alignContent="center"
          justifyContent="space-between"
          alignItems="center">
          <Grid>
            <Box display="flex" alignItems="center">
              <img
                src={themeType === "light" ? HeaterDarkIcon : HeaterLightIcon}
                alt={"heaterIcon"}
                height={40}
                width={40}
              />
              <Typography variant="h6" style={{ paddingLeft: 10 }}>
                {heater.name}
              </Typography>
            </Box>
          </Grid>

          <Grid>
            <Button onClick={zoomOut} variant="outlined" style={{ marginRight: 5 }}>
              Zoom Out
            </Button>
            {!displayDevice ? (
              <Button
                onClick={() => {
                  setOpenLinkDrawer(true);
                }}
                variant="contained"
                color="primary">
                <Link />
                <Typography variant="caption" style={{ paddingLeft: 5 }}>
                  link device
                </Typography>
              </Button>
            ) : (
              <Button
                onClick={() => {
                  setUnlinkDialogOpen(true);
                }}
                variant="contained">
                <LinkOff />
                <Typography variant="caption" style={{ paddingLeft: 5 }}>
                  unlink device
                </Typography>
              </Button>
            )}
          </Grid>
        </Grid>
        <Box marginTop={2}>
          <Card style={{ width: "100%", padding: 10 }}>
            {displayDevice ? (
              <Grid
                container
                direction="row"
                alignContent="center"
                alignItems="center"
                spacing={4}>
                <Grid size={{
                    xs:6, sm:6, md:3 
                  }} 
                  container spacing={0}>
                  <Grid size={12}>
                    {displayHeaterState()}
                  </Grid>
                  <Grid size={12}>
                    <Typography variant="caption">Heater State:</Typography>
                  </Grid>
                </Grid>
                <Grid size={{ xs:6, sm:6, md:3}}>
                  <Grid container alignItems="center">
                    {displayHeaterTemp()}
                  </Grid>
                  <Grid container>
                    <Typography variant="caption">Heater Temperature:</Typography>
                  </Grid>
                </Grid>
                <Grid size={{ xs:6, sm:6, md:3}}>
                  <Grid container alignItems="center">
                    {displaySiteTemp()}
                  </Grid>
                  <Grid container>
                    <Typography variant="caption">Site Temperature:</Typography>
                  </Grid>
                </Grid>
                <Grid size={{ xs:6, sm:6, md:3}}>
                  <Grid container alignItems="center">
                    <FuelIcon />
                    <Typography variant="body1" style={{ paddingLeft: 5, fontWeight: 650 }}>
                      {fuelLevel}
                    </Typography>
                  </Grid>
                  <Grid container>
                    <Typography variant="caption">Fuel Level:</Typography>
                  </Grid>
                </Grid>
              </Grid>
            ) : (
              <Box>No Connected Devices Found</Box>
            )}
          </Card>
        </Box>
      </Box>
    );
  };

  const deviceInformation = () => {
    if (displayDevice) {
      if (loadingDevs) {
        return <Box>Loading Device</Box>;
      } else {
        return (
          <Box>
            <ObjectHeaterCharts
              device={displayDevice}
              linearMutations={displayedMutations}
              getFuelLevel={level => {
                setFuelLevel(level);
              }}
              xDomain={xDomain}
              changeXDomain={setXDomain}
            />
          </Box>
        );
      }
    } else {
      return (
        <Box>
          <Card style={{ height: 350 }}>
            <Box display="flex" justifyContent="center" alignItems="center" height={"100%"}>
              Connect Device To Heater to View Data
            </Box>
          </Card>
        </Box>
      );
    }
  };

  return (
    <PageContainer>
      {deviceDrawer()}
      {unlinkDialog()}
      <Box padding={2}>
        <ObjectControls
          actions={
            <ObjectHeaterActions
              componentsByDevice={componentsByDevice}
              heater={heater}
              permissions={permissions}
              refreshCallback={() => {}}
              immediateUpdate={newHeater => {
                setHeater(newHeater);
                setDisplayedMutations([...newHeater.settings.mutations]);
              }}
            />
          }
          permissions={permissions}
          devices={displayDevice && [Device.any(displayDevice.device)]}
        />
        {heaterInformation()}
        {deviceInformation()}
      </Box>
    </PageContainer>
  );
}
