"use client"

import * as React from "react"
import { Select as SelectPrimitive } from "@base-ui/react/select"

import type { ValueOf } from "@/types/utils"

import { cn } from "@/lib/utils"
import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react"

const SELECT_TRIGGER_SIZE = {
  SMALL: "sm",
  DEFAULT: "default",
} as const

type SelectTriggerSize =
  ValueOf<typeof SELECT_TRIGGER_SIZE>

type SelectItemEntry = {
  value: unknown
  label: React.ReactNode
}

type SelectItemsProp = React.ComponentProps<typeof SelectPrimitive.Root>["items"]

const SelectValueLabelContext = React.createContext<Map<unknown, React.ReactNode> | null>(
  null
)

function collectSelectItems(children: React.ReactNode, entries: SelectItemEntry[] = []) {
  React.Children.forEach(children, (child) => {
    if (!React.isValidElement(child)) {
      return
    }

    if (child.type === SelectItem) {
      const itemProps = child.props as { value?: unknown; children?: React.ReactNode }
      if (itemProps.value !== undefined) {
        entries.push({
          value: itemProps.value,
          label: itemProps.children ?? null,
        })
      }
    }

    const nestedChildren = (child.props as { children?: React.ReactNode }).children
    if (nestedChildren) {
      collectSelectItems(nestedChildren, entries)
    }
  })

  return entries
}

function normalizeSelectItems(items: SelectItemsProp): SelectItemEntry[] {
  if (!items) {
    return []
  }

  if (Array.isArray(items)) {
    return items.map((item) => ({
      value: item.value,
      label: item.label,
    }))
  }

  return Object.entries(items).map(([value, label]) => ({
    value,
    label,
  }))
}

function Select({
  children,
  items,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
  const inferredItems = items ? [] : collectSelectItems(children)
  const resolvedItems = items ?? (inferredItems.length > 0 ? inferredItems : undefined)
  const labelEntries = items ? normalizeSelectItems(items) : inferredItems
  const labelMap = new Map<unknown, React.ReactNode>(
    labelEntries.map((entry) => [entry.value, entry.label])
  )

  return (
    <SelectValueLabelContext.Provider value={labelMap}>
      <SelectPrimitive.Root data-slot="select" items={resolvedItems} {...props}>
        {children}
      </SelectPrimitive.Root>
    </SelectValueLabelContext.Provider>
  )
}

function SelectGroup({
  className,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
  return (
    <SelectPrimitive.Group
      data-slot="select-group"
      className={cn("scroll-my-1 p-1", className)}
      {...props}
    />
  )
}

function SelectValue({
  placeholder,
  children,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Value> & {
  placeholder?: React.ReactNode
}) {
  const labelMap = React.useContext(SelectValueLabelContext)

  if (children !== undefined) {
    return (
      <SelectPrimitive.Value data-slot="select-value" {...props}>
        {children}
      </SelectPrimitive.Value>
    )
  }

  return (
    <SelectPrimitive.Value data-slot="select-value" {...props}>
      {(value: unknown) => {
        if (
          value === null ||
          value === undefined ||
          value === "" ||
          (Array.isArray(value) && value.length === 0)
        ) {
          return placeholder ?? null
        }

        if (Array.isArray(value)) {
          return value.map((item, index) => (
            <React.Fragment key={index}>
              {index > 0 ? ", " : null}
              {labelMap?.get(item) ?? (item as React.ReactNode)}
            </React.Fragment>
          ))
        }

        return labelMap?.get(value) ?? (value as React.ReactNode)
      }}
    </SelectPrimitive.Value>
  )
}

function SelectTrigger({
  className,
  size = SELECT_TRIGGER_SIZE.DEFAULT,
  children,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
  size?: SelectTriggerSize
}) {
  return (
    <SelectPrimitive.Trigger
      data-slot="select-trigger"
      data-size={size}
      className={cn(
        "border-input data-[placeholder]:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 gap-1.5 rounded-md border bg-transparent py-2 pr-2 pl-2.5 text-sm shadow-xs transition-[color,box-shadow] focus-visible:ring-[3px] aria-invalid:ring-[3px] data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:flex *:data-[slot=select-value]:gap-1.5 [&_svg:not([class*='size-'])]:size-4 flex w-fit items-center justify-between whitespace-nowrap outline-none disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
        className
      )}
      {...props}
    >
      {children}
      <SelectPrimitive.Icon
        render={
          <ChevronDownIcon className="text-muted-foreground size-4 pointer-events-none" />
        }
      />
    </SelectPrimitive.Trigger>
  )
}

type SelectContentPosition = "item-aligned" | "popper"

function SelectContent({
  className,
  children,
  position = "item-aligned",
  align = "center",
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Popup> & {
  position?: SelectContentPosition
  align?: React.ComponentProps<typeof SelectPrimitive.Positioner>["align"]
}) {
  const alignItemWithTrigger = position === "item-aligned"

  return (
    <SelectPrimitive.Portal>
      <SelectPrimitive.Positioner
        side="bottom"
        sideOffset={4}
        align={align}
        alignOffset={0}
        alignItemWithTrigger={alignItemWithTrigger}
        className="isolate z-50"
      >
        <SelectPrimitive.Popup
          data-slot="select-content"
          className={cn("bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 min-w-36 rounded-md shadow-md ring-1 duration-100 relative z-50 max-h-(--available-height) origin-(--transform-origin) overflow-x-hidden overflow-y-auto", position === "popper" && "w-(--anchor-width) data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className)}
          {...props}
        >
          <SelectScrollUpButton />
          <SelectPrimitive.List
            data-position={position}
            className={cn(
              "data-[position=popper]:min-w-(--anchor-width)"
            )}
          >
            {children}
          </SelectPrimitive.List>
          <SelectScrollDownButton />
        </SelectPrimitive.Popup>
      </SelectPrimitive.Positioner>
    </SelectPrimitive.Portal>
  )
}

function SelectLabel({
  className,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.GroupLabel>) {
  return (
    <SelectPrimitive.GroupLabel
      data-slot="select-label"
      className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
      {...props}
    />
  )
}

function SelectItem({
  className,
  children,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Item>) {
  return (
    <SelectPrimitive.Item
      data-slot="select-item"
      className={cn(
        "focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 relative flex w-full cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
        className
      )}
      {...props}
    >
      <SelectPrimitive.ItemIndicator
        render={
          <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
        }
      >
        <CheckIcon className="pointer-events-none" />
      </SelectPrimitive.ItemIndicator>
      <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
    </SelectPrimitive.Item>
  )
}

function SelectSeparator({
  className,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
  return (
    <SelectPrimitive.Separator
      data-slot="select-separator"
      className={cn("bg-border -mx-1 my-1 h-px pointer-events-none", className)}
      {...props}
    />
  )
}

function SelectScrollUpButton({
  className,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpArrow>) {
  return (
    <SelectPrimitive.ScrollUpArrow
      data-slot="select-scroll-up-button"
      className={cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4", className)}
      {...props}
    >
      <ChevronUpIcon />
    </SelectPrimitive.ScrollUpArrow>
  )
}

function SelectScrollDownButton({
  className,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownArrow>) {
  return (
    <SelectPrimitive.ScrollDownArrow
      data-slot="select-scroll-down-button"
      className={cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4", className)}
      {...props}
    >
      <ChevronDownIcon />
    </SelectPrimitive.ScrollDownArrow>
  )
}

export {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectScrollDownButton,
  SelectScrollUpButton,
  SelectSeparator,
  SelectTrigger,
  SelectValue,
}
