import { describe, it, expect, vi } from 'vitest'
import { render, screen, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import type { ColumnDef } from '@tanstack/react-table'
import { DataTable } from '@/components/ui/data-table'

interface Row {
  id: string
  name: string
  email: string
  role: string
  lastLogin: string
}

const mockRows: Row[] = [
  { id: '1', name: 'Juan Pérez',  email: 'juan@x.com',   role: 'admin',  lastLogin: '2026-01-15' },
  { id: '2', name: 'Ana García',  email: 'ana@x.com',    role: 'cajero', lastLogin: '2026-01-10' },
  { id: '3', name: 'Luis Torres', email: 'luis@x.com',   role: 'cajero', lastLogin: '2026-01-08' },
]

const columns: ColumnDef<Row>[] = [
  {
    accessorKey: 'name',
    header: 'Nombre',
    meta: { priority: 'high' },
  },
  {
    accessorKey: 'role',
    header: 'Rol',
    meta: { priority: 'high' },
  },
  {
    accessorKey: 'email',
    header: 'Email',
    meta: { priority: 'medium' },
  },
  {
    accessorKey: 'lastLogin',
    header: 'Último login',
    meta: { priority: 'low' },
  },
]

describe('DataTable', () => {
  describe('rendering', () => {
    it('renders headers and rows from data', () => {
      render(<DataTable columns={columns} data={mockRows} getRowId={(r) => r.id} />)

      expect(screen.getByText('Nombre')).toBeInTheDocument()
      expect(screen.getByText('Rol')).toBeInTheDocument()
      expect(screen.getByText('Juan Pérez')).toBeInTheDocument()
      expect(screen.getByText('Ana García')).toBeInTheDocument()
      expect(screen.getByText('Luis Torres')).toBeInTheDocument()
    })

    it('renders empty state when data is empty', () => {
      render(<DataTable columns={columns} data={[]} emptyMessage="Nada acá" />)

      expect(screen.getByText('Nada acá')).toBeInTheDocument()
    })

    it('renders default empty message when none provided', () => {
      render(<DataTable columns={columns} data={[]} />)

      expect(screen.getByText('Sin resultados.')).toBeInTheDocument()
    })

    it('renders skeleton when isLoading=true', () => {
      const { container } = render(
        <DataTable columns={columns} data={[]} isLoading />,
      )

      // Skeleton uses pulse animation — check presence via class
      const skeletons = container.querySelectorAll('[class*="animate-pulse"]')
      expect(skeletons.length).toBeGreaterThan(0)
      // Empty message should NOT be visible during loading
      expect(screen.queryByText('Sin resultados.')).not.toBeInTheDocument()
    })
  })

  describe('row click', () => {
    it('calls onRowClick with row data when row clicked', async () => {
      const onRowClick = vi.fn()
      render(
        <DataTable
          columns={columns}
          data={mockRows}
          onRowClick={onRowClick}
          getRowId={(r) => r.id}
        />,
      )

      await userEvent.click(screen.getByText('Juan Pérez'))
      expect(onRowClick).toHaveBeenCalledTimes(1)
      expect(onRowClick).toHaveBeenCalledWith(mockRows[0])
    })

    it('does not attach click handler when onRowClick missing', () => {
      const { container } = render(
        <DataTable columns={columns} data={mockRows} getRowId={(r) => r.id} />,
      )

      const rows = container.querySelectorAll('tbody tr')
      // First row should NOT have cursor-pointer
      expect(rows[0]?.className).not.toContain('cursor-pointer')
    })
  })

  describe('priority columns + expand row (mobile pattern)', () => {
    it('renders expand chevron when there are medium/low priority columns', () => {
      render(<DataTable columns={columns} data={mockRows} getRowId={(r) => r.id} />)

      const expandButtons = screen.getAllByLabelText(/expandir fila/i)
      expect(expandButtons).toHaveLength(mockRows.length)
    })

    it('does NOT render expand chevron when all columns are high priority', () => {
      const highOnlyCols: ColumnDef<Row>[] = [
        { accessorKey: 'name', header: 'Nombre', meta: { priority: 'high' } },
        { accessorKey: 'role', header: 'Rol', meta: { priority: 'high' } },
      ]
      render(<DataTable columns={highOnlyCols} data={mockRows} getRowId={(r) => r.id} />)

      expect(screen.queryAllByLabelText(/expandir fila/i)).toHaveLength(0)
    })

    it('expand button reveals hidden columns in panel below', async () => {
      render(<DataTable columns={columns} data={mockRows} getRowId={(r) => r.id} />)

      const firstExpand = screen.getAllByLabelText(/expandir fila/i)[0]
      await userEvent.click(firstExpand)

      // Email and lastLogin (hidden cols) should appear in expanded panel.
      // Email is also rendered in the regular cell (hidden visually but in DOM)
      // — instead check the dl/dt structure:
      const dts = screen.getAllByText('Email')
      expect(dts.length).toBeGreaterThan(0)
      const dtLastLogin = screen.getAllByText('Último login')
      expect(dtLastLogin.length).toBeGreaterThan(0)
    })

    it('expand button toggle changes aria-expanded', async () => {
      render(<DataTable columns={columns} data={mockRows} getRowId={(r) => r.id} />)

      const firstExpand = screen.getAllByLabelText(/expandir fila/i)[0]
      expect(firstExpand).toHaveAttribute('aria-expanded', 'false')

      await userEvent.click(firstExpand)
      // After click, label changes to "Colapsar fila"
      const collapse = screen.getAllByLabelText(/colapsar fila/i)[0]
      expect(collapse).toHaveAttribute('aria-expanded', 'true')
    })

    it('expand button click does NOT trigger onRowClick', async () => {
      const onRowClick = vi.fn()
      render(
        <DataTable
          columns={columns}
          data={mockRows}
          onRowClick={onRowClick}
          getRowId={(r) => r.id}
        />,
      )

      const firstExpand = screen.getAllByLabelText(/expandir fila/i)[0]
      await userEvent.click(firstExpand)

      expect(onRowClick).not.toHaveBeenCalled()
    })

    it('only one row can be expanded at a time', async () => {
      render(<DataTable columns={columns} data={mockRows} getRowId={(r) => r.id} />)

      const expands = screen.getAllByLabelText(/expandir fila/i)
      await userEvent.click(expands[0])
      // Now expands[1] still shows expand label (since only first is expanded)
      const stillExpand = screen.getAllByLabelText(/expandir fila/i)
      expect(stillExpand.length).toBe(2) // 2 rows still collapsed (rows 1 and 2)

      await userEvent.click(stillExpand[0]) // Click row 1's expand
      // Row 0 should be collapsed now, row 1 expanded
      const labels = {
        expand: screen.queryAllByLabelText(/expandir fila/i),
        collapse: screen.queryAllByLabelText(/colapsar fila/i),
      }
      expect(labels.collapse.length).toBe(1)
      expect(labels.expand.length).toBe(2)
    })
  })

  describe('responsive priority CSS classes', () => {
    it('applies hidden md:table-cell to medium priority columns', () => {
      const { container } = render(
        <DataTable columns={columns} data={mockRows} getRowId={(r) => r.id} />,
      )

      // Email column (medium) — find header cell
      const headers = within(container.querySelector('thead')!).getAllByRole(
        'columnheader',
      )
      const emailHeader = headers.find((h) => h.textContent === 'Email')
      expect(emailHeader?.className).toContain('hidden')
      expect(emailHeader?.className).toContain('md:table-cell')
    })

    it('applies hidden lg:table-cell to low priority columns', () => {
      const { container } = render(
        <DataTable columns={columns} data={mockRows} getRowId={(r) => r.id} />,
      )

      const headers = within(container.querySelector('thead')!).getAllByRole(
        'columnheader',
      )
      const lastLoginHeader = headers.find((h) => h.textContent === 'Último login')
      expect(lastLoginHeader?.className).toContain('hidden')
      expect(lastLoginHeader?.className).toContain('lg:table-cell')
    })
  })
})
