import '@testing-library/jest-dom'

import { fireEvent, render } from '@testing-library/react'
import { axe, toHaveNoViolations } from 'jest-axe'
import { vi } from 'vitest'

import { PktTabItem, PktTabs } from './Tabs'

expect.extend(toHaveNoViolations)

describe('Tabs component', () => {
  const tabs = [
    { text: 'Tab 1', href: '#', action: vi.fn() },
    { text: 'Tab 2', action: vi.fn() },
  ]

  it('renders tabs with links and buttons', () => {
    const { getByText } = render(<PktTabs tabs={tabs} />)
    tabs.forEach((tab) => {
      const tabElement = getByText(tab.text)
      expect(tabElement).toBeInTheDocument()
      if (tab.href) {
        expect(tabElement.tagName).toBe('A')
      } else {
        expect(tabElement.tagName).toBe('BUTTON')
      }
    })
  })

  it('calls action function when a tab is clicked', () => {
    const { getByText } = render(<PktTabs tabs={tabs} />)
    tabs.forEach((tab, index) => {
      const tabElement = getByText(tab.text)
      fireEvent.click(tabElement)
      expect(tab.action).toHaveBeenCalledWith(index)
    })
  })

  it('calls onTabSelected prop when a tab is clicked', () => {
    const onTabSelected = vi.fn()
    const { getByText } = render(<PktTabs tabs={tabs} onTabSelected={onTabSelected} />)
    tabs.forEach((tab, index) => {
      const tabElement = getByText(tab.text)
      fireEvent.click(tabElement)
      expect(onTabSelected).toHaveBeenCalledWith(index)
    })
  })
  describe('accessibility', () => {
    it('renders with no wcag errors with axe', async () => {
      const tabs = [
        { text: 'Tab 1', href: '/page1', action: vi.fn() },
        { text: 'Tab 2', action: vi.fn() },
      ]
      const { container } = render(<PktTabs tabs={tabs} />)
      const results = await axe(container)
      expect(results).toHaveNoViolations()
    })
  })
})

