import { useEffect, useMemo, useState } from 'react';

import { type OrgRole } from '@grafana/data';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
import { Trans, t } from '@grafana/i18n';
import { config } from '@grafana/runtime';
import {
  Avatar,
  Box,
  Button,
  type CellProps,
  type Column,
  ConfirmModal,
  type FetchDataFunc,
  Icon,
  InteractiveTable,
  Pagination,
  Stack,
  Tag,
  Text,
  TextLink,
  Tooltip,
} from '@grafana/ui';
import { UserRolePicker } from 'app/core/components/RolePicker/UserRolePicker';
import { fetchRoleOptions, updateUserRoles } from 'app/core/components/RolePicker/api';
import { RolePickerBadges } from 'app/core/components/RolePickerDrawer/RolePickerBadges';
import { TagBadge } from 'app/core/components/TagFilter/TagBadge';
import { contextSrv } from 'app/core/services/context_srv';
import { AccessControlAction, type Role } from 'app/types/accessControl';
import { type OrgUser } from 'app/types/user';

import { OrgRolePicker } from '../OrgRolePicker';

type Cell<T extends keyof OrgUser = keyof OrgUser> = CellProps<OrgUser, OrgUser[T]>;

const disabledRoleMessage = `This user's role is not editable because it is synchronized from your auth provider.
Refer to the Grafana authentication docs for details.`;

const getBasicRoleDisabled = (user: OrgUser) => {
  const isUserSynced = user?.isExternallySynced;
  return !contextSrv.hasPermissionInMetadata(AccessControlAction.OrgUsersWrite, user) || isUserSynced;
};

const selectors = e2eSelectors.pages.UserListPage.UsersListPage;

export interface Props {
  users: OrgUser[];
  orgId?: number;
  onRoleChange: (role: OrgRole, user: OrgUser) => void;
  onRemoveUser: (user: OrgUser) => void;
  fetchData?: FetchDataFunc<OrgUser>;
  changePage: (page: number) => void;
  page: number;
  totalPages: number;
  rolesLoading?: boolean;
  onUserRolesChange?: () => void;
}

