import {
  Avatar,
  AppBar,
  Box,
  CircularProgress,
  IconButton,
  List,
  ListItem,
  ListItemText,
  ListItemAvatar,
  Toolbar,
  Tooltip,
  Typography,
  ToggleButtonGroup,
  ToggleButton
} from "@mui/material";
import {Close as CloseIcon} from "@mui/icons-material";
// import { ToggleButton, ToggleButtonGroup } from "@mui";
import ResponsiveDialog from "common/ResponsiveDialog";
import SearchBar from "common/SearchBar";
import { or } from "utils";
import DeviceOverview from "device/DeviceOverview";
import GroupOverview from "group/GroupOverview";
import { useMobile, usePrevious, useSnackbar } from "hooks";
import { Device, Group, Team, newScope } from "models";
import { pond } from "protobuf-ts/pond";
import {
  useDeviceAPI,
  useGlobalState,
  useGroupAPI,
  usePermissionAPI,
  useTeamAPI,
  useUserAPI
} from "providers";
import { useCallback, useEffect, useState } from "react";
import InfiniteScroll from "react-infinite-scroller";
// import { useHistory } from "react-router";
import { capitalize } from "utils/strings";
import { SupervisedUserCircle as TeamIcon } from "@mui/icons-material";
import { useNavigate } from "react-router-dom";

interface Props {
  isOpen: boolean;
  closeDialogCallback: () => void;
}

const REQUEST_LIMIT = 25;

