import { Box, Grid2 as Grid, Theme, Typography } from "@mui/material";
import {makeStyles} from "@mui/styles"
//import { Theme } from "@material-ui/core/styles/createMuiTheme";
import Loader from "common/Loader";
import MapCard from "common/MapCard";
import { Coordinate } from "common/MapGL";
import ComponentCard from "component/ComponentCard";
import ComponentSettings from "component/ComponentSettings";
import DeviceOverview from "device/DeviceOverview";
import {
  useComponentAPI,
  useDeviceAPI,
  useInteractionsAPI,
  useSnackbar,
  useUsageAPI,
  useUserAPI
} from "hooks";
import { cloneDeep } from "lodash";
import { Component, Device as DeviceModel, deviceScope, Interaction } from "models";
import moment from "moment";
import { sameComponentID, sortComponents } from "pbHelpers/Component";
import {
  FindAvailablePositions,
  DeviceAvailabilityMap,
  OffsetAvailabilityMap
} from "pbHelpers/DeviceAvailability";
import { pond } from "protobuf-ts/pond";
import { quack } from "protobuf-ts/quack";
import { useGlobalState } from "providers";
import React, { useCallback, useEffect, useState } from "react";
//import { Redirect } from "react-router";
import { or } from "utils/types";
import DeviceActions from "./DeviceActions";

const useStyles = makeStyles((theme: Theme) => ({
    title: {
      whiteSpace: "nowrap",
      overflow: "hidden",
      textOverflow: "ellipsis"
    },
    noComponents: {
      marginTop: theme.spacing(16)
    },
    addComponentText: {
      ...theme.typography.subtitle2,
      color: theme.palette.primary.main,
      cursor: "pointer"
    },
    deviceLoading: {
      position: "relative",
      height: "100%",
      width: "100%",
      display: "flex",
      marginTop: theme.spacing(16),
      alignItems: "center",
      justifyContent: "center"
    },
    rateLimit: {
      marginTop: theme.spacing(1),
      color: theme.palette.text.secondary
    }
  })
);
interface Props {
  device: DeviceModel;
  isMobile?: boolean;
}

