import {
  Dialog,
  DialogContent,
  DialogTitle,
  Grid2 as Grid,
  IconButton,
  List,
  ListItem,
  ListItemIcon,
  ListItemText,
  Theme,
  Typography
} from "@mui/material";
import { makeStyles } from "@mui/styles";
import { AddCircleOutline as AddIconCircle } from "@mui/icons-material";
import SearchBar from "common/SearchBar";
import { Tag as TagUI } from "common/Tag";
import TagSettings from "common/TagSettings";
import { Device, Tag } from "models";
import { filterByTag } from "pbHelpers/Tag";
import { useDeviceAPI, useSnackbar, useTagAPI } from "providers";
import React, { useEffect, useRef, useState } from "react";
import { pond } from "protobuf-ts/pond";

const useStyles = makeStyles((_theme: Theme) => {
  return ({
    addIcon: {
      color: "var(--status-ok)"
    }
  })
});

interface AddDeviceTagProps {
  device: Device;
  deviceTags: pond.TagSettings[];
  addTagToDevice: (tag: Tag) => void;
}

function AddDeviceTag(props: AddDeviceTagProps) {
  const classes = useStyles();
  const tagAPI = useTagAPI();
  const { device, deviceTags, addTagToDevice } = props;
  const [open, setOpen] = useState(false);
  const [createNewOpen, setCreateNewOpen] = useState(false);
  const [searchValue, setSearchValue] = useState("");
  const [tags, setTags] = useState<Tag[]>([])

  const loadTags = () => {
    tagAPI.listTags().then(resp => {
      let newTags: Tag[] = [];
      resp.data.tags.forEach((tag: pond.Tag) => {
        newTags.push(Tag.create(tag))
      })
      setTags(newTags)
    })
  }

  useEffect(() => {
    loadTags()
  }, [])

  const tagItems = tags
    .filter(tag => !deviceTags.some(tagSettings => tagSettings.key === tag.settings.key))
    .filter(tag => filterByTag(searchValue, tag))
    .sort((a, b) => (a.name().toLowerCase() > b.name().toLowerCase() ? 1 : -1))
    .map((tag, index, array) => (
      <ListItem key={tag.settings.key} divider={index === array.length - 1}>
        <TagUI tag={tag} onClick={() => addTagToDevice(tag)} />
      </ListItem>
    ));
    
  return (
    <React.Fragment>
      <IconButton aria-label="add tag" onClick={() => setOpen(true)} size="small">
        <AddIconCircle className={classes.addIcon} />
      </IconButton>
      <Dialog open={open} onClose={() => setOpen(false)} scroll="paper">
        <DialogTitle>
          Select tags to add
          <Typography variant="body2" color="textSecondary">
            {device.name()}
          </Typography>
        </DialogTitle>
        <DialogContent>
          <SearchBar
            value={searchValue}
            onChange={(newSearchValue: string) => setSearchValue(newSearchValue)}
          />
          <List>
            {tagItems.length > 0 ? (
              <React.Fragment>{tagItems}</React.Fragment>
            ) : (
              <ListItem divider>
                <ListItemText secondary={"No tags found"} />
              </ListItem>
            )}
            <ListItem onClick={() => setCreateNewOpen(true)}>
              <ListItemIcon>
                <AddIconCircle className={classes.addIcon} />
              </ListItemIcon>
              <ListItemText>Create tag</ListItemText>
            </ListItem>
          </List>
        </DialogContent>
      </Dialog>
      <TagSettings open={createNewOpen} mode="add" onClose={() => {
        setCreateNewOpen(false)
        loadTags()
      }} />
    </React.Fragment>
  );
}

interface DeviceTagProps {
  tag: Tag;
  removeTagFromDevice: (tag: Tag) => void;
}

function DeviceTag(props: DeviceTagProps) {
  const { tag, removeTagFromDevice } = props;
  const [open, setOpen] = useState(false);

  return (
    <React.Fragment>
      <TagUI tag={tag} onClick={() => setOpen(true)} onDelete={() => removeTagFromDevice(tag)} />
      <TagSettings open={open} onClose={() => setOpen(false)} tag={tag} mode="update" />
    </React.Fragment>
  );
}

interface DeviceTagsProps {
  device: Device;
  disableAdd?: boolean;
}

export default function DeviceTags(props: DeviceTagsProps) {
  const { device, disableAdd } = props;
  // const [{ tags }] = useGlobalState();
  const tagAPI = useTagAPI()
  const [tags, setTags] = useState<Tag[]>([])
  // const [loading, setLoading] = useState(false)
  const deviceAPI = useDeviceAPI();
  const { error } = useSnackbar();
  const [deviceTags, setDeviceTags] = useState<pond.TagSettings[]>(device.status.tags);
  const previousDeviceRef = useRef(device);

  const loadTags = () => {
    // setLoading(true)
    tagAPI.listTags().then(resp => {
      let newTags: Tag[] = [];
      resp.data.tags.forEach((tag: pond.Tag) => {
        newTags.push(Tag.create(tag))
      })
      setTags(newTags)
    }).finally(() => {
      // setLoading(false)
    })
  }

  useEffect(() => {
    if (previousDeviceRef.current !== device) {
      setDeviceTags(device.status.tags);
      previousDeviceRef.current = device;
    }
  }, [device]);

  useEffect(() => {
    loadTags()
  }, [])

  const addTag = (tag: Tag) => {
    if (!deviceTags.some(dt => dt.key === tag.settings.key)) {
      deviceAPI
        .tag(device.id(), tag.settings.key)
        .then(() => {
          setDeviceTags([...deviceTags, tag.settings]);
        })
        .catch(() => error("Failed to tag device as " + tag.name));
    }
  };

  const removeTagFromDevice = (tag: Tag) => {
    if (deviceTags.some(dt => dt.key === tag.settings.key)) {
      deviceAPI
        .untag(device.id(), tag.key())
        .then(() => {
          setDeviceTags(deviceTags.filter(t => tag.key() !== t.key));
        })
        .catch(() => error("Failed to remove tag " + tag.name + " from device"));
    }
  };

  if (!device || !tags) {
    return null;
  }

  return (
    <React.Fragment>
      {deviceTags?.map(tagSettings => {
        let pondTag = pond.Tag.create({ settings: tagSettings })
        return (
          <Grid key={"device-tags-"+tagSettings.key}>
            <DeviceTag tag={Tag.create(pondTag)} removeTagFromDevice={removeTagFromDevice} />
          </Grid>
        );
      })}
      {disableAdd === undefined || disableAdd === false ? (
        <Grid>
          <AddDeviceTag device={device} deviceTags={deviceTags} addTagToDevice={addTag} />
        </Grid>
      ) : null}
    </React.Fragment>
  );
}
