import React, {
  SyntheticEvent,
  useEffect,
  useState,
  useCallback,
  useRef,
  FormEvent,
} from 'react'
import _ from 'lodash'
import * as Tabs from '@radix-ui/react-tabs'
import { useRecoilState, useSetRecoilState } from 'recoil'
import PhotoAlbum from 'react-photo-album'
import { BarsArrowDownIcon, BarsArrowUpIcon } from '@heroicons/react/24/outline'
import { MagnifyingGlassIcon } from '@radix-ui/react-icons'
import { useDebounce } from 'react-use'
import { Id, Index, IndexSearchResult } from 'flexsearch'
import * as ScrollArea from '@radix-ui/react-scroll-area'
import Modal from '../shared/Modal'
import Flex from '../shared/Layout'
import {
  fileManagerLayout,
  fileManagerSortBy,
  fileManagerSortOrder,
  SortBy,
  SortOrder,
  toastState,
} from '../../store/Atoms'
import { getMedias } from '../../adapters/inpainting'
import Selector from '../shared/Selector'
import Button from '../shared/Button'
import TextInput from '../shared/Input'

interface Photo {
  src: string
  height: number
  width: number
  name: string
}

interface Filename {
  name: string
  height: number
  width: number
  ctime: number
  mtime: number
}

const SORT_BY_NAME = 'Name'
const SORT_BY_CREATED_TIME = 'Created time'
const SORT_BY_MODIFIED_TIME = 'Modified time'

const IMAGE_TAB = 'image'
const OUTPUT_TAB = 'output'

const SortByMap = {
  [SortBy.NAME]: SORT_BY_NAME,
  [SortBy.CTIME]: SORT_BY_CREATED_TIME,
  [SortBy.MTIME]: SORT_BY_MODIFIED_TIME,
}

interface Props {
  show: boolean
  onClose: () => void
  onPhotoClick(tab: string, filename: string): void
  photoWidth: number
}

