import type { ColumnSort, Header, SortDirection, SortingState, Table } from '@tanstack/react-table'

import {
  BaselineIcon,
  CalendarIcon,
  CheckSquareIcon,
  ChevronDownIcon,
  ChevronUpIcon,
  EyeOffIcon,
  HashIcon,
  ListChecksIcon,
  ListIcon,
  PinIcon,
  PinOffIcon,
  TextInitialIcon,
  XIcon,
} from 'lucide-react'
import * as React from 'react'

import type { Cell } from '@components/ui/data-grid/data-grid-types'
import {
  DropdownMenu,
  DropdownMenuCheckboxItem,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from '@components/ui/dropdown-menu/dropdown-menu'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@components/ui/tooltip/tooltip'
import { cn } from '@lib/utils'
import { GanttTimeline } from './cell-variants/utils/gantt-timeline'

function getColumnVariant(variant?: Cell['variant']): {
  icon: React.ComponentType<React.SVGProps<SVGSVGElement>>
  label: string
} | null {
  switch (variant) {
    case 'short-text':
      return { icon: BaselineIcon, label: 'Short text' }
    case 'long-text':
      return { icon: TextInitialIcon, label: 'Long text' }
    case 'number':
      return { icon: HashIcon, label: 'Number' }
    case 'select':
      return { icon: ListIcon, label: 'Select' }
    case 'multi-select':
      return { icon: ListChecksIcon, label: 'Multi-select' }
    case 'checkbox':
      return { icon: CheckSquareIcon, label: 'Checkbox' }
    case 'date':
      return { icon: CalendarIcon, label: 'Date' }
    default:
      return null
  }
}

type DataGridColumnHeaderProps<TData, TValue> = {
  header: Header<TData, TValue>
  table: Table<TData>
} & React.ComponentProps<typeof DropdownMenuTrigger>

export function DataGridColumnHeader<TData, TValue>({
  header,
  table,
  className,
  onPointerDown,
  ...props
}: DataGridColumnHeaderProps<TData, TValue>) {
  const column = header.column
  const label = column.columnDef.meta?.label
    ? column.columnDef.meta.label
    : typeof column.columnDef.header === 'string'
      ? column.columnDef.header
      : column.id

  const isAnyColumnResizing = table.getState().columnSizingInfo.isResizingColumn

  const cellVariant = column.columnDef.meta?.cell
  const columnVariant = getColumnVariant(cellVariant?.variant)

  const pinnedPosition = column.getIsPinned()
  const isPinnedLeft = pinnedPosition === 'left'
  const isPinnedRight = pinnedPosition === 'right'

  const onSortingChange = React.useCallback(
    (direction: SortDirection) => {
      table.setSorting((prev: SortingState) => {
        const existingSortIndex = prev.findIndex((sort) => sort.id === column.id)
        const newSort: ColumnSort = {
          id: column.id,
          desc: direction === 'desc',
        }

        if (existingSortIndex >= 0) {
          const updated = [...prev]
          updated[existingSortIndex] = newSort
          return updated
        } else {
          return [...prev, newSort]
        }
      })
    },
    [column.id, table],
  )

  const onSortRemove = React.useCallback(() => {
    table.setSorting((prev: SortingState) => prev.filter((sort) => sort.id !== column.id))
  }, [column.id, table])

  const onLeftPin = React.useCallback(() => {
    column.pin('left')
  }, [column])

  const onRightPin = React.useCallback(() => {
    column.pin('right')
  }, [column])

  const onUnpin = React.useCallback(() => {
    column.pin(false)
  }, [column])

  const onTriggerPointerDown = React.useCallback(
    (event: React.PointerEvent<HTMLButtonElement>) => {
      onPointerDown?.(event)
      if (event.defaultPrevented) {
        return
      }

      if (event.button !== 0) {
        return
      }
      table.options.meta?.onColumnClick?.(column.id)
    },
    [table.options.meta, column.id, onPointerDown],
  )

  return (
    <>
      <DropdownMenu>
        <DropdownMenuTrigger
          className={cn(
            'flex size-full items-center justify-between gap-2 p-2 text-sm bg-sidebar-accent/80 font-bold hover:bg-neutral-100/40 data-[state=open]:bg-neutral-100/40 [&_svg]:size-4 dark:hover:bg-neutral-800/40 dark:data-[state=open]:bg-neutral-800/40',
            isAnyColumnResizing && 'pointer-events-none',
            className,
          )}
          onPointerDown={onTriggerPointerDown}
          {...props}
        >
          <div className="flex min-w-0 flex-1 items-center gap-1.5">
            {columnVariant && (
              <TooltipProvider>
                <Tooltip delayDuration={100}>
                  <TooltipTrigger asChild>
                    <columnVariant.icon className="size-3.5 shrink-0 text-neutral-500 dark:text-neutral-400" />
                  </TooltipTrigger>
                  <TooltipContent side="top">
                    <p>{columnVariant.label}</p>
                  </TooltipContent>
                </Tooltip>
              </TooltipProvider>
            )}
            <span className="truncate">{label}</span>
          </div>
          <ChevronDownIcon className="shrink-0 text-neutral-500 dark:text-neutral-400" />
        </DropdownMenuTrigger>
        <DropdownMenuContent align="start" sideOffset={0} className="w-60">
          {column.getCanSort() && (
            <>
              <DropdownMenuCheckboxItem
                className="relative pr-8 pl-2 [&>span:first-child]:right-2 [&>span:first-child]:left-auto [&_svg]:text-neutral-500 dark:[&_svg]:text-neutral-400"
                checked={column.getIsSorted() === 'asc'}
                onClick={() => onSortingChange('asc')}
              >
                <ChevronUpIcon />
                Sort asc
              </DropdownMenuCheckboxItem>
              <DropdownMenuCheckboxItem
                className="relative pr-8 pl-2 [&>span:first-child]:right-2 [&>span:first-child]:left-auto [&_svg]:text-neutral-500 dark:[&_svg]:text-neutral-400"
                checked={column.getIsSorted() === 'desc'}
                onClick={() => onSortingChange('desc')}
              >
                <ChevronDownIcon />
                Sort desc
              </DropdownMenuCheckboxItem>
              {column.getIsSorted() && (
                <DropdownMenuItem onClick={onSortRemove}>
                  <XIcon />
                  Remove sort
                </DropdownMenuItem>
              )}
            </>
          )}
          {column.getCanPin() && (
            <>
              {column.getCanSort() && <DropdownMenuSeparator />}

              {isPinnedLeft ? (
                <DropdownMenuItem className="[&_svg]:text-neutral-500 dark:[&_svg]:text-neutral-400" onClick={onUnpin}>
                  <PinOffIcon />
                  Unpin from left
                </DropdownMenuItem>
              ) : (
                <DropdownMenuItem
                  className="[&_svg]:text-neutral-500 dark:[&_svg]:text-neutral-400"
                  onClick={onLeftPin}
                >
                  <PinIcon />
                  Pin to left
                </DropdownMenuItem>
              )}
              {isPinnedRight ? (
                <DropdownMenuItem className="[&_svg]:text-neutral-500 dark:[&_svg]:text-neutral-400" onClick={onUnpin}>
                  <PinOffIcon />
                  Unpin from right
                </DropdownMenuItem>
              ) : (
                <DropdownMenuItem
                  className="[&_svg]:text-neutral-500 dark:[&_svg]:text-neutral-400"
                  onClick={onRightPin}
                >
                  <PinIcon />
                  Pin to right
                </DropdownMenuItem>
              )}
            </>
          )}
          {column.getCanHide() && (
            <>
              <DropdownMenuSeparator />
              <DropdownMenuCheckboxItem
                className="relative pr-8 pl-2 [&>span:first-child]:right-2 [&>span:first-child]:left-auto [&_svg]:text-neutral-500 dark:[&_svg]:text-neutral-400"
                checked={!column.getIsVisible()}
                onClick={() => column.toggleVisibility(false)}
              >
                <EyeOffIcon />
                Hide column
              </DropdownMenuCheckboxItem>
            </>
          )}
        </DropdownMenuContent>
      </DropdownMenu>
      {/* Render any variant-provided header component from HeaderComponents. */}
      {(() => {
        const variant = column.columnDef.meta?.cell?.variant
        const variantHeader = variant ? HeaderComponents[variant] : undefined
        if (variantHeader) {
          return variantHeader({ header, table })
        }

        return null
      })()}
      {header.column.getCanResize() && <DataGridColumnResizer header={header} table={table} label={label} />}
    </>
  )
}

const DataGridColumnResizer = React.memo(DataGridColumnResizerImpl, (prev, next) => {
  const prevColumn = prev.header.column
  const nextColumn = next.header.column

  if (prevColumn.getIsResizing() !== nextColumn.getIsResizing() || prevColumn.getSize() !== nextColumn.getSize()) {
    return false
  }

  if (prev.label !== next.label) {
    return false
  }

  return true
}) as typeof DataGridColumnResizerImpl

type DataGridColumnResizerProps<TData, TValue> = {
  label: string
} & DataGridColumnHeaderProps<TData, TValue>

function DataGridColumnResizerImpl<TData, TValue>({ header, table, label }: DataGridColumnResizerProps<TData, TValue>) {
  const defaultColumnDef = table._getDefaultColumnDef()

  const onDoubleClick = React.useCallback(() => {
    header.column.resetSize()
  }, [header.column])

  return (
    <div
      role="separator"
      aria-orientation="vertical"
      aria-label={`Resize ${label} column`}
      aria-valuenow={header.column.getSize()}
      aria-valuemin={defaultColumnDef.minSize}
      aria-valuemax={defaultColumnDef.maxSize}
      tabIndex={0}
      className={cn(
        'right-0 absolute top-0 z-50 h-full w-0 touch-none select-none cursor-ew-resize focus:outline-none',
        // visible thin line (use right positioning and a stable 1px width)
        "before:content-[''] before:absolute before:inset-y-0 before:right-0 before:translate-x-1/2 before:w-px before:h-full before:bg-neutral-700 dark:before:bg-neutral-400 before:opacity-0 before:transition-opacity before:duration-150",
        // large invisible hit area
        "after:content-[''] after:absolute after:inset-y-0 after:right-0 after:translate-x-1/2 after:w-[18px] after:h-full after:bg-transparent",
        header.column.getIsResizing()
          ? 'before:opacity-100 before:bg-neutral-900 dark:before:bg-neutral-50'
          : 'hover:before:opacity-100',
      )}
      style={{ willChange: 'transform, opacity', transform: 'translateZ(0)' }}
      onDoubleClick={onDoubleClick}
      onMouseDown={header.getResizeHandler()}
      onTouchStart={header.getResizeHandler()}
    />
  )
}

/**
 * Optional header components keyed by cell variant. Components receive { header, table } and
 * should return a React node (or null). This allows the column header to render variant-specific
 * header visuals without coupling the header implementation to variants.
 */
export const HeaderComponents: Record<string, (props: any) => React.ReactNode | null> = {
  gantt: (props) => <GanttTimeline {...props} />,
}
