import { render, screen } from '@testing-library/react'
import type { DropResult } from '@hello-pangea/dnd'
import { KanbanBoard, resolveKanbanMove } from '../KanbanBoard'

interface Card {
  id: string
  title: string
}

const columns = [
  { id: 'todo', label: 'To Do', color: '#6366f1' },
  { id: 'in-progress', label: 'In Progress', color: '#f59e0b' },
  { id: 'done', label: 'Done' },
]

const items: Record<string, Card[]> = {
  todo: [{ id: '1', title: 'Task One' }, { id: '2', title: 'Task Two' }],
  'in-progress': [{ id: '3', title: 'Task Three' }],
  done: [],
}

describe('KanbanBoard', () => {
  const renderCard = (item: Card) => (
    <div key={item.id} data-testid={`card-${item.id}`}>
      {item.title}
    </div>
  )

  it('renders all column labels', () => {
    render(
      <KanbanBoard columns={columns} items={items} renderCard={renderCard} />
    )
    expect(screen.getByText('To Do')).toBeInTheDocument()
    expect(screen.getByText('In Progress')).toBeInTheDocument()
    expect(screen.getByText('Done')).toBeInTheDocument()
  })

  it('renders all card items', () => {
    render(
      <KanbanBoard columns={columns} items={items} renderCard={renderCard} />
    )
    expect(screen.getByText('Task One')).toBeInTheDocument()
    expect(screen.getByText('Task Two')).toBeInTheDocument()
    expect(screen.getByText('Task Three')).toBeInTheDocument()
  })

  it('shows item counts per column in the default header', () => {
    render(
      <KanbanBoard columns={columns} items={items} renderCard={renderCard} />
    )
    // todo has 2, in-progress has 1, done has 0
    const counts = screen.getAllByText(/^[0-9]+$/)
    const countValues = counts.map((el) => el.textContent)
    expect(countValues).toContain('2')
    expect(countValues).toContain('1')
    expect(countValues).toContain('0')
  })

  it('shows empty column message when column has no items', () => {
    render(
      <KanbanBoard columns={columns} items={items} renderCard={renderCard} />
    )
    expect(screen.getByText('No items')).toBeInTheDocument()
  })

  it('shows custom empty column message', () => {
    render(
      <KanbanBoard
        columns={columns}
        items={items}
        renderCard={renderCard}
        emptyColumnMessage="Nothing here yet"
      />
    )
    expect(screen.getByText('Nothing here yet')).toBeInTheDocument()
  })

  it('renders color dot for columns that have a color', () => {
    const { container } = render(
      <KanbanBoard columns={columns} items={items} renderCard={renderCard} />
    )
    // Two columns have colors (todo, in-progress), one does not (done)
    const colorDots = container.querySelectorAll('[aria-hidden="true"]')
    expect(colorDots.length).toBe(2)
  })

  it('uses custom column header when renderColumnHeader is provided', () => {
    const renderColumnHeader = (col: typeof columns[0]) => (
      <div data-testid={`custom-header-${col.id}`}>Custom: {col.label}</div>
    )
    render(
      <KanbanBoard
        columns={columns}
        items={items}
        renderCard={renderCard}
        renderColumnHeader={renderColumnHeader}
      />
    )
    expect(screen.getByTestId('custom-header-todo')).toBeInTheDocument()
    expect(screen.getByText('Custom: To Do')).toBeInTheDocument()
  })

  it('renders column footer when renderColumnFooter is provided', () => {
    const renderColumnFooter = (col: typeof columns[0]) => (
      <div data-testid={`footer-${col.id}`}>Footer for {col.label}</div>
    )
    render(
      <KanbanBoard
        columns={columns}
        items={items}
        renderCard={renderCard}
        renderColumnFooter={renderColumnFooter}
      />
    )
    expect(screen.getByTestId('footer-todo')).toBeInTheDocument()
    expect(screen.getByText('Footer for To Do')).toBeInTheDocument()
  })

  it('handles missing items for a column gracefully', () => {
    // Only provide items for one column
    const partialItems: Record<string, Card[]> = {
      todo: [{ id: '1', title: 'Task One' }],
    }
    render(
      <KanbanBoard columns={columns} items={partialItems} renderCard={renderCard} />
    )
    // The other two columns should show the empty message
    const emptyMessages = screen.getAllByText('No items')
    expect(emptyMessages.length).toBe(2)
  })

  it('applies custom className to container', () => {
    const { container } = render(
      <KanbanBoard
        columns={columns}
        items={items}
        renderCard={renderCard}
        className="my-board"
      />
    )
    expect(container.firstChild).toHaveClass('my-board')
  })

  it('applies ring styles to column that isColumnOver returns true for', () => {
    const isColumnOver = (id: string) => id === 'todo'
    const { container } = render(
      <KanbanBoard
        columns={columns}
        items={items}
        renderCard={renderCard}
        isColumnOver={isColumnOver}
      />
    )
    // The first column div should have ring classes
    const firstCol = container.querySelector('[style*="320"]')
    expect(firstCol).toHaveClass('ring-2')
  })

  it('renders draggable cards when getCardId + onCardMove are provided (DnD enabled)', () => {
    const onCardMove = jest.fn()
    render(
      <KanbanBoard
        columns={columns}
        items={items}
        renderCard={renderCard}
        getCardId={(c) => c.id}
        onCardMove={onCardMove}
      />
    )
    // cards still render through the @hello-pangea/dnd path without crashing
    expect(screen.getByTestId('card-1')).toBeInTheDocument()
    expect(screen.getByTestId('card-3')).toBeInTheDocument()
    expect(screen.getByText('To Do')).toBeInTheDocument()
  })
})

describe('resolveKanbanMove', () => {
  const drop = (
    draggableId: string,
    from: string,
    fromIndex: number,
    to?: string,
    toIndex?: number,
  ): DropResult =>
    ({
      draggableId,
      type: 'DEFAULT',
      source: { droppableId: from, index: fromIndex },
      destination: to === undefined ? null : { droppableId: to, index: toIndex ?? 0 },
      reason: 'DROP',
      mode: 'FLUID',
      combine: null,
    } as unknown as DropResult)

  it('resolves a cross-column move', () => {
    expect(resolveKanbanMove(drop('c1', 'new', 0, 'surfaced', 2))).toEqual({
      cardId: 'c1',
      fromColumnId: 'new',
      toColumnId: 'surfaced',
      toIndex: 2,
    })
  })

  it('returns null when dropped outside any column', () => {
    expect(resolveKanbanMove(drop('c1', 'new', 0))).toBeNull()
  })

  it('returns null when dropped back onto the same slot', () => {
    expect(resolveKanbanMove(drop('c1', 'new', 1, 'new', 1))).toBeNull()
  })

  it('resolves a within-column reorder', () => {
    expect(resolveKanbanMove(drop('c1', 'new', 0, 'new', 3))).toEqual({
      cardId: 'c1',
      fromColumnId: 'new',
      toColumnId: 'new',
      toIndex: 3,
    })
  })
})
