// src/components/ui/DataTable.jsx
import { useState, useRef, useEffect } from 'react'
import { createPortal } from 'react-dom'
import Button from '@/components/ui/Button'

const SKELETON_ROWS = 5

// ─── Sort chevrons ────────────────────────────────────────────────────────────

function SortIcon({ columnKey, sortKey, sortDir }) {
  const active = columnKey === sortKey
  const up     = active && sortDir === 'asc'
  const down   = active && sortDir === 'desc'
  return (
    <span className="dt-sort-icon" aria-hidden="true">
      <svg width="8" height="5" viewBox="0 0 8 5" fill="none">
        <path d="M1 4l3-3 3 3" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" style={{ opacity: up ? 1 : 0.3 }} />
      </svg>
      <svg width="8" height="5" viewBox="0 0 8 5" fill="none">
        <path d="M1 1l3 3 3-3" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" style={{ opacity: down ? 1 : 0.3 }} />
      </svg>
    </span>
  )
}

// ─── Columns icon (grid) ──────────────────────────────────────────────────────

function ColumnsIcon() {
  return (
    <svg width="13" height="13" viewBox="0 0 14 14" fill="none" aria-hidden="true" style={{ display: 'block' }}>
      <rect x="1" y="1" width="4" height="12" rx="1" stroke="currentColor" strokeWidth="1.4" />
      <rect x="7" y="1" width="6" height="5" rx="1" stroke="currentColor" strokeWidth="1.4" />
      <rect x="7" y="8" width="6" height="5" rx="1" stroke="currentColor" strokeWidth="1.4" />
    </svg>
  )
}

// ─── Column picker dropdown ───────────────────────────────────────────────────

function ColumnPicker({ allColumns, visibleKeys, onToggle }) {
  return (
    <div className="dt-col-picker" role="menu" aria-label="Toggle columns">
      {allColumns.map((col) => {
        const label   = col.pickerLabel || col.label || 'Column'
        const checked = visibleKeys.includes(col.key)
        return (
          <label
            key={col.key}
            className={`dt-col-picker-item${col.alwaysOn ? ' dt-col-picker-item--disabled' : ''}`}
          >
            <input
              type="checkbox"
              checked={checked}
              onChange={() => onToggle(col.key)}
              disabled={col.alwaysOn}
              style={{ accentColor: 'var(--color-primary)', flexShrink: 0 }}
            />
            {typeof label === 'string' ? label : (col.pickerLabel || 'Column')}
          </label>
        )
      })}
    </div>
  )
}

// ─── DataTable ────────────────────────────────────────────────────────────────