describe('PktTabItem children format', () => {
  it('renders tabs from children', () => {
    const { getByText } = render(
      <PktTabs>
        <PktTabItem index={0}>First Tab</PktTabItem>
        <PktTabItem index={1}>Second Tab</PktTabItem>
        <PktTabItem index={2}>Third Tab</PktTabItem>
      </PktTabs>,
    )

    expect(getByText('First Tab')).toBeInTheDocument()
    expect(getByText('Second Tab')).toBeInTheDocument()
    expect(getByText('Third Tab')).toBeInTheDocument()
  })

  it('applies active class to the active tab', () => {
    const { getByText } = render(
      <PktTabs>
        <PktTabItem index={0}>First Tab</PktTabItem>
        <PktTabItem index={1} active>
          Second Tab
        </PktTabItem>
        <PktTabItem index={2}>Third Tab</PktTabItem>
      </PktTabs>,
    )

    expect(getByText('Second Tab')).toHaveClass('active')
    expect(getByText('First Tab')).not.toHaveClass('active')
    expect(getByText('Third Tab')).not.toHaveClass('active')
  })

  it('calls onClick handler when tab is clicked', () => {
    const handleClick = vi.fn()
    const { getByText } = render(
      <PktTabs>
        <PktTabItem index={0}>First Tab</PktTabItem>
        <PktTabItem index={1} onClick={handleClick}>
          Second Tab
        </PktTabItem>
        <PktTabItem index={2}>Third Tab</PktTabItem>
      </PktTabs>,
    )

    fireEvent.click(getByText('Second Tab'))
    expect(handleClick).toHaveBeenCalled()
    expect(handleClick.mock.calls[0][0]).toHaveProperty('type', 'click')
  })

  it('calls onTabSelected when tab is clicked', () => {
    const handleTabSelected = vi.fn()
    const { getByText } = render(
      <PktTabs onTabSelected={handleTabSelected}>
        <PktTabItem index={0}>First Tab</PktTabItem>
        <PktTabItem index={1}>Second Tab</PktTabItem>
        <PktTabItem index={2}>Third Tab</PktTabItem>
      </PktTabs>,
    )

    fireEvent.click(getByText('Second Tab'))
    expect(handleTabSelected).toHaveBeenCalledWith(1)
  })

  it('calls both onClick and onTabSelected when tab is clicked', () => {
    const handleClick = vi.fn()
    const handleTabSelected = vi.fn()
    const { getByText } = render(
      <PktTabs onTabSelected={handleTabSelected}>
        <PktTabItem index={0}>First Tab</PktTabItem>
        <PktTabItem index={1} onClick={handleClick}>
          Second Tab
        </PktTabItem>
        <PktTabItem index={2}>Third Tab</PktTabItem>
      </PktTabs>,
    )

    fireEvent.click(getByText('Second Tab'))
    expect(handleClick).toHaveBeenCalled()
    expect(handleTabSelected).toHaveBeenCalledWith(1)
  })

  it('renders as button when no href is provided', () => {
    const { getByText } = render(
      <PktTabs>
        <PktTabItem index={0}>First Tab</PktTabItem>
      </PktTabs>,
    )

    const tab = getByText('First Tab')
    expect(tab.tagName).toBe('BUTTON')
  })

  it('renders as link when href is provided', () => {
    const { getByText } = render(
      <PktTabs>
        <PktTabItem index={0} href="/first">
          First Tab
        </PktTabItem>
      </PktTabs>,
    )

    const tab = getByText('First Tab')
    expect(tab.tagName).toBe('A')
    expect(tab).toHaveAttribute('href', '/first')
  })

  it('renders icon when icon prop is provided', () => {
    const { container } = render(
      <PktTabs>
        <PktTabItem index={0} icon="user">
          First Tab
        </PktTabItem>
      </PktTabs>,
    )

    const button = container.querySelector('.pkt-tabs__button')
    expect(button).toBeInTheDocument()
    const iconElement = container.querySelector('.pkt-icon--small')
    expect(iconElement).toBeInTheDocument()
  })

  it('renders tag when tag and tagSkin props are provided', () => {
    const { getByText } = render(
      <PktTabs>
        <PktTabItem index={0} tag="New" tagSkin="blue">
          First Tab
        </PktTabItem>
      </PktTabs>,
    )

    expect(getByText('New')).toBeInTheDocument()
  })

  it('handles keyboard navigation with ArrowRight', () => {
    const { getByText } = render(
      <PktTabs>
        <PktTabItem index={0} active>
          First Tab
        </PktTabItem>
        <PktTabItem index={1}>Second Tab</PktTabItem>
        <PktTabItem index={2}>Third Tab</PktTabItem>
      </PktTabs>,
    )

    const firstTab = getByText('First Tab')
    const secondTab = getByText('Second Tab')

    firstTab.focus()
    fireEvent.keyDown(firstTab, { key: 'ArrowRight' })

    expect(secondTab).toHaveFocus()
  })

  it('handles keyboard navigation with ArrowLeft', () => {
    const { getByText } = render(
      <PktTabs>
        <PktTabItem index={0}>First Tab</PktTabItem>
        <PktTabItem index={1} active>
          Second Tab
        </PktTabItem>
        <PktTabItem index={2}>Third Tab</PktTabItem>
      </PktTabs>,
    )

    const firstTab = getByText('First Tab')
    const secondTab = getByText('Second Tab')

    secondTab.focus()
    fireEvent.keyDown(secondTab, { key: 'ArrowLeft' })

    expect(firstTab).toHaveFocus()
  })

  it('stays on last tab when navigating past it with ArrowRight', () => {
    const { getByText } = render(
      <PktTabs>
        <PktTabItem index={0}>First Tab</PktTabItem>
        <PktTabItem index={1}>Second Tab</PktTabItem>
        <PktTabItem index={2} active>
          Third Tab
        </PktTabItem>
      </PktTabs>,
    )

    const thirdTab = getByText('Third Tab')

    thirdTab.focus()
    fireEvent.keyDown(thirdTab, { key: 'ArrowRight' })

    expect(thirdTab).toHaveFocus()
  })

  it('stays on first tab when navigating before it with ArrowLeft', () => {
    const { getByText } = render(
      <PktTabs>
        <PktTabItem index={0} active>
          First Tab
        </PktTabItem>
        <PktTabItem index={1}>Second Tab</PktTabItem>
        <PktTabItem index={2}>Third Tab</PktTabItem>
      </PktTabs>,
    )

    const firstTab = getByText('First Tab')

    firstTab.focus()
    fireEvent.keyDown(firstTab, { key: 'ArrowLeft' })

    expect(firstTab).toHaveFocus()
  })

  it('selects tab when Space key is pressed', () => {
    const handleTabSelected = vi.fn()
    const { getByText } = render(
      <PktTabs onTabSelected={handleTabSelected}>
        <PktTabItem index={0}>First Tab</PktTabItem>
        <PktTabItem index={1}>Second Tab</PktTabItem>
      </PktTabs>,
    )

    const secondTab = getByText('Second Tab')
    fireEvent.keyDown(secondTab, { key: ' ' })

    expect(handleTabSelected).toHaveBeenCalledWith(1)
  })

  it('selects tab when ArrowDown key is pressed', () => {
    const handleTabSelected = vi.fn()
    const { getByText } = render(
      <PktTabs onTabSelected={handleTabSelected}>
        <PktTabItem index={0}>First Tab</PktTabItem>
        <PktTabItem index={1}>Second Tab</PktTabItem>
      </PktTabs>,
    )

    const secondTab = getByText('Second Tab')
    fireEvent.keyDown(secondTab, { key: 'ArrowDown' })

    expect(handleTabSelected).toHaveBeenCalledWith(1)
  })

  it('disables keyboard navigation when arrowNav is false', () => {
    const { getByText } = render(
      <PktTabs arrowNav={false}>
        <PktTabItem index={0} active>
          First Tab
        </PktTabItem>
        <PktTabItem index={1}>Second Tab</PktTabItem>
      </PktTabs>,
    )

    const firstTab = getByText('First Tab')
    const secondTab = getByText('Second Tab')

    firstTab.focus()
    fireEvent.keyDown(firstTab, { key: 'ArrowRight' })

    expect(secondTab).not.toHaveFocus()
  })

  it('disables keyboard navigation when disableArrowNav is true', () => {
    const { getByText } = render(
      <PktTabs disableArrowNav={true}>
        <PktTabItem index={0} active>
          First Tab
        </PktTabItem>
        <PktTabItem index={1}>Second Tab</PktTabItem>
      </PktTabs>,
    )

    const firstTab = getByText('First Tab')
    const secondTab = getByText('Second Tab')

    firstTab.focus()
    fireEvent.keyDown(firstTab, { key: 'ArrowRight' })

    expect(secondTab).not.toHaveFocus()
  })

  it('disableArrowNav overrides arrowNav when both are set', () => {
    const { getByText } = render(
      <PktTabs arrowNav={true} disableArrowNav={true}>
        <PktTabItem index={0} active>
          First Tab
        </PktTabItem>
        <PktTabItem index={1}>Second Tab</PktTabItem>
      </PktTabs>,
    )

    const firstTab = getByText('First Tab')
    const secondTab = getByText('Second Tab')

    // Even though arrowNav is true, disableArrowNav should override it
    firstTab.focus()
    fireEvent.keyDown(firstTab, { key: 'ArrowRight' })

    expect(secondTab).not.toHaveFocus()
  })

  it('works with mapped children when explicit index is provided', () => {
    const items = ['First', 'Second', 'Third']
    const handleTabSelected = vi.fn()

    const { getByText } = render(
      <PktTabs onTabSelected={handleTabSelected}>
        {items.map((item, i) => (
          <PktTabItem key={i} index={i}>
            {item}
          </PktTabItem>
        ))}
      </PktTabs>,
    )

    fireEvent.click(getByText('Second'))
    expect(handleTabSelected).toHaveBeenCalledWith(1)

    fireEvent.click(getByText('Third'))
    expect(handleTabSelected).toHaveBeenCalledWith(2)
  })

  describe('Disabled tabs', () => {
    it('does not call onTabSelected or action for disabled button tab from tabs prop', () => {
      const onTabSelected = vi.fn()
      const action = vi.fn()
      const { getByText } = render(
        <PktTabs
          onTabSelected={onTabSelected}
          tabs={[
            { text: 'Enabled', action: vi.fn() },
            { text: 'Disabled', action, disabled: true },
          ]}
        />,
      )

      fireEvent.click(getByText('Disabled'))

      expect(action).not.toHaveBeenCalled()
      expect(onTabSelected).not.toHaveBeenCalled()
    })

    it('renders disabled button tab as native disabled', () => {
      const { getByText } = render(
        <PktTabs>
          <PktTabItem index={0} disabled>
            Disabled button
          </PktTabItem>
        </PktTabs>,
      )

      expect(getByText('Disabled button')).toBeDisabled()
    })

    it('renders disabled link tab with aria-disabled and blocks interaction', () => {
      const handleTabSelected = vi.fn()
      const handleClick = vi.fn()
      const { getByText } = render(
        <PktTabs onTabSelected={handleTabSelected}>
          <PktTabItem index={0} href="/enabled">
            Enabled link
          </PktTabItem>
          <PktTabItem index={1} href="/disabled" disabled onClick={handleClick}>
            Disabled link
          </PktTabItem>
        </PktTabs>,
      )

      const disabledLink = getByText('Disabled link')
      fireEvent.click(disabledLink)
      fireEvent.keyDown(disabledLink, { key: 'Enter' })

      expect(disabledLink).toHaveAttribute('aria-disabled', 'true')
      expect(disabledLink).toHaveAttribute('tabindex', '-1')
      expect(disabledLink).not.toHaveAttribute('href')
      expect(handleClick).not.toHaveBeenCalled()
      expect(handleTabSelected).not.toHaveBeenCalled()
    })

    it('skips disabled tabs during arrow navigation', () => {
      const { getByText } = render(
        <PktTabs>
          <PktTabItem index={0} active>
            First
          </PktTabItem>
          <PktTabItem index={1} disabled>
            Disabled
          </PktTabItem>
          <PktTabItem index={2}>Third</PktTabItem>
        </PktTabs>,
      )

      const first = getByText('First')
      const third = getByText('Third')

      first.focus()
      fireEvent.keyDown(first, { key: 'ArrowRight' })

      expect(third).toHaveFocus()
    })

    it('prevents disabled active tab from being selectable', () => {
      const handleTabSelected = vi.fn()
      const { getByText } = render(
        <PktTabs onTabSelected={handleTabSelected}>
          <PktTabItem index={0} active disabled>
            Disabled active
          </PktTabItem>
          <PktTabItem index={1}>Enabled</PktTabItem>
        </PktTabs>,
      )

      const disabledTab = getByText('Disabled active')
      expect(disabledTab).toHaveAttribute('aria-selected', 'false')

      fireEvent.keyDown(disabledTab, { key: 'Enter' })
      expect(handleTabSelected).not.toHaveBeenCalled()
    })
  })

  describe('Accessibility', () => {
    it('should not have any accessibility violations', async () => {
      const { container } = render(
        <PktTabs>
          <PktTabItem index={0} active>
            First Tab
          </PktTabItem>
          <PktTabItem index={1}>Second Tab</PktTabItem>
          <PktTabItem index={2}>Third Tab</PktTabItem>
        </PktTabs>,
      )
      const results = await axe(container)
      expect(results).toHaveNoViolations()
    })

    it('sets correct ARIA attributes on buttons', () => {
      const { getByText } = render(
        <PktTabs>
          <PktTabItem index={0} active>
            First Tab
          </PktTabItem>
          <PktTabItem index={1}>Second Tab</PktTabItem>
        </PktTabs>,
      )

      const firstTab = getByText('First Tab')
      const secondTab = getByText('Second Tab')

      expect(firstTab).toHaveAttribute('role', 'tab')
      expect(firstTab).toHaveAttribute('aria-selected', 'true')
      expect(secondTab).toHaveAttribute('role', 'tab')
      expect(secondTab).toHaveAttribute('aria-selected', 'false')
    })

    it('does not set tab role or aria-selected on links when arrowNav is false', () => {
      const { getByText } = render(
        <PktTabs arrowNav={false}>
          <PktTabItem index={0} href="/" active>
            Home
          </PktTabItem>
          <PktTabItem index={1} href="/about">
            About
          </PktTabItem>
        </PktTabs>,
      )

      const homeLink = getByText('Home')
      const aboutLink = getByText('About')

      expect(homeLink).not.toHaveAttribute('role')
      expect(homeLink).not.toHaveAttribute('aria-selected')
      expect(aboutLink).not.toHaveAttribute('role')
      expect(aboutLink).not.toHaveAttribute('aria-selected')
    })

    it('works with href links when arrowNav is false (no WCAG violations)', async () => {
      const { container } = render(
        <PktTabs arrowNav={false}>
          <PktTabItem index={0} href="/" active>
            Home
          </PktTabItem>
          <PktTabItem index={1} href="/about">
            About
          </PktTabItem>
        </PktTabs>,
      )

      const results = await axe(container)
      expect(results).toHaveNoViolations()
    })
  })
})