export const OrgUsersTable = ({
  users,
  orgId,
  onRoleChange,
  onUserRolesChange,
  onRemoveUser,
  fetchData,
  changePage,
  page,
  totalPages,
  rolesLoading,
}: Props) => {
  const [userToRemove, setUserToRemove] = useState<OrgUser | null>(null);
  const [roleOptions, setRoleOptions] = useState<Role[]>([]);

  useEffect(() => {
    async function fetchOptions() {
      try {
        if (contextSrv.hasPermission(AccessControlAction.ActionRolesList)) {
          let options = await fetchRoleOptions(orgId);
          setRoleOptions(options);
        }
      } catch (e) {
        console.error('Error loading options');
      }
    }
    if (contextSrv.licensedAccessControlEnabled()) {
      fetchOptions();
    }
  }, [orgId]);

  const columns: Array<Column<OrgUser>> = useMemo(
    () => [
      {
        id: 'avatarUrl',
        header: '',
        cell: ({ cell: { value } }: Cell<'avatarUrl'>) => value && <Avatar src={value} alt="User avatar" />,
      },
      {
        id: 'login',
        header: 'Login',
        cell: ({ cell: { value } }: Cell<'login'>) => <div>{value}</div>,
        sortType: 'string',
      },
      {
        id: 'email',
        header: 'Email',
        cell: ({ cell: { value } }: Cell<'email'>) => value,
        sortType: 'string',
      },
      {
        id: 'name',
        header: 'Name',
        cell: ({ cell: { value } }: Cell<'name'>) => value,
        sortType: 'string',
      },
      {
        id: 'lastSeenAtAge',
        header: 'Last active',
        cell: ({ cell: { value }, row: { original } }: Cell<'lastSeenAtAge'>) => {
          // If lastSeenAt is before created, user has never logged in
          const neverLoggedIn =
            original.lastSeenAt && original.created && new Date(original.lastSeenAt) < new Date(original.created);
          return (
            <>
              {value && (
                <>
                  {neverLoggedIn ? (
                    <Text color={'disabled'}>
                      <Trans i18nKey="admin.org-uers.last-seen-never">Never</Trans>
                    </Text>
                  ) : (
                    value
                  )}
                </>
              )}
            </>
          );
        },
        sortType: (a, b) => new Date(a.original.lastSeenAt).getTime() - new Date(b.original.lastSeenAt).getTime(),
      },
      {
        id: 'role',
        header: 'Role',
        cell: ({ cell: { value }, row: { original } }: Cell<'role'>) => {
          const basicRoleDisabled = getBasicRoleDisabled(original);
          const onUserRolesUpdate = async (newRoles: Role[], userId: number, orgId: number | undefined) => {
            await updateUserRoles(newRoles, userId, orgId);
            if (onUserRolesChange) {
              onUserRolesChange();
            }
          };

          if (config.featureToggles.rolePickerDrawer) {
            return <RolePickerBadges disabled={basicRoleDisabled} user={original} />;
          }

          return contextSrv.licensedAccessControlEnabled() ? (
            <UserRolePicker
              userId={original.userId}
              roles={original.roles}
              apply={true}
              onApplyRoles={onUserRolesUpdate}
              isLoading={rolesLoading}
              orgId={orgId}
              roleOptions={roleOptions}
              basicRole={value}
              onBasicRoleChange={(newRole) => onRoleChange(newRole, original)}
              basicRoleDisabled={basicRoleDisabled}
              basicRoleDisabledMessage={disabledRoleMessage}
              width={40}
            />
          ) : (
            <OrgRolePicker
              aria-label={t('admin.org-users-table.columns.aria-label-role', 'Role')}
              value={value}
              disabled={basicRoleDisabled}
              onChange={(newRole) => onRoleChange(newRole, original)}
            />
          );
        },
      },
      {
        id: 'info',
        header: '',
        cell: ({ row: { original } }: Cell) => {
          const basicRoleDisabled = getBasicRoleDisabled(original);
          return (
            basicRoleDisabled && (
              <Box display={'flex'} alignItems={'center'} marginLeft={1}>
                <Tooltip
                  interactive={true}
                  content={
                    <div>
                      <Trans i18nKey="admin.org-users.not-editable">
                        This user&apos;s role is not editable because it is synchronized from your auth provider. Refer
                        to the&nbsp;
                        <TextLink
                          href={
                            'https://grafana.com/docs/grafana/latest/administration/user-management/manage-org-users/#change-a-users-organization-permissions'
                          }
                          external
                        >
                          Grafana authentication docs
                        </TextLink>
                        &nbsp;for details.
                      </Trans>
                    </div>
                  }
                >
                  <Icon name="question-circle" />
                </Tooltip>
              </Box>
            )
          );
        },
      },
      {
        id: 'authLabels',
        header: 'Origin',
        cell: ({ cell: { value } }: Cell<'authLabels'>) => (
          <>{Array.isArray(value) && value.length > 0 && <TagBadge label={value[0]} removeIcon={false} count={0} />}</>
        ),
      },
      {
        id: 'isProvisioned',
        header: 'Provisioned',
        cell: ({ cell: { value } }: Cell<'isProvisioned'>) => (
          <>{value && <Tag colorIndex={14} name={'Provisioned'} />}</>
        ),
      },
      {
        id: 'isDisabled',
        header: '',
        cell: ({ cell: { value } }: Cell<'isDisabled'>) => <>{value && <Tag colorIndex={9} name={'Disabled'} />}</>,
      },
      {
        id: 'delete',
        header: '',
        cell: ({ row: { original } }: Cell) => {
          return (
            contextSrv.hasPermissionInMetadata(AccessControlAction.OrgUsersRemove, original) && (
              <Button
                size="sm"
                variant="destructive"
                onClick={() => {
                  setUserToRemove(original);
                }}
                icon="times"
                aria-label={t('admin.org-users-table.delete-aria-label', 'Delete user: {{name}}', {
                  name: original.name,
                })}
              />
            )
          );
        },
      },
    ],
    [rolesLoading, orgId, roleOptions, onUserRolesChange, onRoleChange]
  );

  return (
    <Stack direction={'column'} gap={2} data-testid={selectors.container}>
      <InteractiveTable columns={columns} data={users} getRowId={(user) => String(user.userId)} fetchData={fetchData} />
      <Stack justifyContent="flex-end">
        <Pagination onNavigate={changePage} currentPage={page} numberOfPages={totalPages} hideWhenSinglePage={true} />
      </Stack>
      {Boolean(userToRemove) && (
        <ConfirmModal
          body={t('admin.org-users-table.body-delete', 'Are you sure you want to delete user {{user}}?', {
            user: userToRemove?.login,
          })}
          confirmText={t('admin.org-users-table.confirmText-delete', 'Delete')}
          title={t('admin.org-users-table.title-delete', 'Delete')}
          onDismiss={() => {
            setUserToRemove(null);
          }}
          isOpen={true}
          onConfirm={() => {
            if (!userToRemove) {
              return;
            }
            onRemoveUser(userToRemove);
            setUserToRemove(null);
          }}
        />
      )}
    </Stack>
  );
};
