'use client'

import * as React from 'react'
import {CheckIcon, ChevronsUpDown} from 'lucide-react'

import {merge} from '@/lib/utils'
import {Button} from '@/components/ui/button'
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from '@/components/ui/popover'
import {ReactNode, useRef, useState} from 'react'
import {Input} from '@/components/ui/input'

type ComboboxItem = {
  value: string
  label: ReactNode
  alias?: string[]
  children?: ComboboxItem[]
  labelBuild?: (item: ComboboxItem) => string
}

type ComboboxProps = {
  className?: string
  placeholder?: string
  options: ComboboxItem[]
  value?: string[]
  onChange?: (value: string[]) => void
  children?: React.ReactNode
}

export function Combobox(props: ComboboxProps) {
  const [open, setOpen] = useState(false)

  const getLabel = (item: ComboboxItem) => {
    return typeof item.label === 'object' || typeof item.label === 'function'
      ? item.labelBuild?.(item)
      : `${item?.label}`
  }

  let items: ComboboxItem[] | undefined = props.options
  const label: string[] = []
  const values: string[] = []
  props.value?.forEach((value) => {
    if (items) {
      const curr = items.find(item => item.value === value)
      if (curr) {
        items = curr.children
        values.push(curr.value)
        label.push(getLabel(curr) || '-')
      }
    }
  })

  const [wait, setWait] = useState(false)
  const [filter, setFilter] = useState<string>('')
  const [filtered, setFiltered] = useState<ComboboxItem[]>([])

  const onFilter = async () => {
    if (wait) return
    const cond = filter.trim()
    console.log('onFilter', cond)
    setWait(true)
    if (cond.length > 0) {
      setFiltered(mapFilter(JSON.parse(JSON.stringify(props.options)), cond))
    }
    else {
      setFiltered(props.options)
    }
    console.log('onFilter end')
    setWait(false)
  }

  const mapFilter = (items: ComboboxItem[], cond: string): ComboboxItem[] => {
    const nItems: ComboboxItem[] = []
    items.forEach((item) => {
      const label = getLabel(item)
      const match = label?.includes(cond)

      if (item.children?.length) {
        item.children = mapFilter(item.children, cond)
      }

      if (match || item.children?.length) {
        nItems.push(item)
      }
    })
    return nItems
  }

  return (
    <Popover
      open={open}
      onOpenChange={(status) => {
        setOpen(status)
        if (status) {
          setFiltered(props.options)
          setFilter('')
        }
      }}>
      <PopoverTrigger asChild>
        <Button
          theme="outline"
          role="combobox"
          aria-expanded={open}
          className={merge(
            `flex justify-between`,
            props.className,
          )}
        >
          {label.length
            ? <span className="text-sm">{label.join('/')}</span>
            : <span className="text-sm text-weak">{props.placeholder}</span>
          }
          <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50"/>
        </Button>
      </PopoverTrigger>
      <PopoverContent
        className="p-0 rounded-lg w-[var(--radix-popover-trigger-width)] h-[var(--radix-popover-content-available-height)] flex flex-col overflow-hidden"
        align="start"
        collisionPadding={6}
      >
        <div className="p-2 flex gap-2 flex-none">
          <Input
            className="h-9 placeholder:text-weak placeholder:text-sm"
            placeholder="搜索地区"
            value={filter}
            onChange={event => setFilter(event.target.value)}
          />
          <Button className="h-9" onClick={onFilter} disabled={wait}>
            搜索
          </Button>
        </div>
        <div className="flex-auto overflow-auto p-2 pt-0">
          <OptionList
            options={filtered}
            value={values}
            onChange={(value) => {
              console.log(value.map(item => item.value))
              props.onChange?.(value.map(item => item.value))
              setOpen(false)
            }}/>
        </div>
      </PopoverContent>
    </Popover>
  )
}

function OptionList(props: {
  options: ComboboxItem[]
  value: string[]
  onChange: (value: ComboboxItem[]) => void
  path?: ComboboxItem[]
  depth?: number
}) {
  const depth = props.depth || 0
  const parent = props.path || []
  const indent = depth * 16

  return (
    <ul style={{
      marginLeft: `${indent}px`,
    }}>
      {props.options.map((item, i) => {
        const path = [...parent, item]
        const pathValue = path.map(item => item.value)
        const equal = pathValue.join(`.`) === props.value?.join('.')
        return (
          <li key={i}>
            <OptionItem key={`${i}`} item={item} active={equal} onChange={() => props.onChange?.(path)}/>
            {item.children?.length
              && <OptionList depth={depth + 1} options={item.children} value={props.value} path={path} onChange={props.onChange}/>
            }
          </li>
        )
      })}
    </ul>
  )
}

function OptionItem(props: {
  key: string
  item: ComboboxItem
  active: boolean
  onChange?: () => void
}) {
  return (
    <div
      className={merge(
        `transition-colors, duration-100 ease-in-out`,
        `px-4 py-2 text-muted-foreground rounded-md`,
        `flex justify-between items-center`,
        `hover:bg-muted hover:text-foreground`,
      )}
      onClick={props.onChange}>
      {props.item.label}
    </div>
  )
}
