import { describe, test, expect, beforeEach, vi } from 'vitest'
import { render, screen, fireEvent, cleanup } from '@testing-library/react'
import { ChangeColumn, CHANGE_COLUMN_TOOL_ID } from './change-column'
import { useWidgetStore } from '../../stores/widget-store'
import type { TableColumn } from '../../table/types'

// Mock @dnd-kit with minimal implementation for testing
vi.mock('@dnd-kit/core', async () => {
  const actual = await vi.importActual('@dnd-kit/core')
  return {
    ...actual,
    useSensor: () => ({}),
    useSensors: () => [],
  }
})

describe('ChangeColumn', () => {
  const widgetId = 'test-change-column-widget'

  const mockColumns: TableColumn[] = [
    { id: 'name', label: 'Name' },
    { id: 'country', label: 'Country' },
    { id: 'population', label: 'Population' },
  ]

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

  test('returns null when widget has no columns', () => {
    const { container } = render(<ChangeColumn id={widgetId} />)
    expect(container.firstChild).toBeNull()
  })

  test('returns null when widget has fewer than 2 columns', () => {
    useWidgetStore.getState().setWidget(widgetId, {
      columns: [{ id: 'name', label: 'Name' }],
    })

    const { container } = render(<ChangeColumn id={widgetId} />)
    expect(container.firstChild).toBeNull()
  })

  test('renders change column button when widget has 2 or more columns', () => {
    useWidgetStore.getState().setWidget(widgetId, {
      columns: mockColumns,
    })

    render(<ChangeColumn id={widgetId} />)

    const button = screen.getByRole('button')
    expect(button).toBeTruthy()
  })

  test('shows default tooltip label', () => {
    useWidgetStore.getState().setWidget(widgetId, {
      columns: mockColumns,
    })

    render(<ChangeColumn id={widgetId} />)

    const button = screen.getByRole('button', { name: 'Change column' })
    expect(button).toBeTruthy()
  })

  test('opens menu on button click', () => {
    useWidgetStore.getState().setWidget(widgetId, {
      columns: mockColumns,
    })

    render(<ChangeColumn id={widgetId} />)

    const button = screen.getByRole('button')
    fireEvent.click(button)

    // Menu should be open with all columns for reordering
    expect(screen.getByRole('menu')).toBeTruthy()
    expect(screen.getByRole('menuitem', { name: /Name/i })).toBeTruthy()
    expect(screen.getByRole('menuitem', { name: /Country/i })).toBeTruthy()
    expect(screen.getByRole('menuitem', { name: /Population/i })).toBeTruthy()
  })

  test('displays all columns in menu for drag-and-drop reordering', () => {
    useWidgetStore.getState().setWidget(widgetId, {
      columns: mockColumns,
    })

    render(<ChangeColumn id={widgetId} />)

    const button = screen.getByRole('button')
    fireEvent.click(button)

    // All three columns should be visible
    const menuItems = screen.getAllByRole('menuitem')
    expect(menuItems).toHaveLength(3)
  })

  test('uses custom labels when provided', () => {
    useWidgetStore.getState().setWidget(widgetId, {
      columns: mockColumns,
    })

    const customLabels = {
      tooltip: 'Reorder columns',
      ariaLabel: 'Reorder table columns',
    }

    render(<ChangeColumn id={widgetId} labels={customLabels} />)

    const button = screen.getByRole('button', { name: 'Reorder table columns' })
    expect(button).toBeTruthy()
  })

  test('works with exactly 2 columns', () => {
    const twoColumns: TableColumn[] = [
      { id: 'first', label: 'First' },
      { id: 'second', label: 'Second' },
    ]

    useWidgetStore.getState().setWidget(widgetId, {
      columns: twoColumns,
    })

    render(<ChangeColumn id={widgetId} />)

    const button = screen.getByRole('button')
    fireEvent.click(button)

    // Both columns should be available for reordering
    expect(screen.getByRole('menuitem', { name: /First/i })).toBeTruthy()
    expect(screen.getByRole('menuitem', { name: /Second/i })).toBeTruthy()
  })

  test('sets data-active attribute when menu is open', () => {
    useWidgetStore.getState().setWidget(widgetId, {
      columns: mockColumns,
    })

    render(<ChangeColumn id={widgetId} />)

    const button = screen.getByRole('button')
    expect(button.getAttribute('data-active')).toBe('false')

    fireEvent.click(button)
    expect(button.getAttribute('data-active')).toBe('true')
  })

  test('menu items are keyboard focusable', () => {
    useWidgetStore.getState().setWidget(widgetId, {
      columns: mockColumns,
    })

    render(<ChangeColumn id={widgetId} />)

    const button = screen.getByRole('button')
    fireEvent.click(button)

    const menuItems = screen.getAllByRole('menuitem')
    menuItems.forEach((item) => {
      expect(item.getAttribute('tabindex')).toBe('0')
    })
  })

  describe('config tool registration', () => {
    test('registers config tool on mount', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        columns: mockColumns,
      })

      render(<ChangeColumn id={widgetId} />)

      const widget = useWidgetStore.getState().getWidget(widgetId)
      const tool = widget?.registeredTools?.find(
        (t) => t.id === CHANGE_COLUMN_TOOL_ID,
      )

      expect(tool).toBeDefined()
      expect(tool?.type).toBe('config')
      expect(tool?.order).toBe(100)
      expect(tool?.enabled).toBe(true)
    })

    test('unregisters config tool on unmount', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        columns: mockColumns,
      })

      render(<ChangeColumn id={widgetId} />)

      // Verify tool is registered
      let widget = useWidgetStore.getState().getWidget(widgetId)
      expect(
        widget?.registeredTools?.find((t) => t.id === CHANGE_COLUMN_TOOL_ID),
      ).toBeDefined()

      // Unmount
      cleanup()

      // Verify tool is unregistered
      widget = useWidgetStore.getState().getWidget(widgetId)
      expect(
        widget?.registeredTools?.find((t) => t.id === CHANGE_COLUMN_TOOL_ID),
      ).toBeUndefined()
    })

    test('config tool returns currentConfig unchanged when columns match widget state', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        columns: mockColumns,
      })

      render(<ChangeColumn id={widgetId} />)

      const widget = useWidgetStore.getState().getWidget(widgetId)
      const tool = widget?.registeredTools?.find(
        (t) => t.id === CHANGE_COLUMN_TOOL_ID,
      )

      // When config columns match widget columns, fn returns the same reference
      const input = { columns: mockColumns }
      const result = tool?.fn(input)
      expect(result).toBe(input)
    })

    test('column order persists when config pipeline re-runs with original config', async () => {
      useWidgetStore.getState().setWidget(widgetId, {
        columns: mockColumns,
      })

      render(<ChangeColumn id={widgetId} />)

      // Simulate a drag that reorders columns via setWidget
      const reorderedColumns: TableColumn[] = [
        { id: 'population', label: 'Population' },
        { id: 'name', label: 'Name' },
        { id: 'country', label: 'Country' },
      ]
      useWidgetStore.getState().setWidget(widgetId, {
        columns: reorderedColumns,
      })

      // Simulate config pipeline re-running with original column order
      const originalConfig = { columns: mockColumns }
      await useWidgetStore
        .getState()
        .executeConfigPipeline(widgetId, originalConfig)

      // Verify the columns are in the reordered order
      const widget = useWidgetStore.getState().getWidget(widgetId)
      const resultColumns = (widget as unknown as Record<string, unknown>)
        ?.columns as TableColumn[] | undefined
      expect(resultColumns?.map((c) => c.id)).toEqual([
        'population',
        'name',
        'country',
      ])
    })

    test('new columns appended at the end when not in widget order', async () => {
      useWidgetStore.getState().setWidget(widgetId, {
        columns: mockColumns,
      })

      render(<ChangeColumn id={widgetId} />)

      // Simulate widget columns having only a partial order (e.g., user dragged 2 of 3)
      const partialOrder: TableColumn[] = [
        { id: 'country', label: 'Country' },
        { id: 'name', label: 'Name' },
      ]
      useWidgetStore.getState().setWidget(widgetId, {
        columns: partialOrder,
      })

      // Config pipeline runs with all columns (includes 'population' not in widget order)
      const originalConfig = { columns: mockColumns }
      await useWidgetStore
        .getState()
        .executeConfigPipeline(widgetId, originalConfig)

      const widget = useWidgetStore.getState().getWidget(widgetId)
      const resultColumns = (widget as unknown as Record<string, unknown>)
        ?.columns as TableColumn[] | undefined
      expect(resultColumns?.map((c) => c.id)).toEqual([
        'country',
        'name',
        'population',
      ])
    })
  })
})