export default function Device(props: Props) {
  const classes = useStyles();
  const [{ user, as }] = useGlobalState();
  const { error } = useSnackbar();
  const deviceID = props.device.id();
  const deviceAPI = useDeviceAPI();
  const componentAPI = useComponentAPI();
  const interactionsAPI = useInteractionsAPI();
  const usageAPI = useUsageAPI();
  const userAPI = useUserAPI();
  const [isLoading, setIsLoading] = useState<boolean>(false);
  const [device, setDevice] = useState<DeviceModel>(DeviceModel.create());
  const [components, setComponents] = useState<Map<string, Component>>(new Map());
  const [interactions, setInteractions] = useState<Interaction[]>([]);
  const [permissions, setPermissions] = useState<pond.Permission[]>([]);
  const [preferences, setPreferences] = useState<pond.UserPreferences>(
    pond.UserPreferences.create()
  );
  const [invalidDevice, setInvalidDevice] = useState<boolean>(false);
  const [isAddComponentDialogOpen, setIsAddComponentDialogOpen] = useState<boolean>(false);
  const [availablePositions, setAvailablePositions] = useState<DeviceAvailabilityMap>(new Map());
  const [availableOffsets, setAvailableOffsets] = useState<OffsetAvailabilityMap>(new Map());
  const [latitude, setLatitude] = useState<number>(NaN);
  const [longitude, setLongitude] = useState<number>(NaN);
  const [cellularUsage, setCellularUsage] = useState<number>(0);
  const [cellularStatus, setCellularStatus] = useState<string>("");
  const rate = 30;

  const load = useCallback(() => {
    if (user.id() === "") return;
    setIsLoading(true);
    //let devicePromise = deviceAPI.get(deviceID);
    let userPromise = userAPI.getUser(user.id(), deviceScope(deviceID.toString()));
    let componentsPromise = componentAPI.list(
      deviceID,
      undefined,
      [deviceID.toString()],
      ["device"],
      true,
      as
    );
    let interactionsPromise = interactionsAPI.listInteractionsByDevice(deviceID, undefined, as);
    let groupPromise: Promise<any> = Promise.resolve(undefined);
    Promise.all([userPromise, componentsPromise, interactionsPromise, groupPromise])
      .then(([userRes, componentsRes, interactionsRes]) => {
        //let rDevice = DeviceModel.any(deviceRes.data);
        let rawComponents: Array<any> = or(componentsRes.data.components, []);
        let rComponents: Map<string, Component> = new Map();
        rawComponents.forEach((rawComponent: any) => {
          let component = Component.any(rawComponent);
          rComponents.set(component.key(), component);
        });

        setComponents(rComponents);

        setInteractions(interactionsRes);
        setPermissions(userRes.permissions);
        setPreferences(userRes.preferences);

        setDevice(props.device);
        setLongitude(props.device.status.longitude ? props.device.status.longitude : NaN);
        setLatitude(props.device.status.latitude ? props.device.status.latitude : NaN);
        let available = FindAvailablePositions(
          Array.from(rComponents.values()),
          props.device.settings.product
        );
        setAvailablePositions(available.GetAvailability());
        setAvailableOffsets(available.offsetAvailability);
      })
      .catch((err: any) => {
        setDevice(DeviceModel.create());
        setComponents(new Map());
        setInteractions([]);
        setAvailablePositions(new Map());
        setAvailableOffsets(new Map());
        setPermissions([]);
        setPreferences(pond.UserPreferences.create());
        setInvalidDevice(true);
        error(err);
      })
      .finally(() => setIsLoading(false));
    usageAPI
      .getUsage(deviceID, moment().subtract(1, "days"))
      .then((res: any) => {
        let usage = res.data;
        if (usage) {
          let rCellularStatus = "active";
          let rCellularUsage = 0;
          let sessions: any[] = [];
          if (usage.sessions) {
            sessions = usage.sessions.filter((session: any) => {
              return moment(session.begin).isAfter(moment().subtract(1, "days"));
            });
          }
          sessions.forEach((session: any) => (rCellularUsage += Number(or(session.bytes, 0))));
          setCellularStatus(rCellularStatus);
          setCellularUsage(rCellularUsage);
        }
      })
      .catch(err => {});
  }, [componentAPI, props.device, deviceID, error, interactionsAPI, /*usageAPI*/, userAPI, user, as]);

  useEffect(() => {
    load();
  }, [load]);

  const getOrderedComponents = () => {
    if (preferences.childDisplayOrder && preferences.childDisplayOrder.length === Array.from(components.values()).length) {
      return Array.from(components.values()).sort((a, b: Component) =>
        sortComponents(a, b, preferences.childDisplayOrder)
      );
    } else {
      return Array.from(components.values());
    }
  };

  const displayedComponentsBlacklist = [
    quack.ComponentType.COMPONENT_TYPE_POWER,
    quack.ComponentType.COMPONENT_TYPE_MODEM
  ];

  const displayedComponents = () => {
    return [...components.values()].filter(
      c => !displayedComponentsBlacklist.includes(c.settings.type)
    );
  };

  const handleComponentChanged = (component: Component) => {
    let updatedComponents = cloneDeep(components);
    let updatedComponent = cloneDeep(component);
    if (updatedComponents.has(updatedComponent.key())) {
      updatedComponents.set(updatedComponent.key(), updatedComponent);
      setComponents(updatedComponents);
    }
  };

  // const productCard = () => {
  //   return <DeviceProductCard device={device} components={components} />;
  // };

  const componentCards = () => {
    let orderedComponents = getOrderedComponents();
    let componentCards = [];
    let hasGPS = false;
    for (let i = 0; i < orderedComponents.length; i++) {
      let c = orderedComponents[i];
      if (displayedComponentsBlacklist.includes(c.settings.type)) {
        continue;
      }
      let id: quack.IComponentID = quack.ComponentID.fromObject({
        type: c.settings.type,
        addressType: c.settings.addressType,
        address: c.settings.address
      });
      let filteredInteractions = interactions.filter(interaction => {
        let isSource = false;
        let isSink = false;
        if (interaction.settings) {
          isSource = sameComponentID(interaction.settings.source, id);
          isSink = sameComponentID(interaction.settings.sink, id);
        }
        return isSource || isSink;
      });
      if (id.type === quack.ComponentType.COMPONENT_TYPE_GPS) {
        hasGPS = true;
      }
      componentCards.push(
        <ComponentCard
          device={device}
          availablePositions={availablePositions}
          availableOffsets={availableOffsets}
          component={c}
          components={orderedComponents}
          interactions={filteredInteractions}
          permissions={permissions}
          key={i}
          showMobile={props.isMobile}
          refreshCallback={(updatedComponent?: Component) =>
            updatedComponent ? handleComponentChanged(updatedComponent) : load()
          }
        />
      );
    }
    if (!hasGPS && !isNaN(latitude) && !isNaN(longitude)) {
      let path: Array<Coordinate> = [];
      path.push({ latitude: latitude, longitude: longitude, timestamp: "" });
      componentCards.push(<MapCard key={"map"} path={path} />);
    }

    return componentCards;
  };

  const getUsage = () => {
    let usage = undefined;
    if (cellularStatus && cellularStatus !== "") {
      usage = { status: cellularStatus, bytes: cellularUsage };
    }
    return usage;
  };

  const anyComponentsReportingFast = () => {
    let fast = false;
    components.forEach(c => {
      if (
        c.settings.measurementPeriodMs > 0 &&
        c.settings.reportPeriodMs > 0 &&
        c.settings.reportPeriodMs < rate * 1000
      ) {
        fast = true;
      }
    });
    return fast;
  };

  const componentsTab = () => {
    return (
      <Box padding={1} marginTop={1}>
        {anyComponentsReportingFast() && (
          <Typography className={classes.rateLimit} variant="caption">
            Live updates are rate limited to {rate}s to ensure a smooth browsing experience
          </Typography>
        )}

        {isLoading ? (
          <span className={classes.deviceLoading}>
            <Loader />
          </span>
        ) : displayedComponents().length > 0 ? (
          <Grid container direction="row" spacing={2}>
            {componentCards()}
          </Grid>
        ) : (
          <Box marginTop={4}>
            <Typography variant="subtitle1" align="center" color="textSecondary">
              No components found
            </Typography>
            {permissions.includes(pond.Permission.PERMISSION_WRITE) && (
              <React.Fragment>
                <Typography variant="body2" align="center" color="textSecondary">
                  <span
                    className={classes.addComponentText}
                    onClick={() => setIsAddComponentDialogOpen(true)}>
                    {" Add "}
                  </span>
                  a component to {device.name()}.
                </Typography>
                <ComponentSettings
                  mode="add"
                  device={device}
                  isDialogOpen={or(isAddComponentDialogOpen, false)}
                  closeDialogCallback={() => setIsAddComponentDialogOpen(false)}
                  availablePositions={availablePositions}
                  availableOffsets={availableOffsets}
                  refreshCallback={load}
                  canEdit={permissions.includes(pond.Permission.PERMISSION_WRITE)}
                />
              </React.Fragment>
            )}
          </Box>
        )}
      </Box>
    );
  };

  const isPaused = () => {
    let paused = false;
    if (cellularStatus && cellularStatus !== "") {
      if (cellularStatus.toLowerCase().includes("pause")) {
        paused = true;
      }
    }
    return paused;
  };

  const toggleNotificationPreference = () => {
    let updatedPreferences = cloneDeep(preferences);
    updatedPreferences.notify = !preferences.notify;
    deviceAPI
      .updatePreferences(deviceID, updatedPreferences, undefined, undefined, as)
      .then(() => setPreferences(updatedPreferences))
      .catch(() => {
        error(
          "Error occured while " +
            (preferences.notify ? "enabling" : "disabling") +
            " notifications"
        );
      });
  };

  // if (invalidDevice) {
  //   return <Redirect to="/404" />;
  // } else {
    return (
      <Box padding={1}>
        <Grid container direction="row" alignItems="center">
          <Grid style={{ order: 5 }}>
            <DeviceActions
              device={device}
              isPaused={isPaused()}
              components={getOrderedComponents()}
              interactions={interactions}
              availablePositions={availablePositions}
              availableOffsets={availableOffsets}
              permissions={permissions}
              refreshCallback={load}
              preferences={preferences}
              toggleNotificationPreference={toggleNotificationPreference}
              isLoading={isLoading}
            />
          </Grid>
          <Grid style={{ order: 4 }}>
            <DeviceOverview
              device={device}
              components={[...components.values()]}
              usage={getUsage()}
              loading={isLoading}
            />
          </Grid>
        </Grid>
        {componentsTab()}
      </Box>
    );
  //}
}
