import React, { useCallback, useMemo, useState } from 'react';
import { ModalFooterConfig, ModalSideBarConfig, ReusableModal } from 'common/components/reusable-modal';
import { Id, Optional, TeamUser, UserRoleType } from 'backend-api/models';
import { WarningMessage, Mode, SearchUser, ShareModalContext } from 'modals/sharing-modal/types';
import { ModalFooter, ModalHeader, Notification, UsersList } from './components';
import { SelectedUsersSidebar } from './components/selected-users-sidebar';
import { SEARCH_DEBOUNCE_TIME } from 'common/constants';
import { debounce, difference } from 'lodash';
import { ALL_OPTION_ID } from 'common/components/form/select';
import { prepareTeammatesForDisplay, searchUsersToTeamUsers } from 'modals/sharing-modal/transducers';
import { getAllUserSelectedAsEditors, getAllUserSelectedAsEditorsAndViewers } from './transducers';
import { EDITORS_LIMIT, VIEWERS_LIMIT } from 'modals/sharing-modal/constants';
import { filterUsersByRole, hasUserRole } from 'common/transducers';
import { ROLES } from 'common-v2/constants';

interface Props {
  mode: Mode;
  title: React.ReactChild;
  isOpened: boolean;
  isLoading: boolean;
  error?: string;
  teamUsers: TeamUser[];
  searchUsers: SearchUser[];
  pendingAddedUsers: TeamUser[];
  pendingDeletedUsers: TeamUser[];
  pendingUpdatedUsers: TeamUser[];
  onCloseShareWindow(): void;
  onSave(): void;
  onAddUser(user: TeamUser): void;
  onDeleteUser(user: TeamUser): void;
  onUpdateUser(user: TeamUser): void;
  onClearAll(): void;
  isConfidential: boolean;
  context: ShareModalContext;
}

