import styled from "@emotion/styled"
import { memo, useMemo, useState } from "react"
import { produce } from "immer"
import { css } from "@emotion/css"
import Icon from "@aet/icons/macro"
import { ControlledMenu, MenuItem } from "@szhsin/react-menu"
import { useTranslation } from "react-i18next"
import { useAutoMemo } from "hooks.macro"
import { useEvent } from "@aet/hooks"
import { gql, useMutation } from "~/generated/graphql"
import { type Tree, useTreeContext } from "../index"
import { useFileListRenderer } from "./SimpleList"
import { S, useLocalStorageState } from "~/shared/hooks/useLocalStorage"
import { AbstractBaseFileItem } from "../FileItem"
import { useDropdownMenu } from "~/options/hooks/useDropdownMenu"

const RENAME_TAG_MUTATION = gql<"RenameTag">`
  mutation RenameTag($oldTag: String!, $newTag: String!) {
    renameTag(oldTag: $oldTag, newTag: $newTag) {
      id
      tags
    }
  }
`

export const GroupedList = memo<{
  list: Tree
  multiSelect: boolean
}>(({ list, multiSelect }) => {
  const [expandedGroups, setExpandedGroups] = useLocalStorageState(
    S.FileTreeVisibleGroups,
    new Set<string>(),
  )

  const onToggleGroup = useEvent((tag: string) => {
    setExpandedGroups(groups =>
      produce(groups, draft => {
        if (draft.has(tag)) {
          draft.delete(tag)
        } else {
          draft.add(tag)
        }
      }),
    )
  })

  const tagMap = useMemo(() => {
    const map = new Map<string, Tree>()
    for (const item of list) {
      for (const tag of item.tags) {
        if (map.has(tag)) {
          map.get(tag)!.push(item)
        } else {
          map.set(tag, [item])
        }
      }
    }
    return Array.from(map.entries()).sort(([a], [b]) => a.localeCompare(b))
  }, [list])

  return (
    <GroupedListContainer>
      {tagMap.map(([tag, items]) => (
        <Group
          expanded={expandedGroups.has(tag)}
          key={tag}
          tag={tag}
          items={items}
          multiSelect={multiSelect}
          onToggleGroup={onToggleGroup}
        />
      ))}
    </GroupedListContainer>
  )
})

const Group = memo<{
  tag: string
  items: Tree
  multiSelect: boolean
  expanded: boolean
  onToggleGroup: (tag: string) => void
}>(props => {
  const { t } = useTranslation()
  const { tag, items, multiSelect, expanded, onToggleGroup } = props
  const [renderChild, setRenderChild] = useState(expanded)
  const [renameTag] = useMutation(RENAME_TAG_MUTATION)
  const { menuProps, onMouse } = useDropdownMenu()
  const render = useFileListRenderer()
  const { setRenamingFileID } = useTreeContext()

  if (!renderChild && expanded) {
    setRenderChild(true)
  }

  const onStartRename = useEvent(() => {
    setRenamingFileID(`tag-${tag}`)
  })

  return (
    <>
      {menuProps.state === "open" && (
        <ControlledMenu {...menuProps}>
          <MenuItem onClick={onStartRename}>
            <Icon icon="BiRename" />
            {t("editor.verbRename")}
          </MenuItem>
        </ControlledMenu>
      )}
      <AbstractBaseFileItem
        active={false}
        key={tag}
        onClick={useEvent(() => onToggleGroup(tag))}
        onContextMenu={onMouse}
        onRename={useEvent(async (name: string) => {
          await renameTag({ variables: { oldTag: tag, newTag: name } })
        })}
        item={useAutoMemo(() => ({ id: `tag-${tag}`, name: tag }))}
        icon={expanded ? <Icon icon="VscChevronDown" /> : <Icon icon="VscChevronRight" />}
        className={css`
          height: 30px;
          > svg {
            font-size: 19px;
            margin: -3px 2px -3px -7px;
          }
        `}
      />
      {renderChild && (
        <div style={expanded ? undefined : { display: "none" }}>
          {items.map(item => render({ item, className: listItemClass, multiSelect }))}
        </div>
      )}
    </>
  )
})

const GroupedListContainer = styled.div`
  overflow-y: scroll;
`

const listItemClass = css`
  && {
    padding: 4px 11px 4px 20px;
  }
`
