// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

import React, { Fragment, useEffect, useState } from "react";

import { Theme } from "@mui/material/styles";
import { useNavigate } from "react-router-dom";
import { Button } from "mds";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import api from "../../../common/api";
import { Grid, LinearProgress } from "@mui/material";
import { User, UsersList } from "./types";
import { usersSort } from "../../../utils/sortFunctions";
import { AddIcon, DeleteIcon, GroupsIcon, UsersIcon } from "../../../icons";
import {
  actionsTray,
  containerForHeader,
  searchField,
  tableStyles,
} from "../Common/FormComponents/common/styleLibrary";
import { ErrorResponseHandler } from "../../../common/types";

import TableWrapper from "../Common/TableWrapper/TableWrapper";
import PageHeader from "../Common/PageHeader/PageHeader";
import { encodeURLString } from "../../../common/utils";
import HelpBox from "../../../common/HelpBox";
import AButton from "../Common/AButton/AButton";
import PageLayout from "../Common/Layout/PageLayout";
import SearchBox from "../Common/SearchBox";
import withSuspense from "../Common/Components/withSuspense";
import {
  addUserToGroupPermissions,
  CONSOLE_UI_RESOURCE,
  deleteUserPermissions,
  IAM_PAGES,
  IAM_SCOPES,
  listUsersPermissions,
  permissionTooltipHelper,
  S3_ALL_RESOURCES,
  viewUserPermissions,
} from "../../../common/SecureComponent/permissions";

import {
  hasPermission,
  SecureComponent,
} from "../../../common/SecureComponent";
import { setErrorSnackMessage } from "../../../systemSlice";
import { useAppDispatch } from "../../../store";
import TooltipWrapper from "../Common/TooltipWrapper/TooltipWrapper";

const DeleteUser = withSuspense(React.lazy(() => import("./DeleteUser")));
const AddToGroup = withSuspense(React.lazy(() => import("./BulkAddToGroup")));

const styles = (theme: Theme) =>
  createStyles({
    ...actionsTray,
    ...searchField,
    searchField: {
      ...searchField.searchField,
      marginRight: "auto",
      maxWidth: 380,
    },
    ...tableStyles,
    ...containerForHeader(theme.spacing(4)),
  });

interface IUsersProps {
  classes: any;
}

