import SortableTable from '@/components/SortableTable'
import type {
  Column,
  SelectingRow,
  ValueType,
} from '@/components/SortableTable/typing'
import type { User } from '@inject/graphql/fragment-types'
import { useTypedQuery } from '@inject/graphql/graphql'
import { GetUsers } from '@inject/graphql/queries'
import notEmpty from '@inject/shared/utils/notEmpty'
import type { CSSProperties } from 'react'
import { useMemo } from 'react'
import useDeleteUsers from '../useDeleteUsers'
import useFilterUsers from './useFilterUsers'
import type { AUTH_GROUPS, UserProperty } from './utils'

interface UserColumn extends Column {
  getValue: (element: User) => ValueType
}

const activeToBool = (active: { active: boolean; inactive: boolean }) => {
  if (active.active && active.inactive) return undefined
  if (active.active) return true
  if (active.inactive) return false
  return undefined
}

type UserTableProps = {
  onClick: (id: string) => void
  selectedUsers?: string[]
  groups: typeof AUTH_GROUPS
  active: {
    active: boolean
    inactive: boolean
  }
  tags?: string[]
  searchString?: string
  properties: UserProperty[]
} & (
  | {
      onSelectAll?: never
      onDeselectAll?: never
    }
  | {
      onSelectAll: (allIds: string[]) => void
      onDeselectAll: () => void
    }
) &
  (
    | {
        searchString?: string
        filterPredicate?: never
      }
    | {
        searchString?: never
        filterPredicate: (user: User) => boolean
      }
  )

const useUserTable = ({
  onClick,
  selectedUsers,
  groups,
  active,
  tags,
  searchString,
  properties,
  onSelectAll,
  onDeselectAll,
  filterPredicate,
}: UserTableProps) => {
  const [{ data: usersData, fetching: loading }] = useTypedQuery({
    query: GetUsers,
    requestPolicy: 'network-only',
    variables: {
      active: activeToBool(active),
      groups,
      tags: tags && tags.length > 0 ? tags : undefined,
    },
    pause: !active.active && !active.inactive,
  })

  const displayedProperties = useMemo(
    () => properties.filter(property => property.display !== false),
    [properties]
  )
  const columns: UserColumn[] = useMemo(() => {
    const style: CSSProperties = {
      width: `${100 / displayedProperties.length}%`,
    }

    return displayedProperties.map(property => ({
      ...property,
      style,
    }))
  }, [displayedProperties])

  const filteredUsers = useFilterUsers({
    users: usersData?.users?.filter(notEmpty) || [],
    ...(filterPredicate ? { predicate: filterPredicate } : { searchString }),
  })

  const { button, alert } = useDeleteUsers({
    userIds: filteredUsers.map(user => user.id),
    showCount: true,
  })

  const rows: SelectingRow[] = filteredUsers.map(user => ({
    id: user.id,
    columns,
    values: columns.map(column => column.getValue(user)),
    onClick: () => onClick(user.id),
    isSelected: selectedUsers?.includes(user.id) || false,
  }))

  // can't do this directly in the props because of a false-positive TS error
  const selectingTableProps = useMemo(
    () =>
      onSelectAll !== undefined
        ? {
            allSelected: selectedUsers?.length === filteredUsers.length,
            onSelectAll: () => onSelectAll(filteredUsers.map(user => user.id)),
            onDeselectAll,
          }
        : {},
    [filteredUsers, onDeselectAll, onSelectAll, selectedUsers?.length]
  )

  return {
    table: (
      <SortableTable
        columns={columns}
        rows={rows}
        loading={loading}
        {...selectingTableProps}
      />
    ),
    deleteButton: (
      <>
        {button}
        {alert}
      </>
    ),
  }
}

export default useUserTable
