import React, { useEffect, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import classNames from 'clsx';
import dayjs from 'dayjs';
import timezonePlugin from 'dayjs/plugin/timezone';
import utcPlugin from 'dayjs/plugin/utc';
import { useRouter } from 'next/router';
import {
  AddRounded,
  ExpandMoreRounded,
  FilterAltRounded,
  LockOpen,
  LockPersonRounded,
  SearchRounded,
  TokenRounded,
} from '@mui/icons-material';

import { SEARCH_TEXT_MAX_LENGTH, STATUSES_ACTIVE, STATUSES_ALL, STATUSES_OPTIONS } from '../constants';
import { PCN_POST_USERS, USERS_GENERATE_USER_TOKEN } from '@/modules/@common/acl/constants';
import { ACCOUNT, API } from '@/modules/@common/user/constants/rolesType';

import { impersonateUserAction } from '../../Editor/redux/slices';
import { getConfigHeaders } from '../helpers';
import * as computedState from '../helpers/computedState';
import * as selectors from '../redux/selectors';
import {
  bulkUpdateAction,
  generateTokenAction,
  getAccountOptionsAction,
  getApiSetAction,
  getDataAction,
  getDepartmentOptionsAction,
  getRoleOptionsAction,
  resetPasswordAction,
  setAction,
  setTableAction,
} from '../redux/slices';
import { hasPermission } from '@/modules/@common/user/helpers';
import { getUserPermissionsSelector, getUserRoleTypeSelector } from '@/modules/@common/user/redux/selectors';
import { omitUndefinedProps } from '@/mui/helpers';

import CommonList from '@/modules/@common/List';
import CommonTable, { TableCellActions } from '@/modules/@common/Table';
import Empty from './Empty/Empty';
import {
  Autocomplete,
  Button,
  Checkbox,
  DateTimePicker,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  Divider,
  FormControlLabel,
  GridList,
  IconButton,
  InputAdornment,
  Menu,
  MenuItem,
  Stack,
  Switch,
  TableCell,
  TableRow,
  TextField,
  Tooltip,
} from '@/mui/components';

import styles from './List.module.scss';

dayjs.extend(utcPlugin);
dayjs.extend(timezonePlugin);
function List() {
  const router = useRouter();

  const dispatch = useDispatch();
  const data = useSelector(selectors.dataSelector);
  const page = useSelector(selectors.pageSelector);
  const pageSize = useSelector(selectors.pageSizeSelector);
  const totalCount = useSelector(selectors.totalCountSelector);
  const sortBy = useSelector(selectors.sortBySelector);
  const sortOrder = useSelector(selectors.sortOrderSelector);

  const search = useSelector(selectors.searchSelector);
  const account = useSelector(selectors.accountSelector);
  const accountOptions = useSelector(selectors.accountOptionsSelector);

  const role = useSelector(selectors.roleSelector);
  const roleOptions = useSelector(selectors.roleOptionsSelector);

  const hasAccount = useSelector(getUserRoleTypeSelector) === ACCOUNT;

  const autocompleteRoleOptions = useMemo(
    () =>
      hasAccount ? (roleOptions?.filter(({ readOnlyInSelfServe }) => !readOnlyInSelfServe) ?? []) : (roleOptions ?? []),
    [hasAccount, roleOptions],
  );

  const department = useSelector(selectors.departmentSelector);
  const departmentOptions = useSelector(selectors.departmentOptionsSelector);
  const status = useSelector(selectors.statusSelector);

  const selected = useSelector(selectors.selectedSelector);
  const apiSet = useSelector(selectors.apiSetSelector);
  const [apiSetSelected, setApiSelected] = useState([]);

  const shouldShowEmpty = useSelector(computedState.shouldShowEmpty);

  const userPermissions = useSelector(getUserPermissionsSelector);

  const canCreate = hasPermission(PCN_POST_USERS, userPermissions);
  const [actions, setActions] = useState(null);
  const [bulkUserActions, setBulkUserActions] = useState(null);
  const [confirmResetPassword, setResetPassword] = useState(null);
  const [confirmGetToken, setGetToken] = useState(null);
  const minTokenLiveTime = dayjs(new Date());
  const maxTokenLiveTime = dayjs().add(1, 'year').add(1, 'day').startOf('day');
  const [apiTokenExpiration, setTokenExpiration] = useState(maxTokenLiveTime.toDate().getTime());
  const [isTokenExpiration, setIsTokenExpiration] = useState(true);

  const expiredTokenValidation = useMemo(() => {
    let message = '';

    if (confirmGetToken && isTokenExpiration) {
      const min = minTokenLiveTime.toDate().getTime();
      const max = maxTokenLiveTime.toDate().getTime();

      if (apiTokenExpiration < min) {
        message = `Can't be less than ${minTokenLiveTime.format('MMM D, YYYY hh:mm A')}`;
      } else if (apiTokenExpiration > max) {
        message = `Can't be more than ${maxTokenLiveTime.format('MMM D, YYYY hh:mm A')}`;
      }
    }

    return message;
  }, [isTokenExpiration, confirmGetToken, apiTokenExpiration, minTokenLiveTime, maxTokenLiveTime]);

  const dataLength = data?.filter((item) => !hasAccount || (hasAccount && !item.role.readOnlyInSelfServe))?.length || 0;

  const handleFilter = (filter) => {
    const { sortBy: sortBy$, sortOrder: sortOrder$, page: page$, pageSize: pageSize$, ...mainState } = filter;

    dispatch(
      setTableAction(
        omitUndefinedProps({
          sortBy: sortBy$,
          sortOrder: sortOrder$,
          page: page$,
          pageSize: pageSize$,
          selected: [],
        }),
      ),
    );

    dispatch(
      setAction({
        ...mainState,
      }),
    );
    dispatch(getDataAction());
  };

  const handleSelectDeselectAllRows = (checked) => {
    dispatch(
      setTableAction({
        selected: checked
          ? data.filter((item) => !hasAccount || (hasAccount && !item.role.readOnlyInSelfServe)).map((r) => r.id)
          : [],
      }),
    );
  };

  const handleSelectDeselectRow = (rowId) => {
    const targetUser = data.find((user) => user.id === rowId);

    if (!targetUser) {
      return;
    }

    const selectedIndex = selected.indexOf(rowId);
    let newSelected = [];

    if (selectedIndex === -1) {
      newSelected = newSelected.concat(selected, rowId);
    } else if (selectedIndex === 0) {
      newSelected = newSelected.concat(selected.slice(1));
    } else if (selectedIndex === selected.length - 1) {
      newSelected = newSelected.concat(selected.slice(0, -1));
    } else if (selectedIndex > 0) {
      newSelected = newSelected.concat(selected.slice(0, selectedIndex), selected.slice(selectedIndex + 1));
    }

    dispatch(
      setTableAction({
        selected: newSelected,
      }),
    );
  };

  useEffect(() => {
    if (!confirmGetToken) {
      setApiSelected([]);
    }
  }, [confirmGetToken]);

  useEffect(() => {
    dispatch(getDataAction());
  }, []);

  return (
    <>
      <CommonList
        title="User Management"
        renderFilters={
          <Stack direction="row" alignItems="center" spacing={2}>
            <Button
              size="small"
              color="neutral"
              variant="outlined"
              disabled={!selected.length}
              endIcon={<ExpandMoreRounded />}
              onClick={(event) => setActions(event.currentTarget)}
            >
              Actions
            </Button>
            {actions && (
              <Menu open anchorEl={actions} onClose={() => setActions('')}>
                <MenuItem
                  onClick={() => {
                    setBulkUserActions({
                      title: 'Enable 2FA',
                      description: 'enable 2fa',
                      field: 'enable2fa',
                      value: '1',
                    });
                    setActions('');
                  }}
                >
                  Enable 2FA
                </MenuItem>
                <MenuItem
                  onClick={() => {
                    setBulkUserActions({
                      title: 'Disable 2FA',
                      description: 'disable 2fa',
                      field: 'enable2fa',
                      value: '0',
                    });
                    setActions('');
                  }}
                >
                  Disable 2FA
                </MenuItem>
                <MenuItem
                  onClick={() => {
                    setBulkUserActions({
                      title: 'Activate Users',
                      description: 'activate',
                      field: 'status',
                      value: '1',
                    });
                    setActions('');
                  }}
                >
                  Activate
                </MenuItem>
                <MenuItem
                  onClick={() => {
                    setBulkUserActions({
                      title: 'Deactivate Users',
                      description: 'deactivate',
                      field: 'status',
                      value: '0',
                    });
                    setActions('');
                  }}
                >
                  Deactivate
                </MenuItem>
                <MenuItem
                  onClick={() => {
                    setBulkUserActions({
                      title: 'Reset Password',
                      description: 'reset passwords',
                      field: 'resetPassword',
                    });
                    setActions('');
                  }}
                >
                  Reset Password
                </MenuItem>
              </Menu>
            )}
            <div>
              <TextField
                className={styles.SearchField}
                size="small"
                placeholder="Search"
                value={search}
                onChange={({ target }) => handleFilter({ search: target.value, page: 1 })}
                inputProps={{
                  autoComplete: 'off',
                  maxLength: SEARCH_TEXT_MAX_LENGTH,
                }}
                InputProps={{
                  endAdornment: (
                    <InputAdornment position="end">
                      <IconButton onClick={() => null}>
                        <SearchRounded />
                      </IconButton>
                    </InputAdornment>
                  ),
                }}
                variant="outlined"
                disabled={shouldShowEmpty}
              />
            </div>
            <Stack spacing={1} direction="row" alignItems="center">
              <Divider orientation="vertical" flexItem />
              <FilterAltRounded color="shades.900" fontSize="medium" />
            </Stack>
            <Autocomplete
              className={styles.StatusSelect}
              value={STATUSES_OPTIONS.find((s) => s.value === status) ?? STATUSES_ALL}
              options={STATUSES_OPTIONS}
              size="small"
              onChange={(e, selected$) => handleFilter({ status: selected$?.value ?? STATUSES_ALL, page: 1 })}
              renderInput={(params) => <TextField {...params} placeholder="Status" />}
            />
            {!hasAccount && (
              <Autocomplete
                className={styles.AccountSelect}
                value={account}
                options={accountOptions || []}
                loading={!accountOptions}
                size="small"
                onChange={(e, selectedAccount) => handleFilter({ account: selectedAccount, page: 1 })}
                onOpen={() => dispatch(getAccountOptionsAction(''))}
                onInputChange={(e, searchText) => dispatch(getAccountOptionsAction(searchText))}
                renderInput={(params) => <TextField {...params} value="" variant="outlined" placeholder="Account" />}
              />
            )}
            {hasAccount && (
              <Autocomplete
                className={styles.DepartmentSelect}
                value={department}
                options={departmentOptions || []}
                loading={!departmentOptions}
                size="small"
                onChange={(e, selected$) =>
                  handleFilter({
                    department: selected$,
                    page: 1,
                  })
                }
                onOpen={() => dispatch(getDepartmentOptionsAction(''))}
                onInputChange={(e, searchText) => dispatch(getDepartmentOptionsAction(searchText))}
                renderInput={(params) => <TextField {...params} value="" variant="outlined" placeholder="Department" />}
              />
            )}
            <Autocomplete
              className={styles.RolesSelect}
              value={role}
              options={autocompleteRoleOptions}
              loading={!roleOptions}
              size="small"
              onOpen={() => dispatch(getRoleOptionsAction())}
              onChange={(e, selectedRole) => handleFilter({ role: selectedRole, page: 1 })}
              renderInput={(params) => <TextField {...params} placeholder="Role" />}
            />
          </Stack>
        }
        renderActions={
          <>
            {canCreate && (
              <Tooltip title="Add User">
                <Button
                  size="small"
                  variant="contained"
                  color="primary"
                  startIcon={<AddRounded />}
                  onClick={() => router.push('/users/new')}
                >
                  User
                </Button>
              </Tooltip>
            )}
          </>
        }
      >
        {shouldShowEmpty ? (
          <Empty />
        ) : (
          <CommonTable
            configHeaders={getConfigHeaders(hasAccount)}
            enableBulkActions
            bulkActionsIndeterminate={!!selected.length && selected.length < dataLength}
            bulkActionsChecked={!!selected.length && selected.length === dataLength}
            configRenderRow={(row, selectedRows, onSelectDeselectRow) => {
              const isItemSelected = selectedRows.includes(row.id);
              // todo: this condition was migrated from old UI and should refactored
              const showLoginAs =
                row.role?.name !== 'admin' && row.status === STATUSES_ACTIVE && !hasAccount && row.role.type !== API;
              const showTokenGeneration =
                !hasAccount &&
                row.role.type === API &&
                hasPermission(USERS_GENERATE_USER_TOKEN, userPermissions) &&
                !!row.status;

              return (
                <TableRow
                  className={classNames(styles.Row, {
                    [styles.Row___disabled]: hasAccount && row.role.readOnlyInSelfServe,
                  })}
                  key={row.id}
                  hover
                  role="checkbox"
                  aria-checked={isItemSelected}
                  selected={isItemSelected}
                  onClick={() => router.push(`/users/${row.id}`)}
                >
                  <TableCell padding="checkbox" align="center">
                    <Checkbox
                      color="primary"
                      checked={isItemSelected}
                      onClick={(event) => {
                        event.stopPropagation();
                        onSelectDeselectRow(row.id);
                      }}
                    />
                  </TableCell>
                  {/*<TableCell align="center" className={styles.IdCell}>*/}
                  {/*  {row.id}*/}
                  {/*</TableCell>*/}
                  <TableCell>
                    <div className={styles.NoWrap}>{`${row.firstName} ${row.lastName}`}</div>
                  </TableCell>
                  <TableCell>
                    <div className={styles.NoWrap}>{row.email}</div>
                  </TableCell>
                  {!hasAccount && (
                    <TableCell>
                      <div className={styles.NoWrap}>{row.account?.name}</div>
                    </TableCell>
                  )}
                  <TableCell>
                    <div className={styles.NoWrap}>{row.role?.displayName}</div>
                  </TableCell>
                  {hasAccount && (
                    <TableCell>
                      <div className={styles.NoWrap}>{row.department?.name}</div>
                    </TableCell>
                  )}
                  <TableCell>
                    <div className={styles.NoWrap}>{row.status === STATUSES_ACTIVE ? 'Active' : 'Disabled'}</div>
                  </TableCell>
                  <TableCell>
                    <div className={styles.NoWrap}>
                      {row.lastLoginDate && dayjs(row.lastLoginDate).format('MMM D, YYYY hh:mm A')}
                    </div>
                  </TableCell>
                  {!hasAccount && (
                    <TableCell>
                      <div className={styles.NoWrap}>{dayjs(row.updateDate).format('MMM D, YYYY hh:mm A')}</div>
                    </TableCell>
                  )}
                  <TableCellActions>
                    {row.role.type === API ? (
                      <>
                        {showTokenGeneration && (
                          <Tooltip title="Generate Token" disableInteractive>
                            <IconButton
                              size="small"
                              onClick={(event) => {
                                event.stopPropagation();
                                dispatch(getApiSetAction());
                                setGetToken({
                                  userId: row.id,
                                  accountId: row.accountId,
                                });
                              }}
                            >
                              <TokenRounded />
                            </IconButton>
                          </Tooltip>
                        )}
                      </>
                    ) : (
                      <Tooltip title="Reset Password" disableInteractive>
                        <IconButton
                          size="small"
                          onClick={(event) => {
                            event.stopPropagation();
                            setResetPassword({
                              email: row.email,
                            });
                          }}
                        >
                          <LockOpen />
                        </IconButton>
                      </Tooltip>
                    )}
                    {!!showLoginAs && (
                      <Tooltip title="Login As" disableInteractive>
                        <IconButton
                          size="small"
                          onClick={(event) => {
                            event.stopPropagation();

                            dispatch(impersonateUserAction(row.id));
                          }}
                        >
                          <LockPersonRounded />
                        </IconButton>
                      </Tooltip>
                    )}
                  </TableCellActions>
                </TableRow>
              );
            }}
            data={data || []}
            selected={selected}
            sortBy={sortBy}
            sortOrder={sortOrder}
            totalCount={totalCount}
            page={page}
            rowsPerPage={pageSize}
            onSelectDeselectAllRows={handleSelectDeselectAllRows}
            onSelectDeselectRow={handleSelectDeselectRow}
            onFilter={handleFilter}
          />
        )}
      </CommonList>
      {!!confirmResetPassword && (
        <Dialog maxWidth="sm" open={!!confirmResetPassword} onClose={() => setResetPassword(null)}>
          <DialogTitle onClose={() => setResetPassword(null)}>Reset Password</DialogTitle>
          <DialogContent>{`Are you sure you want to reset password for ${confirmResetPassword.email}?`}</DialogContent>
          <DialogActions>
            <Button variant="outlined" size="small" color="neutral" onClick={() => setResetPassword(null)}>
              Cancel
            </Button>
            <Button
              variant="contained"
              size="small"
              color="primary"
              onClick={() => {
                dispatch(resetPasswordAction(confirmResetPassword.email));
                setResetPassword(null);
              }}
            >
              Reset
            </Button>
          </DialogActions>
        </Dialog>
      )}
      {!!bulkUserActions && (
        <Dialog maxWidth="sm" open={!!bulkUserActions} onClose={() => setBulkUserActions(null)}>
          <DialogTitle onClose={() => setBulkUserActions(null)}>{bulkUserActions.title}</DialogTitle>
          <DialogContent>
            {`Are you sure you want to ${bulkUserActions.description} for ${selected.length} users?`}
          </DialogContent>
          <DialogActions>
            <Button variant="outlined" size="small" color="neutral" onClick={() => setBulkUserActions(null)}>
              Cancel
            </Button>
            <Button
              variant="contained"
              size="small"
              color="primary"
              onClick={() => {
                dispatch(
                  bulkUpdateAction({
                    field: bulkUserActions.field,
                    value: bulkUserActions.value ?? null,
                  }),
                );
                setBulkUserActions(null);
              }}
            >
              Apply
            </Button>
          </DialogActions>
        </Dialog>
      )}
      {!!confirmGetToken && (
        <Dialog maxWidth="md" open={!!confirmGetToken} onClose={() => setGetToken(null)}>
          <DialogTitle onClose={() => setGetToken(null)}>Choose Allowed API Sets</DialogTitle>
          <DialogContent>
            <Stack spacing={5} fullWidth>
              <GridList direction="column" rowCount={2}>
                {apiSet &&
                  apiSet.map((resource) => (
                    <FormControlLabel
                      key={resource}
                      size="small"
                      control={
                        <Checkbox
                          size="small"
                          checked={apiSetSelected.includes(resource)}
                          color="primary"
                          onChange={(event) => {
                            setApiSelected(
                              !event.target.checked
                                ? apiSetSelected.filter((res) => res !== resource)
                                : apiSetSelected.concat(resource),
                            );
                          }}
                        />
                      }
                      label={resource}
                    />
                  ))}
              </GridList>
              <Stack fullWidth direction="row" justifyContent="space-between" alignItems="flex-start" spacing={2}>
                <div>
                  <FormControlLabel
                    control={
                      <Switch
                        color="primary"
                        size="small"
                        checked={isTokenExpiration}
                        onChange={({ target }) => {
                          setIsTokenExpiration(target.checked);
                        }}
                      />
                    }
                    label="Expiration Date"
                    labelPlacement="end"
                  />
                </div>
                <div>
                  <DateTimePicker
                    disabled={!isTokenExpiration}
                    value={dayjs(apiTokenExpiration)}
                    minDate={minTokenLiveTime}
                    maxDate={maxTokenLiveTime}
                    slotProps={{
                      textField: {
                        size: 'small',
                        helperText: expiredTokenValidation,
                      },
                    }}
                    onChange={(date) => {
                      setTokenExpiration(date.toDate().getTime());
                    }}
                    onApply={(date) => {
                      setTokenExpiration(date.toDate().getTime());
                    }}
                  />
                </div>
              </Stack>
            </Stack>
          </DialogContent>
          <DialogActions>
            <Button variant="outlined" size="small" color="neutral" onClick={() => setGetToken(null)}>
              Cancel
            </Button>
            <Button
              variant="contained"
              size="small"
              color="primary"
              disabled={!!expiredTokenValidation}
              onClick={() => {
                dispatch(
                  generateTokenAction({
                    accountId: confirmGetToken.accountId,
                    userId: confirmGetToken.userId,
                    expirationDate: apiTokenExpiration,
                    resources: apiSetSelected,
                    isExpirationDateActive: isTokenExpiration,
                  }),
                );
                setGetToken(null);
                setIsTokenExpiration(true);
              }}
            >
              Generate
            </Button>
          </DialogActions>
        </Dialog>
      )}
    </>
  );
}

export default List;