const ListUsers = ({ classes }: IUsersProps) => {
  const dispatch = useAppDispatch();
  const navigate = useNavigate();

  const [records, setRecords] = useState<User[]>([]);
  const [loading, setLoading] = useState<boolean>(true);
  const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
  const [addGroupOpen, setAddGroupOpen] = useState<boolean>(false);
  const [filter, setFilter] = useState<string>("");
  const [checkedUsers, setCheckedUsers] = useState<string[]>([]);

  const displayListUsers = hasPermission(
    CONSOLE_UI_RESOURCE,
    listUsersPermissions
  );

  const viewUser = hasPermission(CONSOLE_UI_RESOURCE, viewUserPermissions);

  const addUserToGroup = hasPermission(
    CONSOLE_UI_RESOURCE,
    addUserToGroupPermissions
  );

  const deleteUser = hasPermission(CONSOLE_UI_RESOURCE, deleteUserPermissions);

  const closeDeleteModalAndRefresh = (refresh: boolean) => {
    setDeleteOpen(false);
    if (refresh) {
      setLoading(true);
    }
    setCheckedUsers([]);
  };

  const closeAddGroupBulk = (unCheckAll: boolean = false) => {
    setAddGroupOpen(false);
    if (unCheckAll) {
      setCheckedUsers([]);
    }
  };

  useEffect(() => {
    if (loading) {
      if (displayListUsers) {
        api
          .invoke("GET", `/api/v1/users`)
          .then((res: UsersList) => {
            const users = res.users === null ? [] : res.users;

            setLoading(false);
            setRecords(users.sort(usersSort));
          })
          .catch((err: ErrorResponseHandler) => {
            setLoading(false);
            dispatch(setErrorSnackMessage(err));
          });
      } else {
        setLoading(false);
      }
    }
  }, [loading, dispatch, displayListUsers]);

  const filteredRecords = records.filter((elementItem) =>
    elementItem.accessKey.includes(filter)
  );

  const selectionChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { target: { value = "", checked = false } = {} } = e;

    let elements: string[] = [...checkedUsers]; // We clone the checkedUsers array

    if (checked) {
      // If the user has checked this field we need to push this to checkedUsersList
      elements.push(value);
    } else {
      // User has unchecked this field, we need to remove it from the list
      elements = elements.filter((element) => element !== value);
    }

    setCheckedUsers(elements);

    return elements;
  };

  const viewAction = (selectionElement: any): void => {
    navigate(
      `${IAM_PAGES.USERS}/${encodeURLString(selectionElement.accessKey)}`
    );
  };

  const tableActions = [
    {
      type: "view",
      onClick: viewAction,
      disableButtonFunction: () => !viewUser,
    },
    {
      type: "edit",
      onClick: viewAction,
      disableButtonFunction: () => !viewUser,
    },
  ];

  return (
    <Fragment>
      {deleteOpen && (
        <DeleteUser
          deleteOpen={deleteOpen}
          selectedUsers={checkedUsers}
          closeDeleteModalAndRefresh={(refresh: boolean) => {
            closeDeleteModalAndRefresh(refresh);
          }}
        />
      )}
      {addGroupOpen && (
        <AddToGroup
          open={addGroupOpen}
          checkedUsers={checkedUsers}
          closeModalAndRefresh={(close: boolean) => {
            closeAddGroupBulk(close);
          }}
        />
      )}
      <PageHeader label={"Users"} />
      <PageLayout>
        <Grid item xs={12} className={classes.actionsTray}>
          <SearchBox
            placeholder={"Search Users"}
            onChange={setFilter}
            overrideClass={classes.searchField}
            value={filter}
          />
          <SecureComponent
            resource={CONSOLE_UI_RESOURCE}
            scopes={[IAM_SCOPES.ADMIN_DELETE_USER]}
            matchAll
            errorProps={{ disabled: true }}
          >
            <TooltipWrapper
              tooltip={
                hasPermission("console", [IAM_SCOPES.ADMIN_DELETE_USER])
                  ? checkedUsers.length === 0
                    ? "Select Users to delete"
                    : "Delete Selected"
                  : permissionTooltipHelper(
                      [IAM_SCOPES.ADMIN_DELETE_USER],
                      "delete users"
                    )
              }
            >
              <Button
                id={"delete-selected-users"}
                onClick={() => {
                  setDeleteOpen(true);
                }}
                label={"Delete Selected"}
                icon={<DeleteIcon />}
                disabled={checkedUsers.length === 0}
                variant={"secondary"}
                aria-label="delete-selected-users"
              />
            </TooltipWrapper>
          </SecureComponent>
          <SecureComponent
            scopes={[IAM_SCOPES.ADMIN_ADD_USER_TO_GROUP]}
            resource={CONSOLE_UI_RESOURCE}
            errorProps={{ disabled: true }}
          >
            <TooltipWrapper
              tooltip={
                hasPermission("console", [IAM_SCOPES.ADMIN_ADD_USER_TO_GROUP])
                  ? checkedUsers.length === 0
                    ? "Select Users to group"
                    : "Add to Group"
                  : permissionTooltipHelper(
                      [IAM_SCOPES.ADMIN_ADD_USER_TO_GROUP],
                      "add users to groups"
                    )
              }
            >
              <Button
                id={"add-to-group"}
                label={"Add to Group"}
                icon={<GroupsIcon />}
                disabled={checkedUsers.length <= 0}
                onClick={() => {
                  if (checkedUsers.length > 0) {
                    setAddGroupOpen(true);
                  }
                }}
                variant={"regular"}
              />
            </TooltipWrapper>
          </SecureComponent>
          <SecureComponent
            scopes={[
              IAM_SCOPES.ADMIN_CREATE_USER,
              IAM_SCOPES.ADMIN_LIST_USER_POLICIES,
              IAM_SCOPES.ADMIN_LIST_GROUPS,
            ]}
            resource={S3_ALL_RESOURCES}
            matchAll
            errorProps={{ disabled: true }}
          >
            <TooltipWrapper
              tooltip={
                hasPermission(
                  "console",
                  [
                    IAM_SCOPES.ADMIN_CREATE_USER,
                    IAM_SCOPES.ADMIN_LIST_USER_POLICIES,
                    IAM_SCOPES.ADMIN_LIST_GROUPS,
                    IAM_SCOPES.ADMIN_ATTACH_USER_OR_GROUP_POLICY,
                  ],
                  true
                )
                  ? "Create User"
                  : permissionTooltipHelper(
                      [
                        IAM_SCOPES.ADMIN_CREATE_USER,
                        IAM_SCOPES.ADMIN_LIST_USER_POLICIES,
                        IAM_SCOPES.ADMIN_LIST_GROUPS,
                        IAM_SCOPES.ADMIN_ATTACH_USER_OR_GROUP_POLICY,
                      ],
                      "create users"
                    )
              }
            >
              <Button
                id={"create-user"}
                label={"Create User"}
                icon={<AddIcon />}
                onClick={() => {
                  navigate(`${IAM_PAGES.USER_ADD}`);
                }}
                variant={"callAction"}
                disabled={
                  !hasPermission(
                    "console",
                    [
                      IAM_SCOPES.ADMIN_CREATE_USER,
                      IAM_SCOPES.ADMIN_LIST_USER_POLICIES,
                      IAM_SCOPES.ADMIN_LIST_GROUPS,
                      IAM_SCOPES.ADMIN_ATTACH_USER_OR_GROUP_POLICY,
                    ],
                    true
                  )
                }
              />
            </TooltipWrapper>
          </SecureComponent>
        </Grid>

        {loading && <LinearProgress />}
        {!loading && (
          <Fragment>
            {records.length > 0 && (
              <Fragment>
                <TooltipWrapper
                  tooltip={
                    viewUser
                      ? ""
                      : permissionTooltipHelper(
                          [IAM_SCOPES.ADMIN_GET_USER],
                          "view user details"
                        )
                  }
                >
                  <Grid
                    item
                    xs={12}
                    className={classes.tableBlock}
                    marginBottom={"15px"}
                  >
                    <SecureComponent
                      scopes={[IAM_SCOPES.ADMIN_LIST_USERS]}
                      resource={CONSOLE_UI_RESOURCE}
                      errorProps={{ disabled: true }}
                    >
                      <TableWrapper
                        itemActions={tableActions}
                        columns={[
                          { label: "Access Key", elementKey: "accessKey" },
                        ]}
                        onSelect={
                          addUserToGroup || deleteUser
                            ? selectionChanged
                            : undefined
                        }
                        selectedItems={checkedUsers}
                        isLoading={loading}
                        records={filteredRecords}
                        entityName="Users"
                        idField="accessKey"
                      />
                    </SecureComponent>
                  </Grid>
                </TooltipWrapper>
                <HelpBox
                  title={"Users"}
                  iconComponent={<UsersIcon />}
                  help={
                    <Fragment>
                      A MinIO user consists of a unique access key (username)
                      and corresponding secret key (password). Clients must
                      authenticate their identity by specifying both a valid
                      access key (username) and the corresponding secret key
                      (password) of an existing MinIO user.
                      <br />
                      Groups provide a simplified method for managing shared
                      permissions among users with common access patterns and
                      workloads.
                      <br />
                      <br />
                      Users inherit access permissions to data and resources
                      through the groups they belong to.
                      <br />
                      MinIO uses Policy-Based Access Control (PBAC) to define
                      the authorized actions and resources to which an
                      authenticated user has access. Each policy describes one
                      or more actions and conditions that outline the
                      permissions of a user or group of users.
                      <br />
                      <br />
                      Each user can access only those resources and operations
                      which are explicitly granted by the built-in role. MinIO
                      denies access to any other resource or action by default.
                      <br />
                      <br />
                      You can learn more at our{" "}
                      <a
                        href="https://min.io/docs/minio/kubernetes/upstream/administration/identity-access-management/minio-user-management.html?ref=con"
                        target="_blank"
                        rel="noreferrer"
                      >
                        documentation
                      </a>
                      .
                    </Fragment>
                  }
                />
              </Fragment>
            )}
            {records.length === 0 && (
              <Grid
                container
                justifyContent={"center"}
                alignContent={"center"}
                alignItems={"start"}
              >
                <Grid item xs={8}>
                  <HelpBox
                    title={"Users"}
                    iconComponent={<UsersIcon />}
                    help={
                      <Fragment>
                        A MinIO user consists of a unique access key (username)
                        and corresponding secret key (password). Clients must
                        authenticate their identity by specifying both a valid
                        access key (username) and the corresponding secret key
                        (password) of an existing MinIO user.
                        <br />
                        Groups provide a simplified method for managing shared
                        permissions among users with common access patterns and
                        workloads.
                        <br />
                        <br />
                        Users inherit access permissions to data and resources
                        through the groups they belong to.
                        <br />
                        MinIO uses Policy-Based Access Control (PBAC) to define
                        the authorized actions and resources to which an
                        authenticated user has access. Each policy describes one
                        or more actions and conditions that outline the
                        permissions of a user or group of users.
                        <br />
                        <br />
                        Each user can access only those resources and operations
                        which are explicitly granted by the built-in role. MinIO
                        denies access to any other resource or action by
                        default.
                        <SecureComponent
                          scopes={[
                            IAM_SCOPES.ADMIN_CREATE_USER,
                            IAM_SCOPES.ADMIN_LIST_USER_POLICIES,
                            IAM_SCOPES.ADMIN_LIST_GROUPS,
                          ]}
                          matchAll
                          resource={CONSOLE_UI_RESOURCE}
                        >
                          <br />
                          <br />
                          To get started,{" "}
                          <AButton
                            onClick={() => {
                              navigate(`${IAM_PAGES.USER_ADD}`);
                            }}
                          >
                            Create a User
                          </AButton>
                          .
                        </SecureComponent>
                      </Fragment>
                    }
                  />
                </Grid>
              </Grid>
            )}
          </Fragment>
        )}
      </PageLayout>
    </Fragment>
  );
};

export default withStyles(styles)(ListUsers);
