import { useCallback, useRef, useState, useMemo, useEffect } from 'react'
import Link from 'next/link'
import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
import { Button } from '../../elements/Button'
import { StyledText } from '../../elements/StyledText'
import { CrossIcon } from '../../elements/images/CrossIcon'
import { styled, theme } from '../../tokens/stitches.config'
import { Label } from '../../../lib/networking/fragments/labelFragment'
import { useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQuery'
import { Check, Circle, PencilSimple, Plus } from 'phosphor-react'
import { isTouchScreenDevice } from '../../../lib/deviceType'
import { createLabelMutation } from '../../../lib/networking/mutations/createLabelMutation'
import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
import { randomLabelColorHex } from '../../../utils/settings-page/labels/labelColorObjects'
import { useRouter } from 'next/router'

export interface LabelsProvider {
  labels?: Label[]
}

type SetLabelsControlProps = {
  provider: LabelsProvider

  selectedLabels: Label[]
  setSelectedLabels: (labels: Label[]) => void

  onLabelsUpdated?: (labels: Label[]) => void
}

type HeaderProps = {
  filterText: string
  focused: boolean
  resetFocusedIndex: () => void
  setFilterText: (text: string) => void
}

const FormInput = styled('input', {
  width: '100%',
  fontSize: '16px',
  fontFamily: 'inter',
  fontWeight: 'normal',
  lineHeight: '1.8',
  color: '$grayTextContrast',
  '&:focus': {
    outline: 'none',
  },
})

const StyledLabel = styled('label', {
  display: 'flex',
  justifyContent: 'flex-start',
})

function Header(props: HeaderProps): JSX.Element {
  const inputRef = useRef<HTMLInputElement>(null)

  useEffect(() => {
    if (!isTouchScreenDevice() && props.focused && inputRef.current) {
      inputRef.current.focus()
    }
  }, [props.focused])

  return (
    <VStack
      css={{ width: '100%', my: '0px', borderBottom: '1px solid $grayBorder' }}
    >
      <Box
        css={{
          width: '100%',
          my: '14px',
          px: '14px',
        }}
      >
        <FormInput
          ref={inputRef}
          type="text"
          tabIndex={props.focused && !isTouchScreenDevice() ? 0 : -1}
          autoFocus={!isTouchScreenDevice()}
          value={props.filterText}
          placeholder="Filter for label"
          onChange={(event) => {
            props.setFilterText(event.target.value)
          }}
          onFocus={() => {
            props.resetFocusedIndex()
          }}
          css={{
            border: '1px solid $grayBorder',
            borderRadius: '8px',
            width: '100%',
            bg: 'transparent',
            fontSize: '16px',
            textIndent: '8px',
            marginBottom: '2px',
            color: '$grayTextContrast',
            '&:focus': {
              outline: 'none',
              boxShadow: '0px 0px 2px 2px rgba(255, 234, 159, 0.56)',
            },
          }}
        />
      </Box>
    </VStack>
  )
}

type LabelListItemProps = {
  label: Label
  focused: boolean
  selected: boolean
  toggleLabel: (label: Label) => void
}

function LabelListItem(props: LabelListItemProps): JSX.Element {
  const ref = useRef<HTMLLabelElement>(null)
  const { label, focused, selected } = props

  useEffect(() => {
    if (props.focused && ref.current) {
      ref.current.focus()
    }
  }, [props.focused])

  return (
    <StyledLabel
      ref={ref}
      css={{
        width: '100%',
        height: '42px',
        borderBottom: '1px solid $grayBorder',
        bg: props.focused ? '$grayBgActive' : 'unset',
      }}
      tabIndex={props.focused ? 0 : -1}
      onClick={(event) => {
        event.preventDefault()
        props.toggleLabel(label)
        ref.current?.blur()
      }}
    >
      <input
        autoFocus={focused}
        hidden={true}
        type="checkbox"
        checked={selected}
        readOnly
      />
      <Box
        css={{
          pl: '10px',
          width: '32px',
          display: 'flex',
          alignItems: 'center',
        }}
      >
        {selected && (
          <Check
            size={15}
            color={theme.colors.grayText.toString()}
            weight="bold"
          />
        )}
      </Box>
      <Box
        css={{
          width: '30px',
          height: '100%',
          display: 'flex',
          alignItems: 'center',
        }}
      >
        <Circle width={22} height={22} color={label.color} weight="fill" />
      </Box>
      <Box
        css={{
          overflow: 'clip',
          height: '100%',
          display: 'flex',
          alignItems: 'center',
        }}
      >
        <StyledText style="caption">{label.name}</StyledText>
      </Box>
      <Box
        css={{
          pl: '10px',
          width: '40px',
          marginLeft: 'auto',
          display: 'flex',
          alignItems: 'center',
        }}
      >
        {selected && (
          <CrossIcon size={14} strokeColor={theme.colors.grayText.toString()} />
        )}
      </Box>
    </StyledLabel>
  )
}

type FooterProps = {
  focused: boolean
}

function Footer(props: FooterProps): JSX.Element {
  const ref = useRef<HTMLDivElement>(null)

  useEffect(() => {
    if (props.focused && ref.current) {
      ref.current.focus()
    }
  }, [props.focused])

  return (
    <HStack
      ref={ref}
      distribution="start"
      alignment="center"
      css={{
        width: '100%',
        height: '42px',
        bg: props.focused ? '$grayBgActive' : 'unset',
        color: theme.colors.grayText.toString(),
        'a:link': {
          textDecoration: 'none',
        },
        'a:visited': {
          color: theme.colors.grayText.toString(),
        },
      }}
    >
      <SpanBox
        css={{ display: 'flex', fontSize: '12px', padding: '33px', gap: '8px' }}
      >
        <PencilSimple size={18} color={theme.colors.grayText.toString()} />
        <Link href="/settings/labels">Edit labels</Link>
      </SpanBox>
    </HStack>
  )
}

export function SetLabelsControl(props: SetLabelsControlProps): JSX.Element {
  const router = useRouter()
  const [filterText, setFilterText] = useState('')
  const { labels, revalidate } = useGetLabelsQuery()

  useEffect(() => {
    setFocusedIndex(undefined)
  }, [filterText])

  const isSelected = useCallback(
    (label: Label): boolean => {
      return props.selectedLabels.some((other) => {
        return other.id === label.id
      })
    },
    [props.selectedLabels]
  )

  const toggleLabel = useCallback(
    async (label: Label) => {
      let newSelectedLabels = [...props.selectedLabels]
      if (isSelected(label)) {
        newSelectedLabels = props.selectedLabels.filter((other) => {
          return other.id !== label.id
        })
      } else {
        newSelectedLabels = [...props.selectedLabels, label]
      }
      props.setSelectedLabels(newSelectedLabels)
      props.provider.labels = newSelectedLabels

      if (props.onLabelsUpdated) {
        props.onLabelsUpdated(newSelectedLabels)
      }

      revalidate()
    },
    [isSelected, props, revalidate]
  )

  const filteredLabels = useMemo(() => {
    if (!labels) {
      return []
    }
    return labels
      .filter((label) => {
        return label.name.toLowerCase().includes(filterText.toLowerCase())
      })
      .sort((left: Label, right: Label) => {
        return left.name.localeCompare(right.name)
      })
  }, [labels, filterText])

  // Move focus through the labels list on tab or arrow up/down keys
  const [focusedIndex, setFocusedIndex] = useState<number | undefined>(
    undefined
  )

  const createLabelFromFilterText = useCallback(async () => {
    const label = await createLabelMutation(
      filterText,
      randomLabelColorHex(),
      ''
    )
    if (label) {
      showSuccessToast(`Created label ${label.name}`, {
        position: 'bottom-right',
      })
      toggleLabel(label)
    } else {
      showErrorToast('Failed to create label', { position: 'bottom-right' })
    }
  }, [filterText, toggleLabel])

  const handleKeyDown = useCallback(
    async (event: React.KeyboardEvent<HTMLInputElement>) => {
      const maxIndex = filteredLabels.length + 1
      if (event.key === 'ArrowUp') {
        event.preventDefault()
        let newIndex = focusedIndex
        if (focusedIndex) {
          newIndex = Math.max(0, focusedIndex - 1)
        } else {
          newIndex = undefined
        }
        // If the `Create New label` button isn't visible we skip it
        // when navigating with the arrow keys
        if (focusedIndex === maxIndex && !filterText) {
          newIndex = maxIndex - 2
        }
        setFocusedIndex(newIndex)
      }
      if (event.key === 'ArrowDown' || event.key === 'Tab') {
        event.preventDefault()
        let newIndex = focusedIndex
        if (focusedIndex === undefined) {
          newIndex = 0
        } else {
          newIndex = Math.min(maxIndex, focusedIndex + 1)
        }
        // If the `Create New label` button isn't visible we skip it
        // when navigating with the arrow keys
        if (focusedIndex === maxIndex - 2 && !filterText) {
          newIndex = maxIndex
        }
        setFocusedIndex(newIndex)
      }
      if (event.key === 'Enter') {
        event.preventDefault()
        if (focusedIndex === maxIndex) {
          router.push('/settings/labels')
          return
        }
        if (focusedIndex === maxIndex - 1) {
          await createLabelFromFilterText()
          return
        }
        if (focusedIndex !== undefined) {
          const label = filteredLabels[focusedIndex]
          if (label) {
            toggleLabel(label)
          }
        }
      }
    },
    [
      filterText,
      filteredLabels,
      focusedIndex,
      createLabelFromFilterText,
      router,
      toggleLabel,
    ]
  )

  return (
    <VStack
      distribution="start"
      onKeyDown={handleKeyDown}
      css={{
        p: '0',
        width: '100%',
      }}
    >
      <Header
        focused={focusedIndex === undefined}
        resetFocusedIndex={() => setFocusedIndex(undefined)}
        setFilterText={setFilterText}
        filterText={filterText}
      />
      <VStack
        distribution="start"
        alignment="start"
        css={{
          flexGrow: '1',
          width: '100%',
          height: '294px',
          overflowY: 'scroll',
        }}
      >
        {filteredLabels &&
          filteredLabels.map((label, idx) => (
            <LabelListItem
              key={label.id}
              label={label}
              focused={idx === focusedIndex}
              selected={isSelected(label)}
              toggleLabel={toggleLabel}
            />
          ))}
      </VStack>
      {filterText && (
        <Button
          style="modalOption"
          css={{
            pl: '26px',
            color: theme.colors.grayText.toString(),
            height: '42px',
            borderBottom: '1px solid $grayBorder',
            bg:
              focusedIndex === filteredLabels.length
                ? '$grayBgActive'
                : 'unset',
          }}
          onClick={createLabelFromFilterText}
        >
          <HStack alignment="center" distribution="start" css={{ gap: '8px' }}>
            <Plus size={18} color={theme.colors.grayText.toString()} />
            <SpanBox
              css={{ fontSize: '12px' }}
            >{`Create new label "${filterText}"`}</SpanBox>
          </HStack>
        </Button>
      )}

      <Footer focused={focusedIndex === filteredLabels.length + 1} />
    </VStack>
  )
}
