import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"
import styled from "@emotion/styled"
import Fuse from "fuse.js"
import Icon from "@aet/icons/macro"
import { useTranslation } from "react-i18next"
import { createContext, useContextSelector } from "use-context-selector"
import { useEvent } from "@aet/hooks"
import type { FixedSizeList } from "react-window"
import { type GetFileListQuery, gql, useQuery } from "~/generated/graphql"
import { LIGHT_MODE, token } from "~/options/theme"
import { useSelector } from "~/options/store"
import { SimpleList } from "./lists/SimpleList"
import { OpenedList } from "./lists/OpenedList"
import { GroupedList } from "./lists/GroupedList"
import { AddStyle as AddFile } from "./Adder"
import { useDebouncedValue } from "~/shared/hooks/useDebouncedValue"
import { FilterStateSelect, useFilterState } from "./FilterState"
import { SubheadingButton } from "~/shared/styled"
import { S, useLocalStorageState } from "~/shared/hooks/useLocalStorage"
import { IconModifierModal } from "./IconModifier"
import { SortDirection, SortStateSelect, sorters, useSorterState } from "./Sorter"

export type Tree = GetFileListQuery["files"]
export type TreeItem = Readonly<Tree[number]>

export const GET_FILES = gql<"GetFileList">`
  query GetFileList {
    files(sortBy: UpdatedAt) {
      enabled
      icon
      id
      lastRequestedAt
      name
      nameLanguage
      size
      state
      tags
      updatedAt
      subLabel
      rules {
        value
        type
        exclude
      }
    }
  }
`

interface TreeContext {
  selectedFiles: string[]
  setSelectedFiles: (ids: string[]) => void
  renamingFileID: string | null
  setRenamingFileID: (id: string | null) => void
  modifyingIconFileID: string | null
  setModifyingIconFileID: (id: string | null) => void
  toggleMultiSelect: () => void
}

const TreeContext = createContext<TreeContext>(null!)

/* eslint-disable react-hooks/rules-of-hooks */
export const useTreeContext = () => ({
  get selectedFiles() {
    return useContextSelector(TreeContext, s => s.selectedFiles)
  },
  get setSelectedFiles() {
    return useContextSelector(TreeContext, s => s.setSelectedFiles)
  },
  get renamingFileID() {
    return useContextSelector(TreeContext, s => s.renamingFileID)
  },
  get setRenamingFileID() {
    return useContextSelector(TreeContext, s => s.setRenamingFileID)
  },
  get modifyingIconFileID() {
    return useContextSelector(TreeContext, s => s.modifyingIconFileID)
  },
  get setModifyingIconFileID() {
    return useContextSelector(TreeContext, s => s.setModifyingIconFileID)
  },
  get toggleMultiSelect() {
    return useContextSelector(TreeContext, s => s.toggleMultiSelect)
  },
})
/* eslint-enable react-hooks/rules-of-hooks */

export const FilesTab = memo<ClassNameProp>(({ className }) => {
  const { t } = useTranslation()
  const listRef = useRef<FixedSizeList>(null)
  const activeId = useSelector(s => s.activeFileId)
  const [filterState, setFilterState] = useFilterState()

  const [sortState, setSortState] = useSorterState()
  const sortFn = sorters.get(sortState.type).fn

  const [renamingFileID, setRenamingFileID] = useState<string | null>(null)
  const [multiSelect, setMultiSelect] = useState(false)
  const [groupView, setGroupView] = useLocalStorageState(S.FileTreeGroupView, false)
  const [selectedFiles, setSelectedFiles] = useState<string[]>([])

  const [search, setSearch] = useState("")
  const debouncedSearch = useDebouncedValue(search, 50)

  const [modifyingIconFileID, setModifyingIconFileID] = useState<string | null>(null)

  const toggleMultiSelect = useEvent(() => {
    if (multiSelect) {
      setMultiSelect(false)
      setSelectedFiles([])
    } else {
      setMultiSelect(true)
      setSelectedFiles(activeId ? [activeId] : [])
    }
  })

  const toggleGroupView = useCallback(() => {
    setGroupView(x => !x)
  }, [setGroupView])

  const { data } = useQuery(GET_FILES)

  const list = useMemo(
    () =>
      (data?.files ?? [])
        .filter(filterState.value)
        .sort((a, b) => sortFn(a, b, sortState.order === SortDirection.Asc)),
    [data?.files, filterState, sortFn, sortState],
  )

  const fuse = useMemo(
    () =>
      new Fuse(list, {
        keys: ["name", "tags", "rules.value"],
        minMatchCharLength: 2,
        shouldSort: true,
        includeScore: true,
        threshold: 0.2,
      }),
    [list],
  )

  const filteredList = useMemo(
    () => (debouncedSearch ? fuse.search(debouncedSearch).map(r => r.item) : list),
    [list, fuse, debouncedSearch],
  )

  // Scroll to renaming item
  useEffect(() => {
    if (!renamingFileID || !listRef.current) return

    const index = filteredList.findIndex(s => s.id === renamingFileID)
    if (index === -1) return

    listRef.current.scrollToItem(index + 1)
  }, [renamingFileID, filteredList])

  return (
    <FilesTabContainer className={className}>
      <div>
        <Input
          type="search"
          placeholder={t("search.find")}
          value={search}
          onChange={e => setSearch(e.target.value)}
        />
      </div>

      <Subheader>
        <FilterStateSelect filterState={filterState} setFilterState={setFilterState} />
        <SubheaderIcons>
          <SortStateSelect sortBy={sortState} onChange={setSortState} />
          {list.length > 1 && (
            <SubheadingButton>
              <Icon
                icon="BiSelectMultiple"
                style={{ fill: multiSelect ? token.accent.fg : undefined }}
                onClick={toggleMultiSelect}
              />
            </SubheadingButton>
          )}
          <SubheadingButton>
            <Icon
              icon="VscLayers"
              onClick={toggleGroupView}
              style={{ fill: groupView ? token.accent.fg : undefined }}
            />
          </SubheadingButton>
          <AddFile />
        </SubheaderIcons>
      </Subheader>

      <TreeContext.Provider
        value={{
          selectedFiles,
          setSelectedFiles,
          renamingFileID,
          setRenamingFileID,
          modifyingIconFileID,
          setModifyingIconFileID,
          toggleMultiSelect,
        }}
      >
        <OpenedList data={data} />
        {groupView ? (
          <GroupedList list={filteredList} multiSelect={multiSelect} />
        ) : (
          <SimpleList list={filteredList} listRef={listRef} multiSelect={multiSelect} />
        )}
        <IconModifierModal />
      </TreeContext.Provider>
    </FilesTabContainer>
  )
})

const FilesTabContainer = styled.div`
  display: flex;
  flex-direction: column;
  border-radius: 5px;
  padding: 7px 10px;
  height: 100%;

  ${LIGHT_MODE} {
    background-color: #f7f8fa;
  }
`

const Input = styled.input`
  background-color: ${token.canvas.overlay};
  border-radius: 75px;
  border: 1px solid ${token.border.default};
  padding: 5px 10px;
  width: 100%;
  &:focus {
    border-color: #5f677a;
  }
`

const Subheader = styled.div`
  display: flex;
  margin-top: 12px;
  padding-left: 11px;
`

const SubheaderIcons = styled.div`
  display: flex;
  align-items: center;
`
