import {
  Button,
  Checkbox,
  CircularProgress,
  DialogActions,
  DialogContent,
  DialogTitle,
  Divider,
  FormControl,
  FormControlLabel,
  FormGroup,
  FormLabel,
  IconButton,
  Link,
  List,
  ListItem,
  ListItemSecondaryAction,
  ListItemText,
  Tab,
  Tabs,
  TextField,
  Theme,
  Tooltip,
  Typography
} from "@mui/material";
import { green, red } from "@mui/material/colors";
import {
  Add as AddIcon,
  Link as LinkIcon,
  LinkOff,
  RemoveCircle as RemoveCircleIcon
} from "@mui/icons-material";
import { MobileDateTimePicker } from '@mui/x-date-pickers';
import ResponsiveDialog from "common/ResponsiveDialog";
import { usePermissionAPI, usePrevious, useSnackbar } from "hooks";
import { cloneDeep } from "lodash";
import { binScope, Scope, ShareableLink, User } from "models";
import moment, { Moment } from "moment";
import { pond } from "protobuf-ts/pond";
import React, { useCallback, useEffect, useState } from "react";
import { openSnackbar } from "providers/Snackbar";
import { useBinAPI, useGateAPI } from "providers";
import { makeStyles } from "@mui/styles";

const useStyles = makeStyles((theme: Theme) => ({
  dialogContent: {
    marginTop: theme.spacing(2)
  },
  formContainer: {
    display: "flex",
    flexWrap: "wrap"
  },
  formControl: {
    margin: theme.spacing(1),
    minWidth: 160,
    width: "100%"
  },
  emailForm: {
    paddingBottom: theme.spacing(2)
  },
  fabContainer: {
    position: "relative",
    minHeight: "64px"
  },
  addIcon: {
    color: green[500],
    "&:hover": {
      color: green[600]
    }
  },
  removeIcon: {
    color: red[500],
    "&:hover": {
      color: red[600]
    }
  },
  grey: {
    color: theme.palette.text.secondary
  }
}));

interface Props {
  scope: Scope;
  label: string;
  permissions: pond.Permission[];
  isDialogOpen: boolean;
  closeDialogCallback: Function;
  useImitation?: boolean;
  sharedUsers?: User[]; //including the users that the object/team is already shared to will prevent users from sharing again to an existing user
}