export const ShareModal = ({
  mode,
  title,
  onCloseShareWindow,
  onSave,
  onAddUser,
  onUpdateUser,
  onDeleteUser,
  onClearAll,
  isOpened,
  isLoading,
  teamUsers,
  searchUsers,
  pendingAddedUsers,
  pendingDeletedUsers,
  pendingUpdatedUsers,
  isConfidential,
  context,
}: Props) => {
  const selectedUsers = prepareTeammatesForDisplay(
    teamUsers,
    pendingAddedUsers,
    pendingUpdatedUsers,
    pendingDeletedUsers
  );

  const isDirty =
    pendingAddedUsers.length !== 0 || pendingDeletedUsers.length !== 0 || pendingUpdatedUsers.length !== 0;

  const isSaveDisabled =
    (!isDirty && mode === Mode.EDIT) ||
    (context === ShareModalContext.ARTIST_TEAM && selectedUsers.length < 1) ||
    isLoading;

  const hasNoSelectedUser =
    selectedUsers.length < 1 && !isLoading && context === ShareModalContext.PROJECT_COLLABORATORS;

  const [search, setSearch] = useState<Optional<string>>();

  const debouncedSearch = useCallback(
    debounce((query: string) => setSearch(query), SEARCH_DEBOUNCE_TIME),
    [setSearch]
  );

  const onSearchChange = useCallback((query: string) => debouncedSearch(query), [debouncedSearch]);

  const selectedIds = useMemo(() => selectedUsers.map(user => user.id), [selectedUsers]);

  const canAddEditors = useMemo(
    () => selectedUsers.filter(user => hasUserRole(user, ROLES.EDITOR)).length < EDITORS_LIMIT,
    [selectedUsers]
  );

  const canAddViewers = useMemo(
    () => selectedUsers.filter(user => hasUserRole(user, ROLES.VIEWER)).length < VIEWERS_LIMIT,
    [selectedUsers]
  );

  const canSelectMoreUsers = useMemo(() => {
    return context === ShareModalContext.ARTIST_TEAM ? canAddEditors : canAddEditors || canAddViewers;
  }, [canAddEditors, canAddViewers, context]);

  const approversIds = useMemo(() => filterUsersByRole(teamUsers, UserRoleType.Approver).map(user => user.id), [
    teamUsers,
  ]);

  const onUpdateSelectedUsersIds = useCallback(
    (updatedUsersIds: Id[]) => {
      if (updatedUsersIds.includes(ALL_OPTION_ID)) {
        const users =
          context === ShareModalContext.ARTIST_TEAM
            ? getAllUserSelectedAsEditors(searchUsers, selectedIds, selectedUsers)
            : getAllUserSelectedAsEditorsAndViewers(searchUsers, selectedIds, selectedUsers);

        users.forEach(teamUser => onAddUser(teamUser));
        return;
      }

      if (updatedUsersIds.length === 0) {
        onClearAll();
        return;
      }

      if (updatedUsersIds.length > selectedIds.length) {
        if (!canSelectMoreUsers) {
          return;
        }

        const newSelectedUsers = searchUsers.filter(
          user => !selectedIds.includes(user.id) && updatedUsersIds.includes(user.id)
        );

        const role = canAddEditors ? ROLES.EDITOR : ROLES.VIEWER;

        searchUsersToTeamUsers(newSelectedUsers, role).forEach(teamUser => onAddUser(teamUser));
      } else if (updatedUsersIds.length < selectedIds.length) {
        const removerUsersIds = difference(selectedIds, updatedUsersIds);
        selectedUsers.filter(user => removerUsersIds.includes(user.id)).forEach(teamUser => onDeleteUser(teamUser));
      }
    },
    [
      canAddEditors,
      canSelectMoreUsers,
      context,
      onAddUser,
      searchUsers,
      onClearAll,
      onDeleteUser,
      selectedIds,
      selectedUsers,
    ]
  );

  const sidebarConfig: ModalSideBarConfig = useMemo(
    () => ({
      component: (
        <SelectedUsersSidebar
          context={context}
          selectedUsers={selectedUsers}
          onClearAll={onClearAll}
          onRemoveUser={onDeleteUser}
          onUpdateUser={onUpdateUser}
          isLoading={isLoading}
        />
      ),
    }),
    [context, onClearAll, onDeleteUser, onUpdateUser, selectedUsers, isLoading]
  );

  const saveButtonTooltipText = useMemo(() => {
    if (context === ShareModalContext.ARTIST_TEAM) {
      if (isConfidential) return WarningMessage.CONFIDENTIAL;
      if (selectedUsers.length < 1) return WarningMessage.TEAM_WITHOUT_EDITORS;
    }
    return undefined;
  }, [isConfidential, selectedUsers.length, context]);

  const footerConfig: ModalFooterConfig = useMemo(
    () => ({
      component: (
        <ModalFooter
          warning={{
            value: hasNoSelectedUser,
            message: WarningMessage.PROJECT_WITH_NO_USERS,
          }}
          isCancelDisabled={isLoading}
          isSaveDisabled={isSaveDisabled}
          onCancel={onCloseShareWindow}
          onSave={onSave}
          saveButtonTooltipText={saveButtonTooltipText}
        />
      ),
    }),
    [isLoading, isSaveDisabled, onCloseShareWindow, onSave, hasNoSelectedUser, saveButtonTooltipText]
  );

  const onAfterCloseModal = useCallback(() => {
    setSearch(undefined);
  }, []);

  return (
    <ReusableModal
      onClose={onCloseShareWindow}
      sideBarConfig={sidebarConfig}
      footerConfig={footerConfig}
      width="780px"
      height="600px"
      isOpen={isOpened}
      hasHeader={false}
      onAfterClose={onAfterCloseModal}
      dataSelector="modal-container"
    >
      <>
        <ModalHeader title={title} onChangeSearch={onSearchChange} />
        {isConfidential && <Notification />}
        <UsersList
          isLoading={isLoading}
          searchUsers={searchUsers}
          selectedUserIds={selectedIds}
          onChangeSelectedUsers={onUpdateSelectedUsersIds}
          search={search}
          canSelectMoreUsers={canSelectMoreUsers}
          approversIds={approversIds}
        />
      </>
    </ReusableModal>
  );
};