export default function AccessObject(props: Props) {
  const [{ user, as }, dispatch] = useGlobalState();
  // const history = useHistory();
  const navigate = useNavigate()
  const isMobile = useMobile();
  const { error, info } = useSnackbar();
  const permissionAPI = usePermissionAPI();
  const deviceAPI = useDeviceAPI();
  const groupAPI = useGroupAPI();
  const teamAPI = useTeamAPI();
  const userAPI = useUserAPI();
  const { isOpen, closeDialogCallback } = props;
  const prevOpen = usePrevious(isOpen);
  const [kind, setKind] = useState<string>("device");
  const prevKind = usePrevious(kind);
  const [searchValue, setSearchValue] = useState<string>("");
  const prevSearchValue = usePrevious(searchValue);
  const [loading, setLoading] = useState<boolean>(false);
  const [devices, setDevices] = useState<Device[]>([]);
  const [groups, setGroups] = useState<Group[]>([]);
  const [teams, setTeams] = useState<Team[]>([]);
  const [groupsOffset, setGroupsOffset] = useState<number>(0);
  const [groupsTotal, setGroupsTotal] = useState<number>(0);
  const [teamsOffset, setTeamsOffset] = useState<number>(0);
  const [teamsTotal, setTeamsTotal] = useState<number>(0);
  const [devicesOffset, setDevicesOffset] = useState<number>(0);
  const [devicesTotal, setDevicesTotal] = useState<number>(0);
  const [prevAs, setPrevAs] = useState("")


  const close = (restoreAs?: boolean) => {
    if(restoreAs){
      dispatch({ key: "as", value: prevAs });
    }
    closeDialogCallback();
  };

  const getAccess = (key: number | string, name: string, url: string, useImitation = false) => {
    user.settings.useTeam = false;
    //dispatch({ key: "as", value: "" });
    userAPI.updateUser(user.id(), user.protobuf()).then(() => {
      info("Will no longer view as team by default");
      permissionAPI
        .shareObject(
          newScope(kind, key.toString()),
          user.settings.email,
          [
            pond.Permission.PERMISSION_USERS,
            pond.Permission.PERMISSION_READ,
            pond.Permission.PERMISSION_WRITE,
            pond.Permission.PERMISSION_SHARE
          ],
          useImitation
        )
        .then((_response: any) => {
          // history.push(url);
          navigate(url)
          close();
        })
        .catch((_err: any) => {
          error("Failed to grant you access to " + name);
        });
    });
  };

  const load = useCallback(() => {
    setLoading(true);

    kind === "group"
      ? groupAPI
          .listGroups(REQUEST_LIMIT, 0, "asc", undefined, searchValue, true)
          .then((response: any) => {
            let rGroupsOffset: number = response.data.nextOffset ? response.data.nextOffset : 0;
            let rGroupsTotal: number = response.data.total ? response.data.total : 0;
            let groups: Group[] = or(response.data.groups, []).map((g: any) => {
              return Group.create(pond.Group.fromObject(g));
            });
            setGroups(groups);
            setGroupsOffset(rGroupsOffset);
            setGroupsTotal(rGroupsTotal);
          })
          .catch((_error: any) => {
            setGroups([]);
            setGroupsOffset(0);
            setGroupsTotal(0);
          })
          .finally(() => setLoading(false))
      : kind === "team"
      ? teamAPI
          .listTeams(REQUEST_LIMIT, 0, "asc", undefined, searchValue, true)
          .then(response => {
            let rTeamsOffset: number = response.data.nextOffset ? response.data.nextOffset : 0;
            let rTeamsTotal: number = response.data.total ? response.data.total : 0;
            let teams: Team[] = or(response.data.teams, []).map((g: any) => {
              return Team.create(pond.Team.fromObject(g));
            });
            setTeams(teams);
            setTeamsOffset(rTeamsOffset);
            setTeamsTotal(rTeamsTotal);
          })
          .finally(() => setLoading(false))
      : deviceAPI
          .list(
            REQUEST_LIMIT,
            0,
            "asc",
            undefined,
            searchValue,
            undefined,
            undefined,
            undefined,
            true,
            undefined,
            undefined,
            undefined,
            undefined,
            undefined,
            undefined,
            as
          )
          .then((response: any) => {
            let rDevicesOffset: number = response.data.nextOffset ? response.data.nextOffset : 0;
            let rDevicesTotal: number = response.data.total ? response.data.total : 0;
            let devices: Device[] = or(response.data.devices, []).map((d: any) => {
              return Device.create(pond.Device.fromObject(d));
            });
            setDevices(devices);
            setDevicesTotal(rDevicesTotal);
            setDevicesOffset(rDevicesOffset);
          })
          .catch((_error: any) => {
            setDevices([]);
            setDevicesOffset(0);
            setDevicesTotal(0);
          })
          .finally(() => setLoading(false));
  }, [deviceAPI, groupAPI, teamAPI, kind, searchValue, as]);

  const loadMoreDevices = useCallback(() => {
    deviceAPI
      .list(
        REQUEST_LIMIT,
        devicesOffset,
        "asc",
        undefined,
        searchValue,
        undefined,
        undefined,
        undefined,
        true,
        undefined,
        undefined,
        undefined,
        undefined,
        undefined,
        undefined,
        as
      )
      .then((response: any) => {
        let rDevicesOffset: number = response.data.nextOffset ? response.data.nextOffset : 0;
        let rDevicesTotal: number = response.data.total ? response.data.total : 0;
        let moreDevices: Device[] = or(response.data.devices, []).map((d: any) => {
          return Device.create(pond.Device.fromObject(d));
        });
        setDevices([...new Set([...devices, ...moreDevices])]);
        setDevicesTotal(rDevicesTotal);
        setDevicesOffset(rDevicesOffset);
      })
      .catch((_error: any) => {
        setDevices([]);
        setDevicesOffset(0);
        setDevicesTotal(0);
      });
  }, [deviceAPI, devicesOffset, searchValue, devices, as]);

  const loadMoreGroups = useCallback(() => {
    groupAPI
      .listGroups(REQUEST_LIMIT, groupsOffset, "asc", undefined, searchValue, true)
      .then((response: any) => {
        let rGroupsOffset: number = response.data.nextOffset ? response.data.nextOffset : 0;
        let rGroupsTotal: number = response.data.total ? response.data.total : 0;
        let moreGroups: Group[] = or(response.data.groups, []).map((g: any) => {
          return Group.create(pond.Group.fromObject(g));
        });
        setGroups([...new Set([...groups, ...moreGroups])]);
        setGroupsOffset(rGroupsOffset);
        setGroupsTotal(rGroupsTotal);
      })
      .catch((_error: any) => {
        setGroups([]);
        setGroupsOffset(0);
        setGroupsTotal(0);
      });
  }, [groupAPI, groupsOffset, searchValue, groups]);

  const loadMoreTeams = useCallback(() => {
    teamAPI
      .listTeams(REQUEST_LIMIT, teamsOffset, "asc", undefined, searchValue, true)
      .then((response: any) => {
        let rTeamsOffset: number = response.data.nextOffset ? response.data.nextOffset : 0;
        let rTeamsTotal: number = response.data.total ? response.data.total : 0;
        let moreTeams: Team[] = or(response.data.teams, []).map((g: any) => {
          return Team.create(pond.Team.fromObject(g));
        });
        setTeams([...new Set([...teams, ...moreTeams])]);
        setTeamsOffset(rTeamsOffset);
        setTeamsTotal(rTeamsTotal);
      })
      .catch((_error: any) => {
        setTeams([]);
        setTeamsOffset(0);
        setTeamsTotal(0);
      });
  }, [teamAPI, teamsOffset, searchValue, teams]);

  useEffect(() => {
    if (isOpen && (prevSearchValue !== searchValue || prevKind !== kind || isOpen !== prevOpen)) {
      //if the user is viewing as a team record who it was
      if(as){
        setPrevAs(as)
      }
      //change as so the user is viewing as themself
      dispatch({ key: "as", value: "" });
      load();
    }
  }, [
    prevSearchValue,
    searchValue,
    load,
    kind,
    prevKind,
    loadMoreDevices,
    loadMoreGroups,
    isOpen,
    prevOpen
  ]);

  const groupURL = (group: Group) => {
    return "/groups/" + group.id().toString();
  };

  const groupItem = (g: Group) => {
    return (
      <ListItem
        key={g.id()}
        divider
        onClick={() => getAccess(g.id(), g.name(), groupURL(g))}>
        <ListItemText
          primary={
            <Tooltip title={"ID: " + g.id()}>
              <span>{g.name()}</span>
            </Tooltip>
          }
          secondaryTypographyProps={{ component: "span" }}
          secondary={<GroupOverview group={g} variant="row" />}
        />
      </ListItem>
    );
  };

  const deviceURL = (device: Device) => {
    return "/devices/" + device.id().toString();
  };

  const teamURL = (team: Team) => {
    return "/teams/" + team.key();
  };

  const deviceItem = (d: Device) => {
    return (
      <ListItem
        key={d.id()}
        divider
        onClick={() => getAccess(d.id(), d.name(), deviceURL(d))}>
        <ListItemText
          primary={
            <Tooltip title={"ID: " + d.id()}>
              <span>{d.name()}</span>
            </Tooltip>
          }
          secondaryTypographyProps={{ component: "span" }}
          secondary={<DeviceOverview device={d} components={[]} disableAddTag />}
        />
      </ListItem>
    );
  };

  const teamItem = (d: Team) => {
    return (
      <ListItem
        key={d.key()}
        divider
        onClick={() => getAccess(d.key(), d.name(), teamURL(d), false)}>
        <ListItemAvatar>
          <Avatar src={d.settings.avatar}>
            {/* Draw the team icon id src icon errors */}
            <TeamIcon style={{ fontSize: 38 }} />
          </Avatar>
        </ListItemAvatar>
        <ListItemText
          primary={
            <Tooltip title={"Key: " + d.key()}>
              <span>{d.name()}</span>
            </Tooltip>
          }
          secondaryTypographyProps={{ component: "span" }}
          secondary={d.settings.info}
        />
      </ListItem>
    );
  };

  return (
    <ResponsiveDialog
      open={isOpen}
      onClose={() => close(true)}
      aria-labelledby="access-object-dialog"
      fullScreen>
      <AppBar position="sticky">
        <Toolbar>
          <IconButton edge="start" color="inherit" onClick={() => close(true)} aria-label="close">
            <CloseIcon />
          </IconButton>
          <Typography variant="h6" noWrap>
            Access {capitalize(kind)}
          </Typography>
          <Box flex={1} display="flex" justifyContent="flex-end" marginLeft={2}>
            <ToggleButtonGroup
              value={kind}
              size="small"
              exclusive
              onChange={(_, newKind) => newKind !== null && setKind(newKind)}
              aria-label="object-kind">
              <ToggleButton value="device" aria-label="device-kind">
                Devices
              </ToggleButton>
              <ToggleButton value="group" aria-label="group-kind">
                Groups
              </ToggleButton>
              <ToggleButton value="team" aria-label="team-kind">
                Teams
              </ToggleButton>
            </ToggleButtonGroup>
          </Box>
          {!isMobile && (
            <Box marginLeft={2}>
              <SearchBar value={searchValue} onChange={value => setSearchValue(value)} />
            </Box>
          )}
        </Toolbar>
        {isMobile && (
          <Toolbar>
            <Box flex={1} display="flex" justifyContent="flex-end">
              <SearchBar value={searchValue} onChange={value => setSearchValue(value)} />
            </Box>
          </Toolbar>
        )}
      </AppBar>

      {loading ? (
        <Box padding={4} height="100%" display="flex" justifyContent="center" alignItems="center">
          <CircularProgress />
        </Box>
      ) : kind === "group" ? (
        <InfiniteScroll
          pageStart={0}
          loadMore={() => loadMoreGroups()}
          hasMore={groups.length < groupsTotal}
          useWindow={false}
          loader={
            <Box
              display="flex"
              justifyContent="center"
              alignContent="center"
              marginTop={2}
              key="load-devices">
              <CircularProgress />
            </Box>
          }>
          <List style={{ height: "100%", minHeight: "250px" }}>
            {groups.length === 0 ? (
              <ListItem style={{ justifyContent: "center", paddingTop: "16px" }}>
                <Typography color="textSecondary" variant="h5">
                  No groups found
                </Typography>
              </ListItem>
            ) : (
              groups.map(g => groupItem(g))
            )}
          </List>
        </InfiniteScroll>
      ) : kind === "team" ? (
        <InfiniteScroll
          pageStart={0}
          loadMore={() => loadMoreTeams()}
          hasMore={teams.length < teamsTotal}
          useWindow={false}
          loader={
            <Box
              display="flex"
              justifyContent="center"
              alignContent="center"
              marginTop={2}
              key="load-devices">
              <CircularProgress />
            </Box>
          }>
          <List style={{ height: "100%", minHeight: "250px" }}>
            {teams.length === 0 ? (
              <ListItem style={{ justifyContent: "center", paddingTop: "16px" }}>
                <Typography color="textSecondary" variant="h5">
                  No teams found
                </Typography>
              </ListItem>
            ) : (
              teams.map(t => teamItem(t))
            )}
          </List>
        </InfiniteScroll>
      ) : (
        <InfiniteScroll
          pageStart={0}
          loadMore={() => loadMoreDevices()}
          hasMore={devices.length < devicesTotal}
          useWindow={false}
          loader={
            <Box
              display="flex"
              justifyContent="center"
              alignContent="center"
              marginTop={2}
              key="load-devices">
              <CircularProgress />
            </Box>
          }>
          <List style={{ height: "100%", minHeight: "250px" }}>
            {devices.length === 0 ? (
              <ListItem style={{ justifyContent: "center", paddingTop: "16px" }}>
                <Typography color="textSecondary" variant="h5">
                  No devices found
                </Typography>
              </ListItem>
            ) : (
              devices.map(d => deviceItem(d))
            )}
          </List>
        </InfiniteScroll>
      )}
    </ResponsiveDialog>
  );
}