export default function FileManager(props: Props) {
  const { show, onClose, onPhotoClick, photoWidth } = props
  const [scrollTop, setScrollTop] = useState(0)
  const [closeScrollTop, setCloseScrollTop] = useState(0)
  const setToastState = useSetRecoilState(toastState)

  const [sortBy, setSortBy] = useRecoilState<SortBy>(fileManagerSortBy)
  const [sortOrder, setSortOrder] = useRecoilState(fileManagerSortOrder)
  const [layout, setLayout] = useRecoilState(fileManagerLayout)

  const ref = useRef(null)
  const [searchText, setSearchText] = useState('')
  const [debouncedSearchText, setDebouncedSearchText] = useState('')
  const [tab, setTab] = useState(IMAGE_TAB)
  const [photos, setPhotos] = useState<Photo[]>([])

  const [, cancel] = useDebounce(
    () => {
      setDebouncedSearchText(searchText)
    },
    500,
    [searchText]
  )

  useEffect(() => {
    if (!show) {
      setCloseScrollTop(scrollTop)
    }
  }, [show, scrollTop])

  const onRefChange = useCallback(
    (node: HTMLDivElement) => {
      if (node !== null) {
        if (show) {
          setTimeout(() => {
            // TODO: without timeout, scrollTo not work, why?
            node.scrollTo({ top: closeScrollTop, left: 0 })
          }, 100)
        }
      }
    },
    [show, closeScrollTop]
  )

  useEffect(() => {
    if (!show) {
      return
    }
    const fetchData = async () => {
      try {
        const filenames = await getMedias(tab)
        let filteredFilenames = filenames
        if (debouncedSearchText) {
          const index = new Index()
          filenames.forEach((filename: Filename, id: number) =>
            index.add(id, filename.name)
          )
          const results: IndexSearchResult = index.search(debouncedSearchText)
          filteredFilenames = results.map((id: Id) => filenames[id as number])
        }

        filteredFilenames = _.orderBy(filteredFilenames, sortBy, sortOrder)

        const newPhotos = filteredFilenames.map((filename: Filename) => {
          const width = photoWidth
          const height = filename.height * (width / filename.width)
          const src = `/media_thumbnail/${tab}/${filename.name}?width=${width}&height=${height}`
          return { src, height, width, name: filename.name }
        })
        setPhotos(newPhotos)
      } catch (e: any) {
        setToastState({
          open: true,
          desc: e.message ? e.message : e.toString(),
          state: 'error',
          duration: 2000,
        })
      }
    }
    fetchData()
  }, [
    setToastState,
    tab,
    debouncedSearchText,
    sortBy,
    sortOrder,
    photoWidth,
    show,
  ])

  const onScroll = (event: SyntheticEvent) => {
    setScrollTop(event.currentTarget.scrollTop)
  }

  const onClick = ({ index }: { index: number }) => {
    onPhotoClick(tab, photos[index].name)
  }

  return (
    <Modal
      onClose={onClose}
      // TODO：layout switch 放到标题中
      title={`Images (${photos.length})`}
      className="file-manager-modal"
      show={show}
    >
      <Flex style={{ justifyContent: 'space-between', gap: 8 }}>
        <Tabs.Root
          className="TabsRoot"
          defaultValue={tab}
          onValueChange={val => setTab(val)}
        >
          <Tabs.List className="TabsList" aria-label="Manage your account">
            <Tabs.Trigger className="TabsTrigger" value={IMAGE_TAB}>
              Image Directory
            </Tabs.Trigger>
            <Tabs.Trigger className="TabsTrigger" value={OUTPUT_TAB}>
              Output Directory
            </Tabs.Trigger>
          </Tabs.List>
        </Tabs.Root>
        <Flex style={{ gap: 8 }}>
          <Flex
            style={{
              position: 'relative',
              justifyContent: 'start',
            }}
          >
            <MagnifyingGlassIcon style={{ position: 'absolute', left: 8 }} />
            <TextInput
              ref={ref}
              value={searchText}
              className="file-search-input"
              tabIndex={-1}
              onInput={(evt: FormEvent<HTMLInputElement>) => {
                evt.preventDefault()
                evt.stopPropagation()
                const target = evt.target as HTMLInputElement
                setSearchText(target.value)
              }}
              placeholder="Search by file name"
            />
          </Flex>
          <Flex style={{ gap: 8 }}>
            <Selector
              width={140}
              value={SortByMap[sortBy]}
              options={Object.values(SortByMap)}
              onChange={val => {
                switch (val) {
                  case SORT_BY_NAME:
                    setSortBy(SortBy.NAME)
                    break
                  case SORT_BY_CREATED_TIME:
                    setSortBy(SortBy.CTIME)
                    break
                  case SORT_BY_MODIFIED_TIME:
                    setSortBy(SortBy.MTIME)
                    break
                  default:
                    break
                }
              }}
              chevronDirection="down"
            />
            <Button
              icon={<BarsArrowDownIcon />}
              toolTip="Descending order"
              onClick={() => {
                setSortOrder(SortOrder.DESCENDING)
              }}
              className={
                sortOrder !== SortOrder.DESCENDING ? 'sort-btn-inactive' : ''
              }
            />
            <Button
              icon={<BarsArrowUpIcon />}
              toolTip="Ascending order"
              onClick={() => {
                setSortOrder(SortOrder.ASCENDING)
              }}
              className={
                sortOrder !== SortOrder.ASCENDING ? 'sort-btn-inactive' : ''
              }
            />
          </Flex>
        </Flex>
      </Flex>
      <ScrollArea.Root className="ScrollAreaRoot">
        <ScrollArea.Viewport
          className="ScrollAreaViewport"
          onScroll={onScroll}
          ref={onRefChange}
        >
          <PhotoAlbum
            layout={layout}
            photos={photos}
            spacing={8}
            padding={0}
            onClick={onClick}
          />
        </ScrollArea.Viewport>
        <ScrollArea.Scrollbar
          className="ScrollAreaScrollbar"
          orientation="vertical"
        >
          <ScrollArea.Thumb className="ScrollAreaThumb" />
        </ScrollArea.Scrollbar>
        {/* <ScrollArea.Scrollbar
          className="ScrollAreaScrollbar"
          orientation="horizontal"
        >
          <ScrollArea.Thumb className="ScrollAreaThumb" />
        </ScrollArea.Scrollbar> */}
        <ScrollArea.Corner className="ScrollAreaCorner" />
      </ScrollArea.Root>
    </Modal>
  )
}