export default function DataTable({
  // Core
  columns, data, loading = false, keyField = 'id',
  emptyMessage = 'No records found', emptyDescription,
  onRowClick, onRowMiddleClick, selectedIds,
  // Sorting
  sortKey, sortDir = 'asc', onSort,
  // Column visibility + reorder (pass all three to enable)
  allColumns, visibleKeys, onColumnChange,
  // Sub-rows: (row) => ReactNode[] — each element becomes its own <tr> spanning all columns
  renderSubRows,
  // When true, sub-rows get symmetric top+bottom padding to match main row padding
  expandedRowPadding = false,
  // Misc
  footer, skeletonRows = SKELETON_ROWS,
}) {
  // ── Column picker ────────────────────────────────────────────────────────
  const [pickerOpen, setPickerOpen] = useState(false)
  const [pickerPos,  setPickerPos]  = useState({ top: 0, right: 0 })
  const pickerRef = useRef(null)
  const btnRef    = useRef(null)

  useEffect(() => {
    if (!pickerOpen) return
    function onMouseDown(e) {
      if (btnRef.current?.contains(e.target))    return
      if (pickerRef.current?.contains(e.target)) return
      setPickerOpen(false)
    }
    document.addEventListener('mousedown', onMouseDown)
    return () => document.removeEventListener('mousedown', onMouseDown)
  }, [pickerOpen])

  function openPicker() {
    if (btnRef.current) {
      const r = btnRef.current.getBoundingClientRect()
      setPickerPos({ top: r.bottom + 4, right: window.innerWidth - r.right })
    }
    setPickerOpen((v) => !v)
  }

  // ── Drag-to-reorder ──────────────────────────────────────────────────────
  // Strategy: track a *drop insertion index* (a gap between columns) rather
  // than highlighting the hovered column. We determine the gap by comparing
  // the cursor's X position against the midpoint of each draggable <th>.
  //
  // dropInsertIdx = 0  → insert before column 0
  // dropInsertIdx = 1  → insert between column 0 and 1
  // dropInsertIdx = n  → insert after the last column
  //
  // A thin vertical line is rendered on the LEFT border of the column at
  // dropInsertIdx (or past the last column's right edge when idx === length).

  const dragColKey    = useRef(null)   // key of the column being dragged
  const dropInsertIdx = useRef(null)   // resolved insertion index
  const [dragInsertIdx, setDragInsertIdx] = useState(null) // for render
  const [dragActiveKey, setDragActiveKey] = useState(null) // which col is being dragged (for opacity)
  const theadRef = useRef(null)

  // Returns the ordered list of draggable column keys currently rendered
  // (excludes __actions which is never draggable)
  function getDraggableKeys() {
    return columns.filter((c) => c.key !== '__actions').map((c) => c.key)
  }

  function resolveInsertIdx(clientX) {
    if (!theadRef.current) return null
    const ths = Array.from(theadRef.current.querySelectorAll('th[draggable="true"]'))
    if (!ths.length) return null
    // Find which gap the cursor is in by comparing to each th's midpoint
    for (let i = 0; i < ths.length; i++) {
      const rect = ths[i].getBoundingClientRect()
      const mid  = rect.left + rect.width / 2
      if (clientX < mid) return i
    }
    return ths.length // past all columns → insert at end
  }

  function handleDragStart(e, key) {
    dragColKey.current = key
    setDragActiveKey(key)
    e.dataTransfer.effectAllowed = 'move'
    // Needed for Firefox
    e.dataTransfer.setData('text/plain', key)
  }

  function handleDragOverHeader(e) {
    e.preventDefault()
    e.dataTransfer.dropEffect = 'move'
    const idx = resolveInsertIdx(e.clientX)
    if (idx !== dropInsertIdx.current) {
      dropInsertIdx.current = idx
      setDragInsertIdx(idx)
    }
  }

  function handleDragLeaveHeader(e) {
    // Only clear if leaving the entire thead, not just moving between cells
    if (theadRef.current && !theadRef.current.contains(e.relatedTarget)) {
      dropInsertIdx.current = null
      setDragInsertIdx(null)
    }
  }

  function handleDrop(e) {
    e.preventDefault()
    const fromKey = dragColKey.current
    const toIdx   = dropInsertIdx.current

    dragColKey.current    = null
    dropInsertIdx.current = null
    setDragInsertIdx(null)
    setDragActiveKey(null)

    if (fromKey === null || toIdx === null || !onColumnChange || !allColumns) return

    const draggableKeys = getDraggableKeys()
    const fromIdx = draggableKeys.indexOf(fromKey)
    if (fromIdx === -1) return

    // Build new order: remove source, insert at target gap
    const next = [...draggableKeys]
    next.splice(fromIdx, 1)
    // After removal, toIdx may need adjustment
    const adjustedTo = fromIdx < toIdx ? toIdx - 1 : toIdx
    next.splice(adjustedTo, 0, fromKey)

    if (next.join(',') === draggableKeys.join(',')) return // no change

    // Preserve visibility: keep alwaysOn + previously visible keys in new order
    const visibleSet  = new Set(visibleKeys || [])
    const alwaysOnSet = new Set((allColumns || []).filter((c) => c.alwaysOn).map((c) => c.key))
    const nextVisible = next.filter((k) => visibleSet.has(k) || alwaysOnSet.has(k))
    onColumnChange(nextVisible)
  }

  function handleDragEnd() {
    dragColKey.current    = null
    dropInsertIdx.current = null
    setDragInsertIdx(null)
    setDragActiveKey(null)
  }

  // ── Sort ─────────────────────────────────────────────────────────────────
  function handleSort(col) {
    if (!col.sortable || !onSort) return
    const nextDir = col.key === sortKey ? (sortDir === 'asc' ? 'desc' : 'asc') : 'asc'
    onSort(col.key, nextDir)
  }

  function handleRowKeyDown(e, row) {
    if (!onRowClick) return
    if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onRowClick(row) }
  }

  // ── Group hover (expanded rows hover together) ───────────────────────────
  const [hoveredGroup, setHoveredGroup] = useState(null)

  const isSelected = (row) => selectedIds ? selectedIds.has?.(row[keyField]) ?? false : false
  const colAlign   = (col) => col.align === 'right' ? 'right' : col.align === 'center' ? 'center' : 'left'
  const hasColMgmt = !!(allColumns && visibleKeys && onColumnChange)
  const draggable  = hasColMgmt

  const lastColKey = columns.length > 0 ? columns[columns.length - 1].key : null

  // Cell padding for sub-rows — matches the main row td padding so top/bottom
  // space is visually even. The default td padding is --space-3 (12px) top/bottom.
  const subRowCellStyle = expandedRowPadding
    ? { paddingTop: 0, paddingBottom: 'var(--space-1)', paddingLeft: 'var(--space-4)', paddingRight: 'var(--space-4)' }
    : { paddingTop: 0, paddingBottom: 'var(--space-1)', paddingLeft: 'var(--space-4)', paddingRight: 'var(--space-4)' }

  // ── Render ───────────────────────────────────────────────────────────────
  return (
    <div>
      <div className="table-container">
        <div className="overflow-x-auto">
          <table className="table" role="grid">
            <thead
              className="table-thead"
              ref={theadRef}
              onDragOver={draggable ? handleDragOverHeader : undefined}
              onDragLeave={draggable ? handleDragLeaveHeader : undefined}
              onDrop={draggable ? handleDrop : undefined}
            >
              <tr>
                {columns.map((col, colIdx) => {
                  const isLast      = col.key === lastColKey
                  const showEdit    = hasColMgmt && isLast
                  const isDraggable = draggable && col.key !== '__actions'

                  // Insertion line: show on left edge of this th when the drop gap
                  // index matches this column's position in the draggable list,
                  // OR on the right edge of the last draggable column when inserting at end.
                  const draggableKeys  = getDraggableKeys()
                  const draggableIdx   = draggableKeys.indexOf(col.key)
                  const isDropBefore   = dragInsertIdx !== null && draggableIdx === dragInsertIdx
                  const isDropAfterEnd = dragInsertIdx !== null
                    && dragInsertIdx === draggableKeys.length
                    && draggableIdx === draggableKeys.length - 1

                  const headerClickHandler = showEdit
                    ? (e) => e.stopPropagation()
                    : col.onHeaderClick
                      ? () => col.onHeaderClick()
                      : () => handleSort(col)

                  return (
                    <th
                      key={col.key}
                      scope="col"
                      className={[
                        (col.sortable || col.onHeaderClick) ? 'sortable' : '',
                        isDraggable ? 'dt-th--draggable' : '',
                        dragActiveKey === col.key ? 'dt-th--dragging' : '',
                        showEdit ? 'dt-col-mgr-th' : '',
                      ].filter(Boolean).join(' ')}
                      style={{
                        width:    col.width ?? undefined,
                        textAlign: showEdit ? 'right' : colAlign(col),
                        position: 'relative',
                        // Insertion line rendered as box-shadow on left or right border
                        boxShadow: isDropBefore
                          ? 'inset 2px 0 0 var(--color-primary)'
                          : isDropAfterEnd
                            ? 'inset -2px 0 0 var(--color-primary)'
                            : undefined,
                      }}
                      onClick={headerClickHandler}
                      aria-sort={
                        !showEdit && !col.onHeaderClick && col.key === sortKey
                          ? (sortDir === 'asc' ? 'ascending' : 'descending')
                          : (col.sortable && !showEdit ? 'none' : undefined)
                      }
                      draggable={isDraggable}
                      onDragStart={isDraggable ? (e) => handleDragStart(e, col.key) : undefined}
                      onDragEnd={isDraggable ? handleDragEnd : undefined}
                    >
                      {showEdit ? (
                        <div style={{ position: 'relative', display: 'inline-flex', justifyContent: 'flex-end', width: '100%' }}>
                          <Button
                            ref={btnRef}
                            variant="table-actions"
                            size="sm"
                            aria-label="Manage columns"
                            aria-expanded={pickerOpen}
                            aria-haspopup="true"
                            onClick={openPicker}
                            icon={<ColumnsIcon />}
                          >
                            Edit
                          </Button>

                          {pickerOpen && createPortal(
                            <div
                              ref={pickerRef}
                              className="dt-col-picker-wrap"
                              style={{ position: 'fixed', top: pickerPos.top, right: pickerPos.right, zIndex: 9999 }}
                            >
                              <ColumnPicker
                                allColumns={allColumns}
                                visibleKeys={visibleKeys}
                                onToggle={(key) => {
                                  const col = allColumns.find((c) => c.key === key)
                                  if (col?.alwaysOn) return
                                  const next = visibleKeys.includes(key)
                                    ? visibleKeys.filter((k) => k !== key)
                                    : [...visibleKeys, key]
                                  onColumnChange(next)
                                }}
                              />
                            </div>,
                            document.body
                          )}
                        </div>
                      ) : (
                        <span className="inline-flex items-center">
                          {col.label}
                          {col.sortable && <SortIcon columnKey={col.key} sortKey={sortKey} sortDir={sortDir} />}
                        </span>
                      )}
                    </th>
                  )
                })}
              </tr>
            </thead>
            <tbody className="table-tbody">
              {loading && Array.from({ length: skeletonRows }).map((_, rowIdx) => (
                <tr key={`skeleton-${rowIdx}`} aria-hidden="true">
                  {columns.map((col) => (
                    <td key={col.key}><span className="table-skeleton block" style={{ width: rowIdx % 2 === 0 ? '70%' : '55%' }} /></td>
                  ))}
                </tr>
              ))}
              {!loading && data.length === 0 && (
                <tr>
                  <td colSpan={columns.length}>
                    <div className="table-empty">
                      <p className="table-empty-title">{emptyMessage}</p>
                      {emptyDescription && <p style={{ marginTop: 'var(--space-1)' }}>{emptyDescription}</p>}
                    </div>
                  </td>
                </tr>
              )}
              {!loading && data.map((row, mainIdx) => {
                const selected  = isSelected(row)
                const clickable = !!onRowClick
                const subRows   = renderSubRows ? renderSubRows(row) : null
                const tint      = mainIdx % 2 === 1 ? '1' : undefined
                const groupKey  = row[keyField]
                const groupHovered = hoveredGroup === groupKey
                return [
                  <tr
                    key={groupKey ?? JSON.stringify(row)}
                    className={[
                      clickable ? 'clickable' : '',
                      selected  ? 'selected'  : '',
                      subRows && expandedRowPadding ? 'dt-has-subrows' : '',
                      groupHovered ? 'dt-group-hover' : '',
                    ].filter(Boolean).join(' ') || undefined}
                    data-row-tint={tint}
                    data-row-group={subRows ? groupKey : undefined}
                    onClick={clickable ? () => onRowClick(row) : undefined}
                    onMouseDown={onRowMiddleClick ? (e) => { if (e.button === 1) { e.preventDefault(); onRowMiddleClick(row, e) } } : undefined}
                    onKeyDown={clickable ? (e) => handleRowKeyDown(e, row) : undefined}
                    onMouseEnter={() => setHoveredGroup(groupKey)}
                    onMouseLeave={() => setHoveredGroup(null)}
                    tabIndex={clickable ? 0 : undefined}
                    role={clickable ? 'button' : undefined}
                    aria-selected={selected || undefined}
                  >
                    {columns.map((col) => (
                      <td key={col.key} style={{ textAlign: colAlign(col) }}>
                        {col.render ? col.render(row) : row[col.key]}
                      </td>
                    ))}
                  </tr>,
                  ...(subRows ? subRows.map((content, i) => (
                    <tr
                      key={`${groupKey}-sub-${i}`}
                      className={[
                        'dt-sub-row',
                        clickable ? 'clickable' : '',
                        groupHovered ? 'dt-group-hover' : '',
                      ].filter(Boolean).join(' ')}
                      data-row-tint={tint}
                      data-row-group={groupKey}
                      onClick={clickable ? () => onRowClick(row) : undefined}
                      onMouseDown={onRowMiddleClick ? (e) => { if (e.button === 1) { e.preventDefault(); onRowMiddleClick(row, e) } } : undefined}
                      onMouseEnter={() => setHoveredGroup(groupKey)}
                      onMouseLeave={() => setHoveredGroup(null)}
                    >
                      <td colSpan={columns.length} style={
                        i === 0 && subRows.length === 1
                          ? { ...subRowCellStyle, paddingTop: 'var(--space-2)', paddingBottom: 'var(--space-4)' }
                          : i === 0
                            ? { ...subRowCellStyle, paddingTop: 'var(--space-2)' }
                            : i === subRows.length - 1
                              ? { ...subRowCellStyle, paddingBottom: 'var(--space-4)' }
                              : subRowCellStyle
                      }>
                        {content}
                      </td>
                    </tr>
                  )) : []),
                ]
              })}
            </tbody>
          </table>
        </div>
      </div>
      {footer && !loading && <div className="mt-3">{footer}</div>}
      {loading && <span className="sr-only" aria-live="polite">Loading data, please wait.</span>}
    </div>
  )
}