export default function ShareObject(props: Props) {
  const binAPI = useBinAPI();
  const classes = useStyles();
  const permissionAPI = usePermissionAPI();
  const { info, success, error } = useSnackbar();
  const { scope, label, permissions, isDialogOpen, closeDialogCallback, sharedUsers } = props;
  const [email, setEmail] = useState<string>("");
  const [sharedPermissions, setSharedPermissions] = useState<pond.Permission[]>([
    pond.Permission.PERMISSION_READ
  ]);
  const [tab, setTab] = useState<number>(0);
  const prevTab = usePrevious(tab);
  const [isLoadingLinks, setIsLoadingLinks] = useState<boolean>(false);
  const [shareableLinks, setShareableLinks] = useState<ShareableLink[]>([]);
  const [newLinkExpiration, setNewLinkExpiration] = useState<Moment>(moment().add(1, "week"));
  const [isNewLinkInfinite, setIsNewLinkInfinite] = useState<boolean>(false);
  const [openExistingUser, setOpenExistingUser] = useState(false);
  const [useImitation] = useState<boolean>(
    props.useImitation !== undefined ? props.useImitation : true
  );
  const gateAPI = useGateAPI();

  const share = () => {
    permissionAPI
      .shareObject(scope, email, sharedPermissions, useImitation)
      .then((result: any) => {
        let shareBins = true;
        if (result && result.data && result.data.existing) {
          success(label + " was shared with " + email);
        } else {
          success(email + " was sent an email to sign up");
          shareBins = false;
        }
        let successBins = true;
        if (scope.kind === "binyard" && shareBins) {
          binAPI.listBins(1000, 0, "asc", "name", scope.key).then(resp => {
            resp.data.bins.forEach(bin => {
              if (bin.settings) {
                let newScope = binScope(bin.settings?.key);
                permissionAPI.shareObject(newScope, email, sharedPermissions).catch(_err => {
                  successBins = false;
                });
              }
            });
          });
          if (!successBins) {
            openSnackbar("error", "One or more bins failed to share with " + email);
          } else {
            success("Bins shared with " + email);

          }
        }
        let successGates = true;
        if (scope.kind === "terminal" && shareBins) {
          gateAPI
            .listGates(
              1000,
              0,
              undefined,
              undefined,
              undefined,
              undefined,
              [scope.key],
              [scope.kind]
            )
            .then(resp => {
              resp.data.gates.forEach(gate => {
                if (gate) {
                  let newScope = { key: gate.key, kind: "gate" } as Scope;
                  permissionAPI.shareObject(newScope, email, sharedPermissions).catch(_err => {
                    successGates = false;
                  });
                }
              });
            });
          if (!successGates) {
            openSnackbar("error", "One or more Gates failed to share");
          } else {
            success("Gates shared");
          }
        }
        closeAfterShare();
      })
      .catch((_err: any) => {
        error("Unable to share " + label + " with " + email);
        close();
      });
  };

  const loadShareableLinks = useCallback(() => {
    setIsLoadingLinks(true);
    permissionAPI
      .listShareableLinks(scope)
      .then((response: any) => {
        let rawShareableLinks = response.data.links ? response.data.links : [];
        let rShareableLinks = rawShareableLinks.map((rawShareableLink: any) =>
          ShareableLink.any(rawShareableLink)
        );
        setShareableLinks(rShareableLinks);
      })
      .catch((_error: any) => {
        setShareableLinks([]);
        console.log("Error occured while loading shareable links");
      })
      .finally(() => {
        setIsLoadingLinks(false);
      });
  }, [permissionAPI, scope]);

  useEffect(() => {
    if (tab === 1 && prevTab !== tab) {
      loadShareableLinks();
    }
  }, [loadShareableLinks, prevTab, tab]);

  const getNeverExpires = () => {
    return "";
  };

  const createShareableLink = () => {
    const expiration = isNewLinkInfinite ? getNeverExpires() : newLinkExpiration.toISOString();
    permissionAPI
      .addShareableLink(scope, expiration)
      .then((_response: any) => {
        loadShareableLinks();
      })
      .catch((_error: any) => {
        console.log("Error occured while creating shareable link");
      });
  };

  const revokeShareableLink = (code: string) => {
    permissionAPI
      .removeShareableLink(scope, code)
      .then((_response: any) => {
        loadShareableLinks();
      })
      .catch((_error: any) => {
        console.log("Error occured while removing the shareable link");
      });
  };

  const getShareableLinkURL = (code: string): string => {
    const base = window.location.origin;
    return base + "/" + scope.kind + "s/" + code;
  };

  const copyShareableLinkToClipboard = (code: string) => {
    const url = getShareableLinkURL(code);
    navigator.clipboard.writeText(url);
    info("Copied link to clipboard");
  };

  const closeAfterShare = () => {
    closeDialogCallback(true);
  };

  const close = () => {
    closeDialogCallback(false);
  };

  const changeEmail = (event: any) => {
    setEmail(event.target.value);
  };

  const changePermissions = (event: any) => {
    let updatedSharedPermissions: Array<pond.Permission> = cloneDeep(sharedPermissions);
    let permission: pond.Permission;
    switch (event.target.value) {
      case "1":
        permission = pond.Permission.PERMISSION_USERS;
        break;
      case "2":
        permission = pond.Permission.PERMISSION_READ;
        break;
      case "3":
        permission = pond.Permission.PERMISSION_WRITE;
        break;
      case "4":
        permission = pond.Permission.PERMISSION_SHARE;
        break;
      case "5":
        permission = pond.Permission.PERMISSION_BILLING;
        break;
      case "6":
        permission = pond.Permission.PERMISSION_FILE_MANAGEMENT;
        break;
      default:
        permission = pond.Permission.PERMISSION_INVALID;
        break;
    }

    if (updatedSharedPermissions.includes(permission)) {
      updatedSharedPermissions = updatedSharedPermissions.filter(
        (curPermission: pond.Permission) => curPermission !== permission
      );
    } else {
      updatedSharedPermissions.push(permission);
    }

    setSharedPermissions(updatedSharedPermissions);
  };

  const changeTab = (_event: any, newTab: number) => {
    setTab(newTab);
  };

  const isValid = () => {
    return email.trim() !== "" && sharedPermissions.length > 0;
  };

  // UI BEGINS

  const emailForm = () => {
    return (
      <TextField
        id="email"
        name="email"
        label="Email"
        value={email}
        onChange={changeEmail}
        margin="normal"
        fullWidth
        variant="outlined"
        className={classes.emailForm}
      />
    );
  };

  const permissionsForm = () => {
    return (
      <FormControl component="fieldset" fullWidth variant="outlined">
        <FormLabel component="legend">Permissions</FormLabel>
        <FormGroup>
          {permissions.includes(pond.Permission.PERMISSION_READ) && (
            <FormControlLabel
              control={
                <Checkbox
                  disabled={sharedPermissions.includes(pond.Permission.PERMISSION_READ)}
                  checked={sharedPermissions.includes(pond.Permission.PERMISSION_READ)}
                  onChange={changePermissions}
                  value={pond.Permission.PERMISSION_READ as pond.Permission}
                />
              }
              label="View"
              labelPlacement="end"
            />
          )}

          {permissions.includes(pond.Permission.PERMISSION_WRITE) && (
            <FormControlLabel
              control={
                <Checkbox
                  checked={sharedPermissions.includes(pond.Permission.PERMISSION_WRITE)}
                  onChange={changePermissions}
                  value={pond.Permission.PERMISSION_WRITE as pond.Permission}
                />
              }
              label="Edit"
              labelPlacement="end"
            />
          )}

          {permissions.includes(pond.Permission.PERMISSION_SHARE) && (
            <FormControlLabel
              control={
                <Checkbox
                  checked={sharedPermissions.includes(pond.Permission.PERMISSION_SHARE)}
                  onChange={changePermissions}
                  value={pond.Permission.PERMISSION_SHARE as pond.Permission}
                />
              }
              label="Share"
              labelPlacement="end"
            />
          )}

          {permissions.includes(pond.Permission.PERMISSION_USERS) && (
            <FormControlLabel
              control={
                <Checkbox
                  checked={sharedPermissions.includes(pond.Permission.PERMISSION_USERS)}
                  onChange={changePermissions}
                  value={pond.Permission.PERMISSION_USERS as pond.Permission}
                />
              }
              label="Manage Users"
              labelPlacement="end"
            />
          )}

          {permissions.includes(pond.Permission.PERMISSION_FILE_MANAGEMENT) && (
            <FormControlLabel
              control={
                <Checkbox
                  checked={sharedPermissions.includes(pond.Permission.PERMISSION_FILE_MANAGEMENT)}
                  onChange={changePermissions}
                  value={pond.Permission.PERMISSION_FILE_MANAGEMENT as pond.Permission}
                />
              }
              label="Files"
              labelPlacement="end"
            />
          )}

          {permissions.includes(pond.Permission.PERMISSION_BILLING) && (
            <FormControlLabel
              control={
                <Checkbox
                  checked={sharedPermissions.includes(pond.Permission.PERMISSION_BILLING)}
                  onChange={changePermissions}
                  value={pond.Permission.PERMISSION_BILLING as pond.Permission}
                />
              }
              label="Billing"
              labelPlacement="end"
            />
          )}
        </FormGroup>
      </FormControl>
    );
  };

  const emailSharing = () => {
    return (
      <React.Fragment>
        {emailForm()}
        {permissionsForm()}
      </React.Fragment>
    );
  };

  const existingUserDialog = () => {
    return (
      <ResponsiveDialog
        open={openExistingUser}
        onClose={() => {
          setOpenExistingUser(false);
          close();
        }}>
        <DialogTitle>User Already exists</DialogTitle>
        <DialogContent>This user is already part of {label}.</DialogContent>
        <DialogActions>
          <Button
            onClick={() => {
              setOpenExistingUser(false);
              close();
            }}>
            Close
          </Button>
        </DialogActions>
      </ResponsiveDialog>
    );
  };

  const shareableLinksList = () => {
    const now = moment();
    let items: any[] = [];
    shareableLinks.forEach((link: ShareableLink) => {
      const expiration = moment(link.settings.expiration);
      const neverExpires = link.settings.expiration === getNeverExpires();
      const expired = !neverExpires && expiration < now;
      items.push(
        <ListItem key={link.key()}>
          {expired ? (
            <IconButton disabled>
              <LinkOff />
            </IconButton>
          ) : (
            <Tooltip title="Click to copy link">
              <IconButton onClick={() => copyShareableLinkToClipboard(link.key())}>
                <LinkIcon />
              </IconButton>
            </Tooltip>
          )}

          <ListItemText
            primary={
              expired ? (
                <span className={classes.grey}>{"/devices/" + link.key()}</span>
              ) : (
                <Link href={getShareableLinkURL(link.key())} target="_blank">
                  {"/devices/" + link.key()}
                </Link>
              )
            }
            secondary={
              neverExpires ? (
                "Never expires"
              ) : (
                <Tooltip title={expiration.calendar()}>
                  <span>{(expired ? "Expired " : "Expires ") + expiration.fromNow()}</span>
                </Tooltip>
              )
            }
          />
          <ListItemSecondaryAction>
            <Tooltip title="Revoke shareable link">
              <IconButton onClick={() => revokeShareableLink(link.key())}>
                <RemoveCircleIcon className={classes.removeIcon} />
              </IconButton>
            </Tooltip>
          </ListItemSecondaryAction>
        </ListItem>
      );
    });
    return (
      <List>
        {isLoadingLinks ? <CircularProgress /> : <React.Fragment>{items}</React.Fragment>}
        {shareableLinks.length > 0 && <Divider variant="middle" />}
        <ListItem>
          <FormGroup row>
            <MobileDateTimePicker
              disabled={isNewLinkInfinite}
              // inputRef={props => <TextField {...props} helperText="" />}
              label="Expiration Date"
              value={newLinkExpiration}
              // onChange={(date: moment.Moment) => setNewLinkExpiration(date as Moment)}
              onChange={(value) => setNewLinkExpiration(value as Moment)}
              disablePast
            />
            <FormControlLabel
              control={
                <Checkbox
                  checked={isNewLinkInfinite}
                  onChange={(_, checked) => setIsNewLinkInfinite(checked)}
                  value="isNewLinkInfinite"
                  color="secondary"
                />
              }
              label="Never expires"
            />
          </FormGroup>
          <ListItemSecondaryAction>
            <Tooltip title="Create new shareable link">
              <IconButton
                className={classes.addIcon}
                color="default"
                onClick={() => createShareableLink()}>
                <AddIcon className={classes.addIcon} />
              </IconButton>
            </Tooltip>
          </ListItemSecondaryAction>
        </ListItem>
      </List>
    );
  };

  const content = () => {
    switch (tab) {
      case 1:
        return <React.Fragment>{shareableLinksList()}</React.Fragment>;
      default:
        return <React.Fragment>{emailSharing()}</React.Fragment>;
    }
  };

  return (
    <React.Fragment>
      {existingUserDialog()}
      <ResponsiveDialog
        maxWidth="sm"
        fullWidth
        open={isDialogOpen}
        onClose={close}
        aria-labelledby="share-dialog-title">
        <DialogTitle id="share-dialog-title">
          Share
          <Typography variant="body2" color="textSecondary">
            {label}
          </Typography>
        </DialogTitle>
        {scope.kind === "device" && (
          <Tabs value={tab} onChange={changeTab}>
            <Tab label="Share by Email" />
            <Tab label="Shareable Links" />
          </Tabs>
        )}
        <Divider />
        <DialogContent className={classes.dialogContent}>{content()}</DialogContent>
        <DialogActions>
          <Button onClick={close} color="primary">
            Cancel
          </Button>
          {tab === 0 && (
            <Button
              onClick={() => {
                //check here if the email they entered is already in the list of shared users if it was passed in
                let hasUser = false;
                sharedUsers?.forEach(user => {
                  if (user.settings.email === email) hasUser = true;
                });
                if (hasUser) {
                  setOpenExistingUser(true);
                } else {
                  share();
                }
              }}
              color="primary"
              disabled={!isValid()}>
              Share
            </Button>
          )}
        </DialogActions>
      </ResponsiveDialog>
    </React.Fragment>
  );
}
