import { Checkbox } from '@mantine/core';
import { type Cell, flexRender } from '@tanstack/react-table';

import { getGriddyColumn } from '../core/columnMapper';
import { CSS, SELECTION_COLUMN_ID } from '../core/constants';
import { useGriddyStore } from '../core/GriddyStore';
import { TreeExpandButton } from '../features/tree/TreeExpandButton';
import styles from '../styles/griddy.module.css';
import { EditableCell } from './EditableCell';

interface TableCellProps<T> {
  cell: Cell<T, unknown>;
  showGrouping?: boolean;
}

export function TableCell<T>({ cell, showGrouping }: TableCellProps<T>) {
  const isSelectionCol = cell.column.id === SELECTION_COLUMN_ID;
  const isEditing = useGriddyStore((s) => s.isEditing);
  const focusedRowIndex = useGriddyStore((s) => s.focusedRowIndex);
  const focusedColumnId = useGriddyStore((s) => s.focusedColumnId);
  const setEditing = useGriddyStore((s) => s.setEditing);
  const setFocusedColumn = useGriddyStore((s) => s.setFocusedColumn);
  const onEditCommit = useGriddyStore((s) => s.onEditCommit);
  const tree = useGriddyStore((s) => s.tree);
  const treeLoadingNodes = useGriddyStore((s) => s.treeLoadingNodes);
  const selection = useGriddyStore((s) => s.selection);

  if (isSelectionCol) {
    return <RowCheckbox cell={cell} />;
  }

  const griddyColumn = getGriddyColumn(cell.column);
  const rowIndex = cell.row.index;
  const columnId = cell.column.id;
  const isEditable = (griddyColumn as any)?.editable ?? false;
  const isFocusedCell = isEditing && focusedRowIndex === rowIndex && focusedColumnId === columnId;

  const handleCommit = async (value: unknown) => {
    if (onEditCommit) {
      await onEditCommit(cell.row.id, columnId, value);
    }
    setEditing(false);
    setFocusedColumn(null);
  };

  const handleCancel = () => {
    setEditing(false);
    setFocusedColumn(null);
  };

  const handleDoubleClick = () => {
    if (isEditable) {
      setEditing(true);
      setFocusedColumn(columnId);
    }
  };

  const isPinned = cell.column.getIsPinned();
  const leftOffset = isPinned === 'left' ? cell.column.getStart('left') : undefined;
  const rightOffset = isPinned === 'right' ? cell.column.getAfter('right') : undefined;

  const isGrouped = cell.getIsGrouped();
  const isAggregated = cell.getIsAggregated();
  const isPlaceholder = cell.getIsPlaceholder();

  // Tree support
  const depth = cell.row.depth;
  const canExpand = cell.row.getCanExpand();
  const isExpanded = cell.row.getIsExpanded();
  const hasSelection = selection?.mode !== 'none';
  const columnIndex = cell.column.getIndex();
  // First content column is index 0 if no selection, or index 1 if selection enabled
  const isFirstColumn = hasSelection ? columnIndex === 1 : columnIndex === 0;
  const indentSize = tree?.indentSize ?? 20;
  const showTreeButton = tree?.enabled && isFirstColumn && tree?.showExpandIcon !== false;

  return (
    <div
      className={[
        styles[CSS.cell],
        isPinned === 'left' ? styles['griddy-cell--pinned-left'] : '',
        isPinned === 'right' ? styles['griddy-cell--pinned-right'] : '',
      ]
        .filter(Boolean)
        .join(' ')}
      onDoubleClick={handleDoubleClick}
      role="gridcell"
      style={{
        left: leftOffset !== undefined ? `${leftOffset}px` : undefined,
        paddingLeft: isFirstColumn && tree?.enabled ? `${depth * indentSize + 8}px` : undefined,
        position: isPinned ? 'sticky' : 'relative',
        right: rightOffset !== undefined ? `${rightOffset}px` : undefined,
        width: cell.column.getSize(),
        zIndex: isPinned ? 1 : 0,
      }}
    >
      {showTreeButton && (
        <TreeExpandButton
          canExpand={canExpand}
          icons={tree?.icons}
          isExpanded={isExpanded}
          isLoading={treeLoadingNodes.has(cell.row.id)}
          onToggle={() => cell.row.toggleExpanded()}
        />
      )}
      {showGrouping && isGrouped && (
        <button
          onClick={() => cell.row.toggleExpanded()}
          style={{
            background: 'none',
            border: 'none',
            cursor: 'pointer',
            marginRight: 4,
            padding: 0,
          }}
        >
          {cell.row.getIsExpanded() ? '\u25BC' : '\u25B6'}
        </button>
      )}
      {isFocusedCell && isEditable ? (
        <EditableCell
          cell={cell}
          isEditing={isFocusedCell}
          onCancelEdit={handleCancel}
          onCommitEdit={handleCommit}
        />
      ) : isGrouped ? (
        <>
          {flexRender(cell.column.columnDef.cell, cell.getContext())} ({cell.row.subRows.length})
        </>
      ) : isAggregated ? (
        flexRender(
          cell.column.columnDef.aggregatedCell ?? cell.column.columnDef.cell,
          cell.getContext()
        )
      ) : isPlaceholder ? null : (
        flexRender(cell.column.columnDef.cell, cell.getContext())
      )}
    </div>
  );
}

function RowCheckbox<T>({ cell }: TableCellProps<T>) {
  const row = cell.row;
  const isPinned = cell.column.getIsPinned();
  const leftOffset = isPinned === 'left' ? cell.column.getStart('left') : undefined;
  const rightOffset = isPinned === 'right' ? cell.column.getAfter('right') : undefined;

  return (
    <div
      className={[
        styles[CSS.cell],
        isPinned === 'left' ? styles['griddy-cell--pinned-left'] : '',
        isPinned === 'right' ? styles['griddy-cell--pinned-right'] : '',
      ]
        .filter(Boolean)
        .join(' ')}
      role="gridcell"
      style={{
        left: leftOffset !== undefined ? `${leftOffset}px` : undefined,
        position: isPinned ? 'sticky' : 'relative',
        right: rightOffset !== undefined ? `${rightOffset}px` : undefined,
        width: cell.column.getSize(),
        zIndex: isPinned ? 1 : 0,
      }}
    >
      <Checkbox
        aria-label={`Select row ${row.index + 1}`}
        checked={row.getIsSelected()}
        disabled={!row.getCanSelect()}
        onChange={row.getToggleSelectedHandler()}
        onClick={(e) => e.stopPropagation()}
        size="xs"
      />
    </div>
  );
}
