/**
 * Tests for ChangeColumn's handleDragEnd + reorderFn branches that
 * require driving the @dnd-kit/core onDragEnd callback. Uses the shared
 * `tests/mocks/dnd-kit` harness so the DnD-kit pointer machinery is
 * replaced with a passthrough that exposes the onDragEnd prop directly.
 *
 * Complements the existing change-column.test.tsx which covers render +
 * menu + tool-registration but doesn't drive drag events.
 */
import { describe, test, expect, beforeEach, vi } from 'vitest'
import { render } from '@testing-library/react'
import {
  dndKitCoreMock,
  dndKitSortableMock,
  lastDndContextProps,
  resetDndKitMocks,
} from '../../../../tests/mocks/dnd-kit'

vi.mock('@dnd-kit/core', () => dndKitCoreMock())
vi.mock('@dnd-kit/sortable', () => dndKitSortableMock())

import { ChangeColumn, CHANGE_COLUMN_TOOL_ID } from './change-column'
import { useWidgetStore } from '../../stores/widget-store'
import type { TableColumn } from '../../table/types'

const widgetId = 'change-column-dnd'
const columns: TableColumn[] = [
  { id: 'name', label: 'Name' },
  { id: 'country', label: 'Country' },
  { id: 'population', label: 'Population' },
]

function seedColumns(extra: Partial<{ columns: TableColumn[] }> = {}) {
  useWidgetStore.getState().setWidget(widgetId, {
    columns,
    ...extra,
  })
}

beforeEach(() => {
  useWidgetStore.getState().clearWidgets()
  resetDndKitMocks()
})

describe('handleDragEnd branches', () => {
  test('reorders columns when active and over differ', () => {
    seedColumns()
    render(<ChangeColumn id={widgetId} />)

    // Drag 'name' (index 0) over 'population' (index 2)
    lastDndContextProps().onDragEnd?.({
      active: { id: 'name' },
      over: { id: 'population' },
    })

    const widget = useWidgetStore.getState().getWidget(widgetId)
    const result = (widget as unknown as { columns: TableColumn[] }).columns
    expect(result.map((c) => c.id)).toEqual(['country', 'population', 'name'])
  })

  test('no-op when `over` is missing (drop outside the sortable area)', () => {
    seedColumns()
    render(<ChangeColumn id={widgetId} />)
    const before = useWidgetStore.getState().getWidget(widgetId)
    lastDndContextProps().onDragEnd?.({
      active: { id: 'name' },
      over: null,
    })
    const after = useWidgetStore.getState().getWidget(widgetId)
    // No reorder; column order unchanged
    const beforeIds = (
      before as unknown as { columns: TableColumn[] }
    ).columns.map((c) => c.id)
    const afterIds = (
      after as unknown as { columns: TableColumn[] }
    ).columns.map((c) => c.id)
    expect(afterIds).toEqual(beforeIds)
  })

  test('no-op when active.id === over.id (no movement)', () => {
    seedColumns()
    render(<ChangeColumn id={widgetId} />)
    lastDndContextProps().onDragEnd?.({
      active: { id: 'name' },
      over: { id: 'name' },
    })
    const widget = useWidgetStore.getState().getWidget(widgetId)
    const result = (widget as unknown as { columns: TableColumn[] }).columns
    expect(result.map((c) => c.id)).toEqual(['name', 'country', 'population'])
  })

  test('no-op when widget has no columns (returns early)', () => {
    // No columns seeded but the component renders for the test we still
    // want to set up — minimum 2 columns are required for the menu to open.
    // Render with 2 columns then clear them.
    seedColumns()
    render(<ChangeColumn id={widgetId} />)
    useWidgetStore.getState().setWidget(widgetId, { columns: undefined })
    expect(() =>
      lastDndContextProps().onDragEnd?.({
        active: { id: 'name' },
        over: { id: 'country' },
      }),
    ).not.toThrow()
  })

  test('no-op when active.id is not found in columns (oldIndex === -1)', () => {
    seedColumns()
    render(<ChangeColumn id={widgetId} />)
    lastDndContextProps().onDragEnd?.({
      active: { id: 'unknown-col' },
      over: { id: 'name' },
    })
    const widget = useWidgetStore.getState().getWidget(widgetId)
    const result = (widget as unknown as { columns: TableColumn[] }).columns
    expect(result.map((c) => c.id)).toEqual(['name', 'country', 'population'])
  })

  test('no-op when over.id is not found in columns (newIndex === -1)', () => {
    seedColumns()
    render(<ChangeColumn id={widgetId} />)
    lastDndContextProps().onDragEnd?.({
      active: { id: 'name' },
      over: { id: 'unknown-col' },
    })
    const widget = useWidgetStore.getState().getWidget(widgetId)
    const result = (widget as unknown as { columns: TableColumn[] }).columns
    expect(result.map((c) => c.id)).toEqual(['name', 'country', 'population'])
  })
})

describe('reorderFn branches', () => {
  test('returns currentConfig when widget has no columns', () => {
    seedColumns()
    render(<ChangeColumn id={widgetId} />)
    const widget = useWidgetStore.getState().getWidget(widgetId)
    const tool = widget?.registeredTools?.find(
      (t) => t.id === CHANGE_COLUMN_TOOL_ID,
    )
    // Clear columns from the widget
    useWidgetStore.getState().setWidget(widgetId, { columns: [] })
    const input = { columns }
    expect(tool?.fn(input)).toBe(input)
  })

  test('returns currentConfig when config has no columns', () => {
    seedColumns()
    render(<ChangeColumn id={widgetId} />)
    const widget = useWidgetStore.getState().getWidget(widgetId)
    const tool = widget?.registeredTools?.find(
      (t) => t.id === CHANGE_COLUMN_TOOL_ID,
    )
    const input = { columns: [] }
    expect(tool?.fn(input)).toBe(input)
  })

  test('returns currentConfig when config columns key is undefined', () => {
    seedColumns()
    render(<ChangeColumn id={widgetId} />)
    const widget = useWidgetStore.getState().getWidget(widgetId)
    const tool = widget?.registeredTools?.find(
      (t) => t.id === CHANGE_COLUMN_TOOL_ID,
    )
    const input = { other: 1 }
    expect(tool?.fn(input)).toBe(input)
  })

  test('drops config columns that have no widget-column counterpart', () => {
    seedColumns({ columns: [{ id: 'name', label: 'Name' }] }) // widget has only 'name'
    render(<ChangeColumn id={widgetId} />)
    const widget = useWidgetStore.getState().getWidget(widgetId)
    const tool = widget?.registeredTools?.find(
      (t) => t.id === CHANGE_COLUMN_TOOL_ID,
    )
    // Config has more columns; the reorder drops the loop's `if (col)` true
    // path for matches and the trailing "append leftovers" loop for extras.
    // With widget=['name'], the reordered result should be ['name'] followed
    // by the remaining config columns in their original order.
    const input = {
      columns: [
        { id: 'population', label: 'Population' },
        { id: 'name', label: 'Name' },
        { id: 'country', label: 'Country' },
      ],
    }
    const result = tool?.fn(input) as { columns: TableColumn[] }
    expect(result.columns.map((c) => c.id)).toEqual([
      'name',
      'population',
      'country',
    ])
  })
})
