import styled from "@emotion/styled"
import Icon from "@aet/icons/macro"
import { Listbox } from "@headlessui/react"
import { css, cx } from "@emotion/css"
import { findLast } from "lodash"
import { useTranslation } from "react-i18next"
import { memo, useMemo } from "react"
import { useEvent } from "@aet/hooks"
import { getMap } from "~/options/utils/componentMap"
import { S, useLocalStorageState } from "~/shared/hooks/useLocalStorage"
import type { TreeItem } from "./index"
import type { TranslationKey } from "~/shared/i18n/data.generated"
import { SubheadingButton } from "~/shared/styled"
import { subheadingOptions } from "./FilterState"

export const enum SortDirection {
  Asc = "Asc",
  Desc = "Desc",
}

export const enum SortType {
  Name = "NameAsc",
  Size = "SizeAsc",
  LastModified = "LastModified",
  LastUsed = "LastUsed",
}

const sortDirectionNames = getMap<SortDirection, TranslationKey>(() => ({
  [SortDirection.Asc]: "sortBy.asc",
  [SortDirection.Desc]: "sortBy.desc",
}))

export const sorters = getMap<
  SortType,
  {
    name: TranslationKey
    fn: (a: TreeItem, b: TreeItem, asc: boolean) => number
  }
>(() => ({
  [SortType.Name]: {
    name: "sortBy.name",
    fn: (a, b, asc) => a.name.localeCompare(b.name) * (asc ? 1 : -1),
  },
  [SortType.Size]: {
    name: "sortBy.size",
    fn: (a, b, asc) => compareNumber(a.size, b.size, asc),
  },
  [SortType.LastModified]: {
    name: "sortBy.lastModified",
    fn: (a, b, asc) => compareDate(b.updatedAt, a.updatedAt, asc),
  },
  [SortType.LastUsed]: {
    name: "sortBy.lastUsed",
    fn: (a, b, asc) => compareDate(a.lastRequestedAt, b.lastRequestedAt, asc),
  },
}))

interface SortState {
  type: SortType
  order: SortDirection
}

const defaultSortState: SortState = {
  type: SortType.LastModified,
  order: SortDirection.Desc,
}

export function useSorterState() {
  return useLocalStorageState(S.FileTreeSorting, defaultSortState)
}

export const SortStateSelect = memo<{
  sortBy: SortState
  onChange: (key: SortState) => void
}>(({ sortBy, onChange }) => {
  const { t } = useTranslation()

  const sortValue = useMemo(() => [sortBy.type, sortBy.order], [sortBy])

  return (
    <Listbox
      name="filterState"
      multiple
      as="div"
      value={sortValue}
      onChange={useEvent(e => {
        const lastType = findLast(e, key => sorters.has(key as SortType)) as
          | SortType
          | undefined
        const lastOrder = findLast(e, key =>
          sortDirectionNames.has(key as SortDirection),
        ) as SortDirection | undefined
        if (lastType !== sortBy.type || lastOrder !== sortBy.order) {
          onChange({
            type: lastType ?? sortBy.type,
            order: lastOrder ?? sortBy.order,
          })
        }
      })}
    >
      {({ open }) => (
        <>
          <Select>
            <SubheadingButton className={cx({ active: open })}>
              <Icon icon="BsSortUp" title={t(sorters.get(sortBy.type).name)} />
            </SubheadingButton>
          </Select>
          <Options
            className={cx(
              subheadingOptions,
              css`
                li {
                  padding-left: 8px;
                  > svg {
                    margin-right: 5px;
                  }
                }
              `,
            )}
          >
            {sorters.map(({ name }, type) => {
              const active = type === sortBy.type
              return (
                <Listbox.Option key={type} value={type} className={cx({ active })}>
                  {active ? (
                    <Icon icon="BsCheckLg" />
                  ) : (
                    <svg style={{ width: "1em", height: "1em" }} />
                  )}
                  {t(name)}
                </Listbox.Option>
              )
            })}
            <hr />
            {[SortDirection.Asc, SortDirection.Desc].map(type => {
              const active = type === sortBy.order
              return (
                <Listbox.Option key={type} value={type} className={cx({ active })}>
                  {active ? (
                    <Icon icon="BsCheckLg" />
                  ) : (
                    <svg style={{ width: "1em", height: "1em" }} />
                  )}
                  {t(sortDirectionNames.get(type))}
                </Listbox.Option>
              )
            })}
          </Options>
        </>
      )}
    </Listbox>
  )
})

const Select = styled(Listbox.Button)`
  border: none;
  padding: 0;
  margin-top: 2px;
  font-size: 1.1em;
`

const Options = styled(Listbox.Options)`
  z-index: 5;
  right: 10px;
  margin-top: 3px;
`

function compareNumber(
  a: number | null | undefined,
  b: number | null | undefined,
  asc: boolean,
) {
  if (a == null && b == null) {
    return 0
  } else if (a == null) {
    return asc ? 1 : -1
  } else if (b == null) {
    return asc ? -1 : 1
  } else {
    return asc ? a - b : b - a
  }
}
function compareDate(
  a: Date | null | undefined,
  b: Date | null | undefined,
  asc: boolean,
) {
  if (!a && !b) {
    return 0
  } else if (!a) {
    return asc ? 1 : -1
  } else if (!b) {
    return asc ? -1 : 1
  } else {
    const dateA = a.getTime()
    const dateB = b.getTime()
    return asc ? dateA - dateB : dateB - dateA
  }
}
