import {
  Avatar,
  Button,
  DialogActions,
  DialogContent,
  DialogContentText,
  DialogTitle,
  List,
  ListItem,
  ListItemAvatar,
  ListItemIcon,
  ListItemText,
  ListSubheader,
  Typography,
  Theme
} from "@mui/material";
import { makeStyles } from "@mui/styles";
import { DragHandle as DragIcon } from "@mui/icons-material";
import ResponsiveDialog from "common/ResponsiveDialog";
import { useDeviceAPI, usePrevious, useSnackbar } from "hooks";
import { cloneDeep } from "lodash";
import { Component, Device } from "models";
import { sortComponents } from "pbHelpers/Component";
import { pond, quack } from "protobuf-ts/pond";
import { useEffect, useState } from "react";
import { DragDropContext, Draggable, Droppable, DropResult } from "react-beautiful-dnd";
import { useThemeType } from "../hooks/useThemeType";
import { useGlobalState } from "providers";

const useStyles = makeStyles((_theme: Theme) => {
    const themeType = useThemeType()
    return ({
    item: {
      backgroundColor: "rgba(0,0,0,0)",
      "&:hover": {
        backgroundColor:
          themeType === "light" ? "rgba(0,0,0,0.07)" : "rgba(255,255,255,0.07)"
      }
    }
  })}
);

interface ItemProps {
  component: Component;
  index: number;
}

function ComponentItem(props: ItemProps) {
  const classes = useStyles();
  const { component, index } = props;
  const name = component.name();
  return (
    <Draggable draggableId={component.key()} index={index}>
      {provided => (
        <ListItem
          ref={provided.innerRef}
          {...provided.draggableProps}
          {...provided.dragHandleProps}
          className={classes.item}>
          <ListItemIcon>
            <DragIcon />
          </ListItemIcon>
          <ListItemText primary={<Typography color="textPrimary">{name}</Typography>} />
          <ListItemAvatar>
            <Avatar alt={name}>{index + 1}</Avatar>
          </ListItemAvatar>
        </ListItem>
      )}
    </Draggable>
  );
}

interface ListProps {
  open: boolean;
  close: (refresh: boolean) => void;
  device: Device;
  components: Component[];
  devicePreferences: pond.DevicePreferences;
}

const filteredComponents = (components: Component[]) => {
  const componentBlacklist = [
    quack.ComponentType.COMPONENT_TYPE_POWER,
    quack.ComponentType.COMPONENT_TYPE_MODEM
  ];
  return components.filter(c => !componentBlacklist.includes(c.settings.type));
};

export default function ComponentOrder(props: ListProps) {
  const { open, close, device, devicePreferences } = props;
  const [{as}] = useGlobalState();
  const deviceAPI = useDeviceAPI();
  const { error, success } = useSnackbar();
  const prevDisplayOrder = usePrevious(devicePreferences.childDisplayOrder);
  const [components, setComponents] = useState<Component[]>(
    Array.from(filteredComponents(props.components).values()).sort((a, b: Component) =>
      sortComponents(a, b, devicePreferences.childDisplayOrder)
    )
  );

  useEffect(() => {
    if (
      props.components.length !== components.length ||
      prevDisplayOrder !== devicePreferences.childDisplayOrder
    ) {
      setComponents(
        Array.from(filteredComponents(props.components).values()).sort((a, b: Component) =>
          sortComponents(a, b, devicePreferences.childDisplayOrder)
        )
      );
    }
  }, [props.components, prevDisplayOrder, devicePreferences.childDisplayOrder, components.length]);

  const updateComponentOrder = (componentOrder: Component[]) => {
    let updatedPreferences = cloneDeep(devicePreferences);
    updatedPreferences.childDisplayOrder = componentOrder.map(c => c.locationString());
    deviceAPI
      .updatePreferences(device.id(), updatedPreferences, undefined, undefined, as)
      .then(() => success("Successfully update the component display order"))
      .catch(() => {
        error("Error occured while updating the component display order");
      })
      .finally(() => close(true));
  };

  const submit = () => {
    updateComponentOrder(components);
  };

  const reorder = (components: Component[], startIndex: number, endIndex: number): Component[] => {
    const result = Array.from(components);
    const [removed] = result.splice(startIndex, 1);
    result.splice(endIndex, 0, removed);
    return result;
  };

  const onDragEnd = (result: DropResult) => {
    if (!result.destination) {
      return;
    }
    setComponents(reorder(components, result.source.index, result.destination.index));
  };

  return (
    <ResponsiveDialog open={open} onClose={() => close(false)} maxWidth="sm" fullWidth>
      <DialogTitle id="component-order-dialog-title">
        Component Display Order
        <Typography variant="body2" color="textSecondary">
          {device.name()}
        </Typography>
      </DialogTitle>
      <DialogContent>
        {components.length < 1 ? (
          <DialogContentText>No components to reorder</DialogContentText>
        ) : (
          <DragDropContext onDragEnd={onDragEnd}>
            <Droppable droppableId="list">
              {provided => (
                <List
                  ref={provided.innerRef}
                  {...provided.droppableProps}
                  subheader={
                    <ListSubheader disableSticky>
                      <Typography variant="body2" color="textSecondary">
                        Drag and Drop to change the display order
                      </Typography>
                    </ListSubheader>
                  }>
                  {components.map((component, index) => (
                    <ComponentItem key={component.key()} component={component} index={index} />
                  ))}
                  {provided.placeholder}
                </List>
              )}
            </Droppable>
          </DragDropContext>
        )}
      </DialogContent>
      <DialogActions>
        <Button onClick={() => close(false)} color="primary">
          Close
        </Button>
        <Button onClick={submit} color="primary">
          Submit
        </Button>
      </DialogActions>
    </ResponsiveDialog>
  );
}
