import {
  Avatar,
  Box,
  Button,
  Checkbox,
  CircularProgress,
  DialogActions,
  DialogContent,
  DialogTitle,
  Divider,
  FormControl,
  FormControlLabel,
  FormGroup,
  FormLabel,
  Grid2,
  IconButton,
  List,
  ListItem,
  ListItemAvatar,
  ListItemSecondaryAction,
  ListItemText,
  PaletteColor,
  Stack,
  Theme,
  Tooltip,
  Typography,
  useTheme
} from "@mui/material";
import RemoveUserIcon from "@mui/icons-material/RemoveCircle";
import EditIcon from "@mui/icons-material/EditOutlined";
import ShareIcon from "@mui/icons-material/Share";
import ResponsiveDialog from "common/ResponsiveDialog";
import { usePermissionAPI, usePrevious, useSnackbar, useUserAPI } from "hooks";
import { cloneDeep, isEqual } from "lodash";
import { Scope, User } from "models";
import { sortUsersByRole, userRoleFromPermissions } from "pbHelpers/User";
import { pond } from "protobuf-ts/pond";
import React, { useCallback, useEffect, useState } from "react";
import { or } from "utils/types";
import RemoveSelfFromObject from "user/RemoveSelfFromObject";
import ShareObject from "user/ShareObject";
import { useGlobalState } from "providers";
import { red, blue } from "@mui/material/colors";
import { makeStyles } from "@mui/styles";

const useStyles = makeStyles((theme: Theme) => {
  const avatarBG = theme.palette.secondary["700" as keyof PaletteColor];
  return {
    dialogContent: {
      padding: `${theme.spacing(1)}px ${theme.spacing(2)}px`,
      [theme.breakpoints.up("sm")]: {
        padding: `${theme.spacing(1)}px ${theme.spacing(3)}px`
      }
    },
    userAvatar: {
      color: "#fff",
      backgroundColor: avatarBG
    },
    userPermissions: {
      paddingLeft: theme.spacing(4),
      paddingBottom: theme.spacing(1)
    },
    textOverflowEllipsis: {
      whiteSpace: "nowrap",
      overflow: "hidden",
      textOverflow: "ellipsis"
    },
    removeIcon: {
      color: red["500"],
      "&:hover": {
        color: red["600"]
      }
    },
    verticalSpacing: {
      paddingTop: theme.spacing(4),
      paddingBottom: theme.spacing(3)
    },
    lessSpacing: {
      paddingTop: theme.spacing(1),
      paddingBottom: theme.spacing(1)
    },
    iconButton: {
      margin: theme.spacing(1)
    },
    shareIcon: {
      color: blue["600"],
      "&:hover": {
        color: blue["700"]
      }
    },
    editIcon: {
      color: blue["300"],
      "&:hover": {
        color: blue["400"]
      }
    }
  };
});

interface Props {
  scope: Scope;
  label: string;
  permissions: pond.Permission[];
  isDialogOpen: boolean;
  closeDialogCallback: Function;
  refreshCallback: Function;
  userCallback?: (users?: User[] | undefined) => void;
  dialog?: string;
  cardMode?: boolean;
  useImitation?: boolean;
  shareLabel?: string; // s string to replace the share icon button with a labelled button
}

