import {
    attachClosestEdge,
    type Edge,
    extractClosestEdge,
} from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
import {
    draggable,
    dropTargetForElements,
} from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import { disableNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/disable-native-drag-preview';
import clsx from 'clsx';
import React, { CSSProperties, ReactElement, ReactNode, useEffect, useRef, useState } from 'react';
import { useParams } from 'react-router';
import { CellComponentProps } from 'react-window-v2';

import styles from './item-table-list-column.module.css';

import i18n from '/@/i18n/i18n';
import { getDraggedItems } from '/@/renderer/components/item-list/helpers/get-dragged-items';
import {
    useItemDraggingState,
    useItemSelectionState,
} from '/@/renderer/components/item-list/helpers/item-list-state';
import { ActionsColumn } from '/@/renderer/components/item-list/item-table-list/columns/actions-column';
import { AlbumArtistsColumn } from '/@/renderer/components/item-list/item-table-list/columns/album-artists-column';
import { AlbumColumn } from '/@/renderer/components/item-list/item-table-list/columns/album-column';
import { ArtistsColumn } from '/@/renderer/components/item-list/item-table-list/columns/artists-column';
import { CountColumn } from '/@/renderer/components/item-list/item-table-list/columns/count-column';
import {
    AbsoluteDateColumn,
    DateColumn,
    RelativeDateColumn,
} from '/@/renderer/components/item-list/item-table-list/columns/date-column';
import { DefaultColumn } from '/@/renderer/components/item-list/item-table-list/columns/default-column';
import { DurationColumn } from '/@/renderer/components/item-list/item-table-list/columns/duration-column';
import { FavoriteColumn } from '/@/renderer/components/item-list/item-table-list/columns/favorite-column';
import { GenreBadgeColumn } from '/@/renderer/components/item-list/item-table-list/columns/genre-badge-column';
import { GenreColumn } from '/@/renderer/components/item-list/item-table-list/columns/genre-column';
import { ImageColumn } from '/@/renderer/components/item-list/item-table-list/columns/image-column';
import { NumericColumn } from '/@/renderer/components/item-list/item-table-list/columns/numeric-column';
import { PathColumn } from '/@/renderer/components/item-list/item-table-list/columns/path-column';
import { PlaylistReorderColumn } from '/@/renderer/components/item-list/item-table-list/columns/playlist-reorder-column';
import { RatingColumn } from '/@/renderer/components/item-list/item-table-list/columns/rating-column';
import { RowIndexColumn } from '/@/renderer/components/item-list/item-table-list/columns/row-index-column';
import { SizeColumn } from '/@/renderer/components/item-list/item-table-list/columns/size-column';
import { TextColumn } from '/@/renderer/components/item-list/item-table-list/columns/text-column';
import { TitleColumn } from '/@/renderer/components/item-list/item-table-list/columns/title-column';
import { TitleCombinedColumn } from '/@/renderer/components/item-list/item-table-list/columns/title-combined-column';
import { YearColumn } from '/@/renderer/components/item-list/item-table-list/columns/year-column';
import { TableItemProps } from '/@/renderer/components/item-list/item-table-list/item-table-list';
import { ItemControls, ItemListItem } from '/@/renderer/components/item-list/types';
import { eventEmitter } from '/@/renderer/events/event-emitter';
import { useDragDrop } from '/@/renderer/hooks/use-drag-drop';
import { Flex } from '/@/shared/components/flex/flex';
import { Icon } from '/@/shared/components/icon/icon';
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
import { Text } from '/@/shared/components/text/text';
import { useDoubleClick } from '/@/shared/hooks/use-double-click';
import { useMergedRef } from '/@/shared/hooks/use-merged-ref';
import { Folder, LibraryItem, QueueSong, Song } from '/@/shared/types/domain-types';
import {
    dndUtils,
    DragData,
    DragOperation,
    DragTarget,
    DragTargetMap,
} from '/@/shared/types/drag-and-drop';
import { TableColumn } from '/@/shared/types/types';

export interface ItemTableListColumn extends CellComponentProps<TableItemProps> {}

export interface ItemTableListInnerColumn extends ItemTableListColumn {
    controls: ItemControls;
    dragRef?: null | React.Ref<HTMLDivElement>;
    isDraggedOver?: 'bottom' | 'top' | null;
    isDragging?: boolean;
    type: TableColumn;
}

export const ItemTableListColumn = (props: ItemTableListColumn) => {
    const { playlistId } = useParams() as { playlistId?: string };
    const type = props.columns[props.columnIndex].id as TableColumn;

    const isHeaderEnabled = !!props.enableHeader;
    const isDataRow = isHeaderEnabled ? props.rowIndex > 0 : true;
    const item = isDataRow ? props.data[props.rowIndex] : null;
    const shouldEnableDrag = !!props.enableDrag && isDataRow && !!item;
    const itemType = (item as unknown as { _itemType?: LibraryItem })?._itemType || props.itemType;

    // Check if this row should render a group header (must be before conditional returns)
    // Group headers need to be rendered consistently across all grids (pinned left, main, pinned right)
    // to maintain proper styling and row heights
    let groupHeader: 'GROUP_HEADER' | null | ReactElement = null;
    if (props.groups && isDataRow && props.groups.length > 0) {
        const groupInfo = props.groupHeaderInfoByRowIndex?.get(props.rowIndex);
        const group = groupInfo ? props.groups[groupInfo.groupIndex] : undefined;

        if (groupInfo && group) {
            // Determine where to render the group header content:
            // - If pinned left columns exist, render in the first pinned left column
            // - Otherwise, render in the first column of the main grid
            const hasPinnedLeftColumns = (props.pinnedLeftColumnCount || 0) > 0;
            const isFirstPinnedLeftColumn = props.columnIndex === 0 && hasPinnedLeftColumns;
            const isMainGridFirstColumn =
                !hasPinnedLeftColumns &&
                (props.columnIndex === (props.pinnedLeftColumnCount || 0) ||
                    (props.columnIndex === 0 && (props.pinnedLeftColumnCount || 0) === 0));

            // Render group header content in the first pinned left column (if exists) or first main grid column
            if (isFirstPinnedLeftColumn || isMainGridFirstColumn) {
                groupHeader = group.render({
                    data: props.getGroupRenderData?.() ?? [],
                    groupIndex: groupInfo.groupIndex,
                    index: props.rowIndex,
                    internalState: props.internalState,
                    startDataIndex: groupInfo.startDataIndex,
                });
            } else {
                // For other columns, mark as group header row for styled rendering
                groupHeader = 'GROUP_HEADER';
            }
        }
    }

    const {
        isDraggedOver,
        isDragging: isDraggingLocal,
        ref: dragRef,
    } = useDragDrop<HTMLDivElement>({
        drag: {
            getId: () => {
                if (!item || !isDataRow) {
                    return [];
                }

                const draggedItems = getDraggedItems(item as any, props.internalState);

                return draggedItems.map((draggedItem) => draggedItem.id);
            },
            getItem: () => {
                if (!item || !isDataRow) {
                    return [];
                }

                const draggedItems = getDraggedItems(item as any, props.internalState);

                return draggedItems;
            },
            itemType: props.itemType,
            onDragStart: () => {
                if (!item || !isDataRow) {
                    return;
                }

                const draggedItems = getDraggedItems(item as any, props.internalState);
                if (props.internalState) {
                    props.internalState.setDragging(draggedItems);
                }
            },
            onDrop: () => {
                if (props.internalState) {
                    props.internalState.setDragging([]);
                }
            },
            operation:
                props.itemType === LibraryItem.QUEUE_SONG
                    ? [DragOperation.REORDER, DragOperation.ADD]
                    : props.itemType === LibraryItem.PLAYLIST_SONG
                      ? [DragOperation.REORDER, DragOperation.ADD]
                      : [DragOperation.ADD],
            target: DragTargetMap[props.itemType] || DragTarget.GENERIC,
        },
        drop: {
            canDrop: (args) => {
                if (args.source.type === DragTarget.TABLE_COLUMN) {
                    return false;
                }

                // Allow drops for QUEUE_SONG (queue reordering)
                if (props.itemType === LibraryItem.QUEUE_SONG) {
                    return true;
                }

                // Allow drops for PLAYLIST_SONG (playlist reordering)
                // Only allow drops when drag is started from the reorder handle
                if (
                    props.itemType === LibraryItem.PLAYLIST_SONG &&
                    args.source.itemType === LibraryItem.PLAYLIST_SONG &&
                    args.source.metadata?.fromReorderHandle === true
                ) {
                    return true;
                }

                return false;
            },
            getData: () => {
                return {
                    id: [(item as unknown as { id: string }).id],
                    item: [item as unknown as unknown[]],
                    itemType: props.itemType,
                    type: DragTargetMap[props.itemType] || DragTarget.GENERIC,
                };
            },
            onDrag: () => {
                return;
            },
            onDragLeave: () => {
                return;
            },
            onDrop: (args) => {
                if (args.self.type === DragTarget.QUEUE_SONG) {
                    const sourceServerId = (
                        args.source.item?.[0] as unknown as { _serverId: string }
                    )._serverId;

                    const sourceItemType = args.source.itemType as LibraryItem;

                    const droppedOnUniqueId = (
                        args.self.item?.[0] as unknown as { _uniqueId: string }
                    )._uniqueId;

                    switch (args.source.type) {
                        case DragTarget.ALBUM: {
                            props.playerContext.addToQueueByFetch(
                                sourceServerId,
                                args.source.id,
                                sourceItemType,
                                { edge: args.edge, uniqueId: droppedOnUniqueId },
                            );
                            break;
                        }
                        case DragTarget.ALBUM_ARTIST: {
                            props.playerContext.addToQueueByFetch(
                                sourceServerId,
                                args.source.id,
                                sourceItemType,
                                { edge: args.edge, uniqueId: droppedOnUniqueId },
                            );
                            break;
                        }
                        case DragTarget.ARTIST: {
                            props.playerContext.addToQueueByFetch(
                                sourceServerId,
                                args.source.id,
                                sourceItemType,
                                { edge: args.edge, uniqueId: droppedOnUniqueId },
                            );
                            break;
                        }
                        case DragTarget.FOLDER: {
                            const items = args.source.item;

                            const { folders, songs } = (items || []).reduce<{
                                folders: Folder[];
                                songs: Song[];
                            }>(
                                (acc, item) => {
                                    if ((item as unknown as Song)._itemType === LibraryItem.SONG) {
                                        acc.songs.push(item as unknown as Song);
                                    } else if (
                                        (item as unknown as Folder)._itemType === LibraryItem.FOLDER
                                    ) {
                                        acc.folders.push(item as unknown as Folder);
                                    }
                                    return acc;
                                },
                                { folders: [], songs: [] },
                            );

                            const folderIds = folders.map((folder) => folder.id);

                            // Handle folders: fetch and add to queue
                            if (folderIds.length > 0) {
                                props.playerContext.addToQueueByFetch(
                                    sourceServerId,
                                    folderIds,
                                    LibraryItem.FOLDER,
                                    { edge: args.edge, uniqueId: droppedOnUniqueId },
                                );
                            }

                            // Handle songs: add directly to queue
                            if (songs.length > 0) {
                                props.playerContext.addToQueueByData(songs, {
                                    edge: args.edge,
                                    uniqueId: droppedOnUniqueId,
                                });
                            }

                            break;
                        }
                        case DragTarget.GENRE: {
                            props.playerContext.addToQueueByFetch(
                                sourceServerId,
                                args.source.id,
                                sourceItemType,
                                { edge: args.edge, uniqueId: droppedOnUniqueId },
                            );
                            break;
                        }
                        case DragTarget.PLAYLIST: {
                            props.playerContext.addToQueueByFetch(
                                sourceServerId,
                                args.source.id,
                                sourceItemType,
                                { edge: args.edge, uniqueId: droppedOnUniqueId },
                            );
                            break;
                        }
                        case DragTarget.QUEUE_SONG: {
                            const sourceItems = (args.source.item || []) as QueueSong[];
                            if (
                                sourceItems.length > 0 &&
                                args.edge &&
                                (args.edge === 'top' || args.edge === 'bottom')
                            ) {
                                props.playerContext.moveSelectedTo(
                                    sourceItems,
                                    args.edge,
                                    droppedOnUniqueId,
                                );
                            }
                            break;
                        }
                        case DragTarget.SONG: {
                            const sourceItems = (args.source.item || []) as Song[];
                            if (sourceItems.length > 0) {
                                props.playerContext.addToQueueByData(sourceItems, {
                                    edge: args.edge,
                                    uniqueId: droppedOnUniqueId,
                                });
                            }
                            break;
                        }
                        default: {
                            break;
                        }
                    }
                }

                // Handle PLAYLIST_SONG reordering
                // Only allow drops when drag is started from the reorder handle
                if (
                    args.self.itemType === LibraryItem.PLAYLIST_SONG &&
                    args.source.itemType === LibraryItem.PLAYLIST_SONG &&
                    args.source.metadata?.fromReorderHandle === true &&
                    playlistId
                ) {
                    const sourceItems = (args.source.item || []) as any[];
                    const targetItem = item as any;

                    if (
                        sourceItems.length > 0 &&
                        args.edge &&
                        (args.edge === 'top' || args.edge === 'bottom') &&
                        targetItem
                    ) {
                        // Emit event to reorder playlist songs
                        eventEmitter.emit('PLAYLIST_REORDER', {
                            edge: args.edge,
                            playlistId,
                            sourceIds: args.source.id,
                            targetId: targetItem.id,
                        });
                    }
                }

                if (props.internalState) {
                    props.internalState.setDragging([]);
                }

                return;
            },
        },
        isEnabled: shouldEnableDrag,
    });

    const itemRowId =
        item && typeof item === 'object' && 'id' in item && props.internalState
            ? props.internalState.extractRowId(item)
            : undefined;
    const isDraggingState = useItemDraggingState(
        props.internalState,
        itemRowId ||
            (item && typeof item === 'object' && 'id' in item ? (item as any).id : undefined),
    );
    const isDragging = props.internalState ? isDraggingState : isDraggingLocal;

    const controls = props.controls;

    const dragProps = {
        dragRef: shouldEnableDrag ? dragRef : null,
        isDraggedOver: isDraggedOver === 'top' || isDraggedOver === 'bottom' ? isDraggedOver : null,
        isDragging,
    };

    if (isHeaderEnabled && props.rowIndex === 0) {
        return <TableColumnHeaderContainer {...props} controls={controls} type={type} />;
    }

    // Render group header if this row should have one
    if (groupHeader) {
        if (groupHeader === 'GROUP_HEADER') {
            // For non-first columns (pinned left, other main columns, pinned right),
            // render a styled cell that matches the group header styling
            // This ensures consistent row heights and styling across all grids
            return <div style={{ ...props.style }} />;
        }
        // Render the group header spanning full table width
        // If rendering in pinned left column, extend right to cover all columns
        // If rendering in main grid, extend left to cover pinned columns
        const pinnedLeftWidth =
            props.pinnedLeftColumnWidths?.reduce((sum, width) => sum + width, 0) || 0;

        // Determine if we're rendering in the first pinned left column
        const isFirstPinnedLeftColumn =
            props.columnIndex === 0 && (props.pinnedLeftColumnCount || 0) > 0;

        if (isFirstPinnedLeftColumn) {
            return (
                <div
                    style={{
                        ...props.style,
                        marginLeft: 0,
                        marginRight: 0,
                    }}
                >
                    {groupHeader}
                </div>
            );
        }

        // For main grid, use negative margin to extend left
        return (
            <div
                style={{
                    ...props.style,
                    marginLeft: pinnedLeftWidth > 0 ? `-${pinnedLeftWidth}px` : 0,
                }}
            >
                {groupHeader}
            </div>
        );
    }

    if (itemType !== LibraryItem.FOLDER) {
        switch (type) {
            case TableColumn.ACTIONS:
            case TableColumn.SKIP:
                return <ActionsColumn {...props} {...dragProps} controls={controls} type={type} />;

            case TableColumn.ALBUM:
                return <AlbumColumn {...props} {...dragProps} controls={controls} type={type} />;

            case TableColumn.ALBUM_ARTIST:
                return (
                    <AlbumArtistsColumn {...props} {...dragProps} controls={controls} type={type} />
                );

            case TableColumn.ALBUM_COUNT:
            case TableColumn.PLAY_COUNT:
            case TableColumn.SONG_COUNT:
                return <CountColumn {...props} {...dragProps} controls={controls} type={type} />;

            case TableColumn.ARTIST:
                return <ArtistsColumn {...props} {...dragProps} controls={controls} type={type} />;

            case TableColumn.BIOGRAPHY:
            case TableColumn.COMMENT:
                return <TextColumn {...props} {...dragProps} controls={controls} type={type} />;

            case TableColumn.BIT_DEPTH:
            case TableColumn.BIT_RATE:
            case TableColumn.BPM:
            case TableColumn.CHANNELS:
            case TableColumn.DISC_NUMBER:
            case TableColumn.SAMPLE_RATE:
            case TableColumn.TRACK_NUMBER:
                return <NumericColumn {...props} {...dragProps} controls={controls} type={type} />;

            case TableColumn.DATE_ADDED:
                return <DateColumn {...props} {...dragProps} controls={controls} type={type} />;

            case TableColumn.DURATION:
                return <DurationColumn {...props} {...dragProps} controls={controls} type={type} />;

            case TableColumn.GENRE:
                return <GenreColumn {...props} {...dragProps} controls={controls} type={type} />;

            case TableColumn.GENRE_BADGE:
                return (
                    <GenreBadgeColumn {...props} {...dragProps} controls={controls} type={type} />
                );

            case TableColumn.IMAGE:
                return <ImageColumn {...props} {...dragProps} controls={controls} type={type} />;

            case TableColumn.LAST_PLAYED:
                return (
                    <RelativeDateColumn {...props} {...dragProps} controls={controls} type={type} />
                );

            case TableColumn.PATH:
                return <PathColumn {...props} {...dragProps} controls={controls} type={type} />;

            case TableColumn.PLAYLIST_REORDER:
                return <PlaylistReorderColumn {...props} controls={controls} type={type} />;

            case TableColumn.RELEASE_DATE:
                return (
                    <AbsoluteDateColumn {...props} {...dragProps} controls={controls} type={type} />
                );

            case TableColumn.ROW_INDEX:
                return <RowIndexColumn {...props} {...dragProps} controls={controls} type={type} />;

            case TableColumn.SIZE:
                return <SizeColumn {...props} {...dragProps} controls={controls} type={type} />;

            case TableColumn.TITLE:
                return <TitleColumn {...props} {...dragProps} controls={controls} type={type} />;

            case TableColumn.TITLE_COMBINED:
                return (
                    <TitleCombinedColumn
                        {...props}
                        {...dragProps}
                        controls={controls}
                        type={type}
                    />
                );

            case TableColumn.USER_FAVORITE:
                return <FavoriteColumn {...props} {...dragProps} controls={controls} type={type} />;

            case TableColumn.USER_RATING:
                return <RatingColumn {...props} {...dragProps} controls={controls} type={type} />;

            case TableColumn.YEAR:
                return <YearColumn {...props} {...dragProps} controls={controls} type={type} />;

            default:
                return <DefaultColumn {...props} {...dragProps} controls={controls} type={type} />;
        }
    }

    switch (type) {
        case TableColumn.ACTIONS:
            return <ActionsColumn {...props} {...dragProps} controls={controls} type={type} />;

        case TableColumn.IMAGE:
            return <ImageColumn {...props} {...dragProps} controls={controls} type={type} />;

        case TableColumn.ROW_INDEX:
            return <RowIndexColumn {...props} {...dragProps} controls={controls} type={type} />;

        case TableColumn.TITLE:
            return <TitleColumn {...props} {...dragProps} controls={controls} type={type} />;

        case TableColumn.TITLE_COMBINED:
            return (
                <TitleCombinedColumn {...props} {...dragProps} controls={controls} type={type} />
            );

        default:
            return <ColumnNullFallback {...props} {...dragProps} controls={controls} type={type} />;
    }
};

const NonMutedColumns = [TableColumn.TITLE, TableColumn.TITLE_COMBINED];

export const TableColumnTextContainer = (
    props: ItemTableListColumn & {
        children: React.ReactNode;
        className?: string;
        containerClassName?: string;
        controls: ItemControls;
        dragRef?: null | React.Ref<HTMLDivElement>;
        isDraggedOver?: 'bottom' | 'top' | null;
        isDragging?: boolean;
        type: TableColumn;
    },
) => {
    const containerRef = useRef<HTMLDivElement>(null);
    const isDataRow = props.enableHeader ? props.rowIndex > 0 : true;
    const dataIndex = props.enableHeader ? props.rowIndex - 1 : props.rowIndex;
    const item = isDataRow ? props.data[props.rowIndex] : null;
    const itemRowId =
        item && typeof item === 'object' && 'id' in item
            ? props.internalState.extractRowId(item)
            : undefined;
    const isSelected = useItemSelectionState(props.internalState, itemRowId || undefined);

    const isDragging = props.isDragging ?? false;
    const mergedRef = useMergedRef(containerRef, props.dragRef ?? null);

    const isLastColumn = props.columnIndex === props.columns.length - 1;
    const isLastRow =
        isDataRow &&
        (props.enableHeader
            ? props.rowIndex === props.data.length
            : props.rowIndex === props.data.length - 1);

    // Apply dragged over state to all cells in the row so border can span entire row
    useEffect(() => {
        if (!isDataRow || !containerRef.current) return;
        const rowKey = `${props.tableId}-${props.rowIndex}`;
        const edge =
            props.isDraggedOver === 'top' || props.isDraggedOver === 'bottom'
                ? props.isDraggedOver
                : null;

        containerRef.current.dispatchEvent(
            new CustomEvent('itl:row-drag-over', {
                bubbles: true,
                detail: { edge, rowKey },
            }),
        );
    }, [isDataRow, props.isDraggedOver, props.rowIndex, props.tableId]);

    const handleClick = useDoubleClick({
        onDoubleClick: (event: React.MouseEvent<HTMLDivElement>) => {
            if (isDataRow && item) {
                const rowId = props.internalState.extractRowId(item);
                const index = rowId ? props.internalState.findItemIndex(rowId) : -1;
                props.controls.onDoubleClick?.({
                    event,
                    index,
                    internalState: props.internalState,
                    item: item as ItemListItem,
                    itemType: props.itemType,
                });
            }
        },
        onSingleClick: (event: React.MouseEvent<HTMLDivElement>) => {
            // Don't trigger row selection if clicking on interactive elements
            const target = event.target as HTMLElement;
            const isInteractiveElement = target.closest(
                'button, a, input, select, textarea, [role="button"]',
            );

            if (isInteractiveElement) {
                return;
            }

            if (isDataRow && item && props.enableSelection) {
                const rowId = props.internalState.extractRowId(item);
                const index = rowId ? props.internalState.findItemIndex(rowId) : -1;
                props.controls.onClick?.({
                    event,
                    index,
                    internalState: props.internalState,
                    item: item as ItemListItem,
                    itemType: props.itemType,
                });
            }
        },
    });

    const handleContextMenu = (event: React.MouseEvent<HTMLDivElement>) => {
        if (isDataRow && item) {
            event.preventDefault();
            const rowId = props.internalState.extractRowId(item);
            const index = rowId ? props.internalState.findItemIndex(rowId) : -1;
            props.controls.onMore?.({
                event,
                index,
                internalState: props.internalState,
                item: item as ItemListItem,
                itemType: props.itemType,
            });
        }
    };

    return (
        <div
            className={clsx(styles.container, props.containerClassName, {
                [styles.alternateRowEven]:
                    props.enableAlternateRowColors && isDataRow && dataIndex % 2 === 0,
                [styles.alternateRowOdd]:
                    props.enableAlternateRowColors && isDataRow && dataIndex % 2 === 1,
                [styles.center]: props.columns[props.columnIndex].align === 'center',
                [styles.compact]: props.size === 'compact',
                [styles.dataRow]: isDataRow,
                [styles.dragging]: isDataRow && isDragging,
                [styles.large]: props.size === 'large',
                [styles.left]: props.columns[props.columnIndex].align === 'start',
                [styles.paddingLg]: props.cellPadding === 'lg',
                [styles.paddingMd]: props.cellPadding === 'md',
                [styles.paddingSm]: props.cellPadding === 'sm',
                [styles.paddingXl]: props.cellPadding === 'xl',
                [styles.paddingXs]: props.cellPadding === 'xs',
                [styles.right]: props.columns[props.columnIndex].align === 'end',
                [styles.rowHoverHighlightEnabled]: isDataRow && props.enableRowHoverHighlight,
                [styles.rowSelected]: isDataRow && isSelected,
                [styles.withHorizontalBorder]:
                    props.enableHorizontalBorders &&
                    props.enableHeader &&
                    props.rowIndex > 0 &&
                    !isLastRow,
                [styles.withVerticalBorder]: props.enableVerticalBorders && !isLastColumn,
            })}
            data-row-index={isDataRow ? `${props.tableId}-${props.rowIndex}` : undefined}
            onClick={handleClick}
            onContextMenu={handleContextMenu}
            ref={mergedRef}
            style={props.style}
        >
            <Text
                className={clsx(styles.content, props.className, {
                    [styles.compact]: props.size === 'compact',
                    [styles.large]: props.size === 'large',
                })}
                isMuted={!NonMutedColumns.includes(props.type)}
                isNoSelect
            >
                {props.children}
            </Text>
        </div>
    );
};

export const TableColumnContainer = (
    props: ItemTableListColumn & {
        children: React.ReactNode;
        className?: string;
        containerStyle?: CSSProperties;
        controls: ItemControls;
        dragRef?: null | React.Ref<HTMLDivElement>;
        isDraggedOver?: 'bottom' | 'top' | null;
        isDragging?: boolean;
        type: TableColumn;
    },
) => {
    const containerRef = useRef<HTMLDivElement>(null);
    const isDataRow = props.enableHeader ? props.rowIndex > 0 : true;
    const dataIndex = props.enableHeader ? props.rowIndex - 1 : props.rowIndex;
    const item = isDataRow ? props.data[props.rowIndex] : null;
    const itemRowId =
        item && typeof item === 'object' && 'id' in item
            ? props.internalState.extractRowId(item)
            : undefined;
    const isSelected = useItemSelectionState(props.internalState, itemRowId || undefined);

    const isDragging = props.isDragging ?? false;
    const mergedRef = useMergedRef(containerRef, props.dragRef ?? null);

    const isLastColumn = props.columnIndex === props.columns.length - 1;
    const isLastRow =
        isDataRow &&
        (props.enableHeader
            ? props.rowIndex === props.data.length
            : props.rowIndex === props.data.length - 1);

    // Apply dragged over state to all cells in the row so border can span entire row
    useEffect(() => {
        if (!isDataRow || !containerRef.current) return;
        const rowKey = `${props.tableId}-${props.rowIndex}`;
        const edge =
            props.isDraggedOver === 'top' || props.isDraggedOver === 'bottom'
                ? props.isDraggedOver
                : null;

        containerRef.current.dispatchEvent(
            new CustomEvent('itl:row-drag-over', {
                bubbles: true,
                detail: { edge, rowKey },
            }),
        );
    }, [isDataRow, props.isDraggedOver, props.rowIndex, props.tableId]);

    const handleClick = useDoubleClick({
        onDoubleClick: (event: React.MouseEvent<HTMLDivElement>) => {
            if (isDataRow && item) {
                const rowId = props.internalState.extractRowId(item);
                const index = rowId ? props.internalState.findItemIndex(rowId) : -1;
                props.controls.onDoubleClick?.({
                    event,
                    index,
                    internalState: props.internalState,
                    item: item as ItemListItem,
                    itemType: props.itemType,
                });
            }
        },
        onSingleClick: (event: React.MouseEvent<HTMLDivElement>) => {
            // Don't trigger row selection if clicking on interactive elements
            const target = event.target as HTMLElement;
            const isInteractiveElement = target.closest(
                'button, a, input, select, textarea, [role="button"]',
            );

            if (isInteractiveElement) {
                return;
            }

            if (isDataRow && item && props.enableSelection) {
                const rowId = props.internalState.extractRowId(item);
                const index = rowId ? props.internalState.findItemIndex(rowId) : -1;
                props.controls.onClick?.({
                    event,
                    index,
                    internalState: props.internalState,
                    item: item as ItemListItem,
                    itemType: props.itemType,
                });
            }
        },
    });

    const handleContextMenu = (event: React.MouseEvent<HTMLDivElement>) => {
        if (isDataRow && item) {
            event.preventDefault();
            const rowId = props.internalState.extractRowId(item);
            const index = rowId ? props.internalState.findItemIndex(rowId) : -1;
            props.controls.onMore?.({
                event,
                index,
                internalState: props.internalState,
                item: item as ItemListItem,
                itemType: props.itemType,
            });
        }
    };

    return (
        <div
            className={clsx(styles.container, props.className, {
                [styles.alternateRowEven]:
                    props.enableAlternateRowColors && isDataRow && dataIndex % 2 === 0,
                [styles.alternateRowOdd]:
                    props.enableAlternateRowColors && isDataRow && dataIndex % 2 === 1,
                [styles.center]: props.columns[props.columnIndex].align === 'center',
                [styles.compact]: props.size === 'compact',
                [styles.dataRow]: isDataRow,
                [styles.dragging]: isDataRow && isDragging,
                [styles.large]: props.size === 'large',
                [styles.left]: props.columns[props.columnIndex].align === 'start',
                [styles.paddingLg]: props.cellPadding === 'lg',
                [styles.paddingMd]: props.cellPadding === 'md',
                [styles.paddingSm]: props.cellPadding === 'sm',
                [styles.paddingXl]: props.cellPadding === 'xl',
                [styles.paddingXs]: props.cellPadding === 'xs',
                [styles.right]: props.columns[props.columnIndex].align === 'end',
                [styles.rowHoverHighlightEnabled]: isDataRow && props.enableRowHoverHighlight,
                [styles.rowSelected]: isDataRow && isSelected,
                [styles.withHorizontalBorder]:
                    props.enableHorizontalBorders &&
                    props.enableHeader &&
                    props.rowIndex > 0 &&
                    !isLastRow,
                [styles.withVerticalBorder]: props.enableVerticalBorders && !isLastColumn,
            })}
            data-row-index={isDataRow ? `${props.tableId}-${props.rowIndex}` : undefined}
            onClick={handleClick}
            onContextMenu={handleContextMenu}
            ref={mergedRef}
            style={{ ...props.containerStyle, ...props.style }}
        >
            {props.children}
        </div>
    );
};

interface ColumnResizeHandleProps {
    columnId: TableColumn;
    initialWidth: number;
    onResize: (columnId: TableColumn, width: number) => void;
    side: 'left' | 'right';
}

const ColumnResizeHandle = ({
    columnId,
    initialWidth,
    onResize,
    side,
}: ColumnResizeHandleProps) => {
    const [isDragging, setIsDragging] = useState(false);
    const handleRef = useRef<HTMLDivElement>(null);
    const startWidthRef = useRef<number>(initialWidth);
    const startXRef = useRef<number>(0);
    const finalWidthRef = useRef<number>(initialWidth);

    // Update the ref when initialWidth changes (but not during drag)
    useEffect(() => {
        if (!isDragging) {
            startWidthRef.current = initialWidth;
        }
    }, [initialWidth, isDragging]);

    useEffect(() => {
        if (!isDragging) return;

        const handleMouseMove = (event: MouseEvent) => {
            const deltaX = event.clientX - startXRef.current;
            const newWidth = Math.min(Math.max(10, startWidthRef.current + deltaX), 1000);
            finalWidthRef.current = newWidth;
        };

        const handleMouseUp = () => {
            setIsDragging(false);
            document.body.style.cursor = '';
            document.body.style.userSelect = '';
            document.removeEventListener('mousemove', handleMouseMove);
            document.removeEventListener('mouseup', handleMouseUp);
            onResize(columnId, finalWidthRef.current);
        };

        document.addEventListener('mousemove', handleMouseMove);
        document.addEventListener('mouseup', handleMouseUp);

        return () => {
            document.removeEventListener('mousemove', handleMouseMove);
            document.removeEventListener('mouseup', handleMouseUp);
        };
    }, [isDragging, columnId, onResize]);

    const handleMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {
        event.preventDefault();
        event.stopPropagation();
        setIsDragging(true);
        startWidthRef.current = initialWidth;
        startXRef.current = event.clientX;
        document.body.style.cursor = 'col-resize';
        document.body.style.userSelect = 'none';
    };

    return (
        <div
            className={clsx(styles.resizeHandle, {
                [styles.resizeHandleDragging]: isDragging,
                [styles.resizeHandleLeft]: side === 'left',
                [styles.resizeHandleRight]: side === 'right',
            })}
            onMouseDown={handleMouseDown}
            ref={handleRef}
        />
    );
};

export const TableColumnHeaderContainer = (
    props: ItemTableListColumn & {
        className?: string;
        containerClassName?: string;
        controls: ItemControls;
        type: TableColumn;
    },
) => {
    const columnConfig = props.columns[props.columnIndex];
    // Use the actual rendered width from style if available, otherwise fall back to config width
    const currentWidth = (props.style?.width as number | undefined) || columnConfig.width;

    const handleResize = (columnId: TableColumn, width: number) => {
        props.controls.onColumnResized?.({ columnId, width });
    };

    const containerRef = useRef<HTMLDivElement>(null);
    const [isDragging, setIsDragging] = useState(false);
    const [isDraggedOver, setIsDraggedOver] = useState<Edge | null>(null);

    useEffect(() => {
        if (!containerRef.current || !props.enableColumnReorder) {
            return;
        }

        const handleReorder = (
            columnIdFrom: TableColumn,
            columnIdTo: TableColumn,
            edge: Edge | null,
        ) => {
            props.controls.onColumnReordered?.({ columnIdFrom, columnIdTo, edge });
        };

        return combine(
            draggable({
                element: containerRef.current,
                getInitialData: () => {
                    const data = dndUtils.generateDragData(
                        {
                            id: [props.type],
                            operation: [DragOperation.REORDER],
                            type: DragTarget.TABLE_COLUMN,
                        },
                        { tableId: props.tableId },
                    );
                    return data;
                },
                onDragStart: () => {
                    setIsDragging(true);
                },
                onDrop: () => {
                    setIsDragging(false);
                },
                onGenerateDragPreview: (data) => {
                    disableNativeDragPreview({ nativeSetDragImage: data.nativeSetDragImage });
                },
            }),
            dropTargetForElements({
                canDrop: (args) => {
                    const data = args.source.data as unknown as DragData;
                    const sourceTableId = (data.metadata as { tableId?: string })?.tableId;
                    const isSelf = (args.source.data.id as string[])[0] === props.type;
                    const isSameTable = sourceTableId === props.tableId;
                    return (
                        dndUtils.isDropTarget(data.type, [DragTarget.TABLE_COLUMN]) &&
                        !isSelf &&
                        isSameTable
                    );
                },
                element: containerRef.current,
                getData: ({ element, input }) => {
                    const data = dndUtils.generateDragData(
                        {
                            id: [props.type],
                            operation: [DragOperation.REORDER],
                            type: DragTarget.TABLE_COLUMN,
                        },
                        { tableId: props.tableId },
                    );

                    return attachClosestEdge(data, {
                        allowedEdges: ['left', 'right'],
                        element,
                        input,
                    });
                },
                onDrag: (args) => {
                    const closestEdgeOfTarget: Edge | null = extractClosestEdge(args.self.data);
                    setIsDraggedOver(closestEdgeOfTarget);
                },
                onDragLeave: () => {
                    setIsDraggedOver(null);
                },
                onDrop: (args) => {
                    const closestEdgeOfTarget: Edge | null = extractClosestEdge(args.self.data);

                    const from = args.source.data.id as string[];
                    const to = args.self.data.id as string[];

                    handleReorder(
                        from[0] as TableColumn,
                        to[0] as TableColumn,
                        closestEdgeOfTarget,
                    );
                    setIsDraggedOver(null);
                },
            }),
        );
    }, [props.type, props.enableColumnReorder, props.controls, props.tableId]);

    return (
        <Flex
            className={clsx(styles.container, styles.headerContainer, props.containerClassName, {
                [styles.headerDraggedOverLeft]: isDraggedOver === 'left',
                [styles.headerDraggedOverRight]: isDraggedOver === 'right',
                [styles.headerDragging]: isDragging,
                [styles.paddingLg]: props.cellPadding === 'lg',
                [styles.paddingMd]: props.cellPadding === 'md',
                [styles.paddingSm]: props.cellPadding === 'sm',
                [styles.paddingXl]: props.cellPadding === 'xl',
                [styles.paddingXs]: props.cellPadding === 'xs',
            })}
            ref={containerRef}
            style={props.style}
        >
            <Text
                className={clsx(styles.headerContent, props.className, {
                    [styles.center]: props.columns[props.columnIndex].align === 'center',
                    [styles.left]: props.columns[props.columnIndex].align === 'start',
                    [styles.right]: props.columns[props.columnIndex].align === 'end',
                })}
                isNoSelect
            >
                {columnLabelMap[props.type]}
            </Text>
            {!columnConfig.autoSize && props.enableColumnResize && (
                <ColumnResizeHandle
                    columnId={props.type}
                    initialWidth={currentWidth}
                    onResize={handleResize}
                    side="right"
                />
            )}
        </Flex>
    );
};

const columnLabelMap: Record<TableColumn, ReactNode | string> = {
    [TableColumn.ACTIONS]: (
        <Flex className={styles.headerIconWrapper}>
            <Icon fill="default" icon="ellipsisHorizontal" />
        </Flex>
    ),
    [TableColumn.ALBUM]: i18n.t('table.column.album', { postProcess: 'upperCase' }) as string,
    [TableColumn.ALBUM_ARTIST]: i18n.t('table.column.albumArtist', {
        postProcess: 'upperCase',
    }) as string,
    [TableColumn.ALBUM_COUNT]: i18n.t('table.column.albumCount', {
        postProcess: 'upperCase',
    }) as string,
    [TableColumn.ARTIST]: i18n.t('table.column.artist', { postProcess: 'upperCase' }) as string,
    [TableColumn.BIOGRAPHY]: i18n.t('table.column.biography', {
        postProcess: 'upperCase',
    }) as string,
    [TableColumn.BIT_DEPTH]: i18n.t('table.column.bitDepth', {
        postProcess: 'upperCase',
    }) as string,
    [TableColumn.BIT_RATE]: i18n.t('table.column.bitrate', { postProcess: 'upperCase' }) as string,
    [TableColumn.BPM]: i18n.t('table.column.bpm', { postProcess: 'upperCase' }) as string,
    [TableColumn.CHANNELS]: i18n.t('table.column.channels', { postProcess: 'upperCase' }) as string,
    [TableColumn.CODEC]: i18n.t('table.column.codec', { postProcess: 'upperCase' }) as string,
    [TableColumn.COMMENT]: i18n.t('table.column.comment', { postProcess: 'upperCase' }) as string,
    [TableColumn.DATE_ADDED]: i18n.t('table.column.dateAdded', {
        postProcess: 'upperCase',
    }) as string,
    [TableColumn.DISC_NUMBER]: (
        <Flex className={styles.headerIconWrapper}>
            <Icon icon="disc" />
        </Flex>
    ),
    [TableColumn.DURATION]: (
        <Flex className={styles.headerIconWrapper}>
            <Icon icon="duration" />
        </Flex>
    ),
    [TableColumn.GENRE]: i18n.t('table.column.genre', { postProcess: 'upperCase' }) as string,
    [TableColumn.GENRE_BADGE]: i18n.t('table.column.genre', {
        postProcess: 'upperCase',
    }) as string,
    [TableColumn.ID]: 'ID',
    [TableColumn.IMAGE]: '',
    [TableColumn.LAST_PLAYED]: i18n.t('table.column.lastPlayed', {
        postProcess: 'upperCase',
    }) as string,
    [TableColumn.OWNER]: i18n.t('table.column.owner', { postProcess: 'upperCase' }) as string,
    [TableColumn.PATH]: i18n.t('table.column.path', { postProcess: 'upperCase' }) as string,
    [TableColumn.PLAY_COUNT]: i18n.t('table.column.playCount', {
        postProcess: 'upperCase',
    }) as string,
    [TableColumn.PLAYLIST_REORDER]: (
        <Flex className={styles.headerIconWrapper}>
            <Icon icon="dragVertical" />
        </Flex>
    ),
    [TableColumn.RELEASE_DATE]: i18n.t('table.column.releaseDate', {
        postProcess: 'upperCase',
    }) as string,
    [TableColumn.ROW_INDEX]: (
        <Flex className={styles.headerIconWrapper}>
            <Icon icon="hash" />
        </Flex>
    ),
    [TableColumn.SAMPLE_RATE]: i18n.t('table.column.sampleRate', {
        postProcess: 'upperCase',
    }) as string,
    [TableColumn.SIZE]: i18n.t('table.column.size', { postProcess: 'upperCase' }) as string,
    [TableColumn.SKIP]: '',
    [TableColumn.SONG_COUNT]: i18n.t('table.column.songCount', {
        postProcess: 'upperCase',
    }) as string,
    [TableColumn.TITLE]: i18n.t('table.column.title', { postProcess: 'upperCase' }) as string,
    [TableColumn.TITLE_COMBINED]: i18n.t('table.column.title', {
        postProcess: 'upperCase',
    }) as string,
    [TableColumn.TRACK_NUMBER]: (
        <Flex className={styles.headerIconWrapper}>
            <Icon icon="itemSong" />
        </Flex>
    ),
    [TableColumn.USER_FAVORITE]: (
        <Flex className={styles.headerIconWrapper}>
            <Icon icon="favorite" />
        </Flex>
    ),
    [TableColumn.USER_RATING]: i18n.t('table.column.rating', {
        postProcess: 'upperCase',
    }) as string,
    [TableColumn.YEAR]: i18n.t('table.column.releaseYear', { postProcess: 'upperCase' }) as string,
};

export const ColumnNullFallback = (props: ItemTableListInnerColumn) => {
    return <TableColumnTextContainer {...props}>&nbsp;</TableColumnTextContainer>;
};

export const ColumnSkeletonVariable = (props: ItemTableListInnerColumn) => {
    return (
        <TableColumnContainer {...props}>
            <Skeleton height="1rem" width={`${props.rowIndex % 2 === 0 ? '80%' : '60%'}`} />
        </TableColumnContainer>
    );
};

export const ColumnSkeletonFixed = (props: ItemTableListInnerColumn) => {
    return (
        <TableColumnContainer {...props}>
            <Skeleton height="1rem" width="80%" />
        </TableColumnContainer>
    );
};