export default function ObjectUsers(props: Props) {
  const classes = useStyles();
  const theme = useTheme();
  const permissionAPI = usePermissionAPI();
  const [{ user, team }, dispatch] = useGlobalState();
  const isAdmin = user.hasAdmin();
  const userAPI = useUserAPI();
  const { error, success, warning } = useSnackbar();
  const {
    scope,
    label,
    permissions,
    isDialogOpen,
    closeDialogCallback,
    refreshCallback,
    userCallback,
    dialog,
    cardMode,
    //useImitation
    shareLabel
  } = props;
  const prevPermissions = usePrevious(permissions);
  const prevIsDialogOpen = usePrevious(isDialogOpen);
  const [initialUsers, setInitialUsers] = useState<User[]>([]);
  const [users, setUsers] = useState<User[]>([]);
  const [canManageUsers, setCanManageUsers] = useState<boolean>(
    permissions.includes(pond.Permission.PERMISSION_USERS)
  );
  const [canShare, setCanShare] = useState<boolean>(
    permissions.includes(pond.Permission.PERMISSION_SHARE)
  );
  const [canShareBilling, setCanShareBilling] = useState<boolean>(
    permissions.includes(pond.Permission.PERMISSION_SHARE) &&
      permissions.includes(pond.Permission.PERMISSION_USERS) &&
      permissions.includes(pond.Permission.PERMISSION_WRITE)
  );
  const [removedUsers, setRemovedUsers] = useState<string[]>([]);
  const [removeSelfDialogIsOpen, setRemoveSelfDialogIsOpen] = useState<boolean>(false);
  const [isLoading, setIsLoading] = useState<boolean>(false);
  const [isShareObjectDialogOpen, setIsShareObjectDialogOpen] = useState<boolean>(false);
  const [numOwners, setNumOwners] = useState(0);
  const [useImitation] = useState<boolean>(
    props.useImitation !== undefined ? props.useImitation : true
  );
  // const [editUserDialog, setEditUserDialog] = useState(false);
  const [userForDialog, setUserForDialog] = useState<User | undefined>(undefined);

  const setDefaultState = () => {
    setInitialUsers([]);
    setUsers([]);
    setRemovedUsers([]);
  };

  const load = useCallback(() => {
    setIsLoading(true);
    userAPI
      .listObjectUsers(scope)
      .then((response: any) => {
        let rUsers: User[] = [];
        or(response.data, { users: [] }).users.forEach((user: any) => {
          rUsers.push(User.any(user));
        });
        rUsers = rUsers.filter(u => u.permissions.length > 0);
        setInitialUsers(cloneDeep(rUsers));
        let numOwners = 0;
        rUsers.forEach(user => {
          if (userRoleFromPermissions(user.permissions) === "Owner") numOwners++;
        });
        setNumOwners(numOwners);
        setUsers(rUsers);
      })
      .catch((err: any) => {
        console.log(err);
        setInitialUsers([]);
        setUsers([]);
      })
      .finally(() => setIsLoading(false));
  }, [scope, userAPI]);

  useEffect(() => {
    if (!prevIsDialogOpen && isDialogOpen) {
      load();
    }
    if (prevPermissions !== permissions) {
      setCanManageUsers(permissions.includes(pond.Permission.PERMISSION_USERS));
      setCanShare(permissions.includes(pond.Permission.PERMISSION_SHARE));
      setCanShareBilling(
        permissions.includes(pond.Permission.PERMISSION_SHARE) &&
          permissions.includes(pond.Permission.PERMISSION_USERS) &&
          permissions.includes(pond.Permission.PERMISSION_WRITE)
      );
    }
  }, [isDialogOpen, load, permissions, prevIsDialogOpen, prevPermissions]);

  const closeRemoveSelfDialog = () => {
    setRemoveSelfDialogIsOpen(false);
  };

  const close = () => {
    closeRemoveSelfDialog();
    closeDialogCallback();
    setDefaultState();
  };

  const submit = () => {
    console.log(users);
    permissionAPI
      .updatePermissions(scope, users)
      .then((_response: any) => {
        success("Users were sucessfully updated for " + label);
        close();
        refreshCallback();
        userCallback && userCallback(users);
        users.forEach(u => {
          if (u.id() === user.id()) {
            if (scope.kind === "team" && scope.key === team.key()) {
              dispatch({ key: "userTeamPermissions", value: u.permissions });
            }
          }
        });
        if (cardMode) {
          load();
        }
      })
      .catch((err: any) => {
        err.response.data.error
          ? warning(err.response.data.error)
          : error("Error occured when updating users for " + label);
        close();
      })
      .finally(() => setUserForDialog(undefined));
  };

  const changeUserPermissions = (user: pond.IUser) => (event: any) => {
    let updatedUsers = cloneDeep(users);
    let permissionMapping = new Map<string, pond.Permission>([
      ["2", pond.Permission.PERMISSION_READ],
      ["3", pond.Permission.PERMISSION_WRITE],
      ["4", pond.Permission.PERMISSION_SHARE],
      ["1", pond.Permission.PERMISSION_USERS],
      ["5", pond.Permission.PERMISSION_BILLING],
      ["6", pond.Permission.PERMISSION_FILE_MANAGEMENT]
    ]);
    let permission = or(
      permissionMapping.get(event.target.value),
      pond.Permission.PERMISSION_INVALID
    );
    for (let i = 0; i < updatedUsers.length; i++) {
      let currUser = updatedUsers[i];
      if (
        user &&
        user.settings &&
        currUser &&
        currUser.settings &&
        currUser.settings.id === user.settings.id
      ) {
        let permissions = user.permissions ? cloneDeep(user.permissions) : [];
        if (permissions.includes(permission)) {
          permissions = permissions.filter(
            (curPermission: pond.Permission) => curPermission !== permission
          );
        } else {
          permissions.push(permission);
        }
        updatedUsers[i].permissions = permissions;
        user.permissions = permissions;
        break;
      }
    }
    setUsers(updatedUsers);
  };

  const removeUser = (user: string) => {
    let updatedUsers = cloneDeep(users);
    let updatedRemovedUsers = cloneDeep(removedUsers);
    for (let i = 0; i < updatedUsers.length; i++) {
      let currUser = updatedUsers[i];
      if (currUser && currUser.settings && currUser.settings.id === user) {
        updatedRemovedUsers.push(currUser.settings.id);
        updatedUsers[i].permissions = [];
        break;
      }
    }
    setUsers(updatedUsers);
    setRemovedUsers(updatedRemovedUsers);
  };

  const checkIsSelf = (checkUser: pond.IUser): boolean => {
    const id = checkUser && checkUser.settings ? checkUser.settings.id : "";
    return user.id() === id;
  };

  const userIsRemoved = (user: pond.IUser): boolean => {
    const id = user && user.settings ? user.settings.id : "";
    return removedUsers.includes(or(id, ""));
  };

  const objectUsersUnchanged = (): boolean => {
    return isEqual(initialUsers, users);
  };

  const openRemoveSelfDialog = () => {
    setRemoveSelfDialogIsOpen(true);
  };

  const openShareObjectDialog = () => {
    setIsShareObjectDialogOpen(true);
  };

  const closeShareObjectDialog = (shared: boolean | undefined) => {
    setIsShareObjectDialogOpen(false);
    if (shared) {
      load();
    }
  };

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UI BEGINS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  const title = () => {
    return (
      // <Grid container direction="column" justify="space-between">
      //   <Grid container direction="row" justify="space-between">
      //     <Grid item xs={8}>
      //       Users
      //       <Typography variant="body2" color="textSecondary">
      //         {label}
      //       </Typography>
      //     </Grid>
      //     <Grid item xs={4} container justify="flex-end">
      //       {canShare && (
      //         <Tooltip title={"Share " + label}>
      //           <IconButton
      //             aria-label="Share"
      //             className={classes.iconButton}
      //             onClick={openShareObjectDialog}>
      //             <ShareIcon className={classes.shareIcon} />
      //           </IconButton>
      //         </Tooltip>
      //       )}
      //     </Grid>
      //   </Grid>
      //   {dialog && (
      //     <Typography variant="body2" color="textSecondary" style={{ marginTop: "4px" }}>
      //       {dialog}
      //     </Typography>
      //   )}
      // </Grid>
      <Stack direction="column" spacing={2}>
        <Stack direction="row" spacing={2} justifyContent="space-between">
          <Box>
            Users
            <Typography variant="body2" color="textSecondary">
              {label}
            </Typography>
          </Box>
          <Box display="flex" justifyContent="flex-end">
            {canShare && (
              <Tooltip title={`Share ${label}`}>
                {shareLabel ?
                  <Button variant="contained" color="primary" aria-label="Share" onClick={openShareObjectDialog}>
                    {shareLabel}
                  </Button>
                :
                  <IconButton
                  aria-label="Share"
                  onClick={openShareObjectDialog}>
                    <ShareIcon />
                  </IconButton>
                }
              </Tooltip>
            )}
          </Box>
        </Stack>
      {dialog && (
        <Typography variant="body2" color="textSecondary" sx={{ mt: 1 }}>
          {dialog}
        </Typography>
      )}
    </Stack>
    );
  };

  const userPermissionManagement = (user: User) => {
    let permissions = user.permissions;
    if (
      !canManageUsers ||
      (checkIsSelf(user) && !canShareBilling) ||
      userIsRemoved(user) ||
      (permissions.includes(pond.Permission.PERMISSION_USERS) &&
        permissions.includes(pond.Permission.PERMISSION_READ) &&
        permissions.includes(pond.Permission.PERMISSION_WRITE) &&
        permissions.includes(pond.Permission.PERMISSION_SHARE) &&
        permissions.includes(pond.Permission.PERMISSION_BILLING) &&
        permissions.includes(pond.Permission.PERMISSION_FILE_MANAGEMENT) &&
        !cardMode)
    ) {
      return null;
    }
    const canRead = permissions.includes(pond.Permission.PERMISSION_READ);
    const xs = cardMode ? 12 : 4;
    const sm = cardMode ? 4 : 3;
    const justify = cardMode ? "flex-start" : "center";
    return (
      <FormControl component="fieldset" fullWidth className={classes.userPermissions}>
        <FormLabel component="legend"></FormLabel>
        <FormGroup>
          <Grid2 container direction="row" justifyContent={justify} spacing={2}>
            <Grid2 sx={{ gridColumn: `span ${xs} / span ${sm}` }}>
              <FormControlLabel
                control={
                  <Checkbox
                    checked={permissions.includes(pond.Permission.PERMISSION_READ)}
                    onChange={changeUserPermissions(user)}
                    value={pond.Permission.PERMISSION_READ}
                  />
                }
                label="View"
                labelPlacement="end"
              />
            </Grid2>
            
            <Grid2 sx={{ gridColumn: `span ${xs} / span ${sm}` }}>
              <FormControlLabel
                control={
                  <Checkbox
                    disabled={!canRead}
                    checked={permissions.includes(pond.Permission.PERMISSION_WRITE)}
                    onChange={changeUserPermissions(user)}
                    value={pond.Permission.PERMISSION_WRITE}
                  />
                }
                label="Edit"
                labelPlacement="end"
              />
            </Grid2>
            
            <Grid2 sx={{ gridColumn: `span ${xs} / span ${sm}` }}>
              <FormControlLabel
                control={
                  <Checkbox
                    disabled={!canRead}
                    checked={permissions.includes(pond.Permission.PERMISSION_SHARE)}
                    onChange={changeUserPermissions(user)}
                    value={pond.Permission.PERMISSION_SHARE}
                  />
                }
                label="Share"
                labelPlacement="end"
              />
            </Grid2>

            <Grid2 sx={{ gridColumn: `span ${xs} / span ${sm}` }}>
              <FormControlLabel
                control={
                  <Checkbox
                    disabled={!canRead}
                    checked={permissions.includes(pond.Permission.PERMISSION_USERS)}
                    onChange={changeUserPermissions(user)}
                    value={pond.Permission.PERMISSION_USERS}
                  />
                }
                label="Manage Users"
                labelPlacement="end"
              />
            </Grid2>

            <Grid2 sx={{ gridColumn: 'span 6 / span 3' }}>
              <FormControlLabel
                control={
                  <Checkbox
                    checked={permissions.includes(pond.Permission.PERMISSION_FILE_MANAGEMENT)}
                    onChange={changeUserPermissions(user)}
                    value={pond.Permission.PERMISSION_FILE_MANAGEMENT}
                  />
                }
                label="Files"
                labelPlacement="end"
              />
            </Grid2>

            <Grid2 sx={{ gridColumn: `span ${xs} / span ${sm}` }}>
              <FormControlLabel
                control={
                  <Checkbox
                    disabled={!canShareBilling}
                    checked={permissions.includes(pond.Permission.PERMISSION_BILLING)}
                    onChange={changeUserPermissions(user)}
                    value={pond.Permission.PERMISSION_BILLING}
                  />
                }
                label="Billing"
                labelPlacement="end"
              />
            </Grid2>
          </Grid2>
        </FormGroup>
      </FormControl>
    );
  };

  const userListItem = (user: User) => {
    let isSelf = checkIsSelf(user);
    let isRemoved = userIsRemoved(user);
    let name = user.name();
    let permissions = user.permissions;
    let userSettings = pond.UserSettings.create(user.settings !== null ? user.settings : undefined);

    return (
      <ListItem alignItems="flex-start" >
        <ListItemAvatar>
          <Avatar
            alt={name}
            src={
              userSettings.avatar && userSettings.avatar !== "" ? userSettings.avatar : undefined
            }
            className={classes.userAvatar}>
            {!(userSettings.avatar && userSettings.avatar !== "") && name}
          </Avatar>
        </ListItemAvatar>
        <ListItemText
          className={classes.textOverflowEllipsis}
          disableTypography
          primary={
            <Typography component="div" variant="body1" color="textPrimary">
              {name + (isSelf ? " (you)" : "")}
            </Typography>
          }
          secondary={
            <React.Fragment>
              <Typography component="div" variant="caption" color="textSecondary">
                {user.settings.email}
              </Typography>
              <Typography component="div" variant="caption" color="textSecondary">
                {isRemoved ? "REMOVED " : userRoleFromPermissions(permissions)}
              </Typography>
            </React.Fragment>
          }
        />
        {isSelf && !canShareBilling ? (
          <ListItemSecondaryAction>
            <Tooltip title={"Remove yourself from " + label}>
              <IconButton
                edge="end"
                aria-label={"Remove yourself from " + label}
                onClick={openRemoveSelfDialog}>
                <RemoveUserIcon className={classes.removeIcon} />
              </IconButton>
            </Tooltip>
          </ListItemSecondaryAction>
        ) : (canManageUsers || isAdmin) && !isRemoved && !cardMode ? (
          <ListItemSecondaryAction>
            <Tooltip title="Revoke user's access">
              <IconButton
                edge="end"
                aria-label="Remove user from device"
                onClick={() => removeUser(or(user.settings, { id: "" }).id)}>
                <RemoveUserIcon className={classes.removeIcon} />
              </IconButton>
            </Tooltip>
          </ListItemSecondaryAction>
        ) : (
          (canManageUsers || isAdmin) && (
            <ListItemSecondaryAction>
              <Tooltip title="Edit user's permissions">
                <IconButton
                  edge="end"
                  aria-label="Edit user permissions"
                  disabled={!canManageUsers}
                  onClick={() => setUserForDialog(user)}>
                  <EditIcon className={classes.editIcon} />
                </IconButton>
              </Tooltip>
            </ListItemSecondaryAction>
          )
        )}
      </ListItem>
    );
  };

  const objectUsersList = () => {
    const sortedUsers = sortUsersByRole(users);

    let userListItems: any = [];
    for (var i = 0; i < sortedUsers.length; i++) {
      let user = sortedUsers[i];
      if (user) {
        userListItems.push(
          <React.Fragment key={i}>
            {userListItem(user)}
            {!cardMode && userPermissionManagement(user)}
            <Divider />
          </React.Fragment>
        );
      }
    }

    return <List>{userListItems}</List>;
  };

  const content = () => {
    if (isLoading) {
      return (
        <Grid2 container justifyContent="center" alignContent="center" className={classes.verticalSpacing}>
          <CircularProgress />
        </Grid2>
      );
    }

    if (users.length < 1) {
      return (
        <React.Fragment>
          <Typography
            variant="h6"
            align="center"
            color="textSecondary"
            className={!cardMode ? classes.verticalSpacing : ""}
            style={{ padding: cardMode ? 0 : "" }}>
            No users found.
          </Typography>
        </React.Fragment>
      );
    }

    return objectUsersList();
  };

  const actions = () => {
    return (
      <Grid2 container direction="row" justifyContent="space-between">
        <Grid2 sx={{ gridColumn: `span ${4} ` }} ></Grid2>
        <Grid2 sx={{ gridColumn: `span ${8} ` }} container justifyContent="flex-end">
          {!cardMode && (
            <Button onClick={close} color="primary">
              Cancel
            </Button>
          )}
          {canManageUsers && !cardMode && (
            <Button onClick={submit} color="primary" disabled={objectUsersUnchanged()}>
              {cardMode ? "Update" : "Submit"}
            </Button>
          )}
        </Grid2>
      </Grid2>
    );
  };

  const dialogs = () => {
    return (
      <React.Fragment>
        <RemoveSelfFromObject
          scope={scope}
          label={label}
          isDialogOpen={removeSelfDialogIsOpen}
          closeDialogCallback={closeRemoveSelfDialog}
          numberOfOwners={numOwners}
        />
        <ShareObject
          scope={scope}
          label={label}
          permissions={permissions}
          isDialogOpen={isShareObjectDialogOpen}
          closeDialogCallback={closeShareObjectDialog}
          useImitation={useImitation}
          sharedUsers={users}
        />
      </React.Fragment>
    );
  };

  if (cardMode) {
    return (
      <React.Fragment>
        <Typography variant="h6">{title()}</Typography>
        <div style={{ overflowY: "scroll" }}>
          <Divider />
          {content()}
          {actions()}
          {dialogs()}
          <div style={{ marginTop: theme.spacing(2) }} />
        </div>
        <ResponsiveDialog
          open={userForDialog !== undefined}
          onClose={() => setUserForDialog(undefined)}>
          <DialogTitle>Change {userForDialog?.name()}'s Permissions?</DialogTitle>
          <DialogContent>{userForDialog && userPermissionManagement(userForDialog)}</DialogContent>
          <DialogActions>
            <Button onClick={() => setUserForDialog(undefined)}>Close</Button>
            <Button color="primary" onClick={submit}>
              Submit
            </Button>
          </DialogActions>
        </ResponsiveDialog>
      </React.Fragment>
    );
  }

  return (
    <ResponsiveDialog
      fullWidth
      maxWidth="xs"
      open={props.isDialogOpen}
      onClose={close}
      aria-labelledby="object-users-dialog">
      <DialogTitle id="object-users-title">{title()}</DialogTitle>
      <Divider />
      <DialogContent className={classes.dialogContent}>{content()}</DialogContent>
      <DialogActions>{actions()}</DialogActions>
      {dialogs()}
    </ResponsiveDialog>
  );
}
