import { describe, it, expect, vi } from 'vitest'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { MemoryRouter } from 'react-router-dom'
import { Sidebar } from './Sidebar'
import { ThemeProvider } from '../../providers/ThemeProvider'

// ── Helpers ─────────────────────────────────────────────────────────────────

const LogoIcon = () => <svg data-testid="logo-icon" />

function Wrapper({ children }: { children: React.ReactNode }) {
  return (
    <ThemeProvider>
      <MemoryRouter>{children}</MemoryRouter>
    </ThemeProvider>
  )
}

// ── Tests ────────────────────────────────────────────────────────────────────

describe('Sidebar compound component', () => {
  // 1. renders Header with logo, title, version
  it('renders Header with logo, title, and version', () => {
    render(
      <Wrapper>
        <Sidebar>
          <Sidebar.Header logo={<LogoIcon />} title="MyApp" version="v1.2.3" />
        </Sidebar>
      </Wrapper>,
    )
    expect(screen.getByTestId('logo-icon')).toBeInTheDocument()
    expect(screen.getByText('MyApp')).toBeInTheDocument()
    expect(screen.getByText('v1.2.3')).toBeInTheDocument()
  })

  // 2. hides Header title and version when collapsed
  it('hides Header title and version when sidebar is collapsed', () => {
    render(
      <Wrapper>
        <Sidebar collapsed>
          <Sidebar.Header logo={<LogoIcon />} title="MyApp" version="v1.2.3" />
        </Sidebar>
      </Wrapper>,
    )
    expect(screen.queryByText('MyApp')).not.toBeInTheDocument()
    expect(screen.queryByText('v1.2.3')).not.toBeInTheDocument()
    // Logo should still be visible
    expect(screen.getByTestId('logo-icon')).toBeInTheDocument()
  })

  // 3. renders Section with label and children
  it('renders Section with label and children when open', () => {
    render(
      <Wrapper>
        <Sidebar>
          <Sidebar.Section
            icon={<span>icon</span>}
            label="Settings"
            open
            onToggle={vi.fn()}
          >
            <div>Section Child</div>
          </Sidebar.Section>
        </Sidebar>
      </Wrapper>,
    )
    expect(screen.getByText('Settings')).toBeInTheDocument()
    expect(screen.getByText('Section Child')).toBeInTheDocument()
  })

  // 4. hides Section children when section collapsed (open=false)
  it('hides Section children when section is not open', () => {
    render(
      <Wrapper>
        <Sidebar>
          <Sidebar.Section
            icon={<span>icon</span>}
            label="Settings"
            open={false}
            onToggle={vi.fn()}
          >
            <div>Section Child</div>
          </Sidebar.Section>
        </Sidebar>
      </Wrapper>,
    )
    expect(screen.getByText('Settings')).toBeInTheDocument()
    expect(screen.queryByText('Section Child')).not.toBeInTheDocument()
  })

  // 5. calls onToggle when Section header clicked
  it('calls onToggle when Section chevron button is clicked', async () => {
    const user = userEvent.setup()
    const onToggle = vi.fn()

    render(
      <Wrapper>
        <Sidebar>
          <Sidebar.Section
            icon={<span>icon</span>}
            label="Settings"
            open
            onToggle={onToggle}
          >
            <div>child</div>
          </Sidebar.Section>
        </Sidebar>
      </Wrapper>,
    )

    const btn = screen.getByRole('button', { name: /collapse settings/i })
    await user.click(btn)
    expect(onToggle).toHaveBeenCalledTimes(1)
  })

  // 6. renders collapse toggle and calls onCollapseToggle
  it('renders collapse toggle button and calls onCollapseToggle when clicked', async () => {
    const user = userEvent.setup()
    const onCollapseToggle = vi.fn()

    render(
      <Wrapper>
        <Sidebar onCollapseToggle={onCollapseToggle}>
          <Sidebar.Header logo={<LogoIcon />} title="App" />
        </Sidebar>
      </Wrapper>,
    )

    const toggleBtn = screen.getByRole('button', { name: /collapse sidebar/i })
    await user.click(toggleBtn)
    expect(onCollapseToggle).toHaveBeenCalledTimes(1)
  })

  // 7. renders expand toggle label when collapsed
  it('renders expand toggle when sidebar is collapsed', () => {
    render(
      <Wrapper>
        <Sidebar collapsed onCollapseToggle={vi.fn()}>
          <Sidebar.Header logo={<LogoIcon />} title="App" />
        </Sidebar>
      </Wrapper>,
    )
    expect(screen.getByRole('button', { name: /expand sidebar/i })).toBeInTheDocument()
  })

  // 8. renders search input and calls onSearchChange
  it('renders search input and calls onSearchChange on input', async () => {
    const user = userEvent.setup()
    const onSearchChange = vi.fn()

    render(
      <Wrapper>
        <Sidebar searchValue="" onSearchChange={onSearchChange}>
          <Sidebar.Header logo={<LogoIcon />} title="App" />
        </Sidebar>
      </Wrapper>,
    )

    const input = screen.getByPlaceholderText('Filter...')
    expect(input).toBeInTheDocument()

    await user.type(input, 'hello')
    expect(onSearchChange).toHaveBeenCalled()
    // Each keystroke fires once
    expect(onSearchChange.mock.calls[0][0]).toBe('h')
  })

  // 9. hides search when collapsed
  it('hides search input when sidebar is collapsed', () => {
    render(
      <Wrapper>
        <Sidebar collapsed searchValue="" onSearchChange={vi.fn()}>
          <Sidebar.Header logo={<LogoIcon />} title="App" />
        </Sidebar>
      </Wrapper>,
    )
    expect(screen.queryByPlaceholderText('Filter...')).not.toBeInTheDocument()
  })

  // 10. hides search when onSearchChange not provided
  it('hides search input when onSearchChange is not provided', () => {
    render(
      <Wrapper>
        <Sidebar searchValue="">
          <Sidebar.Header logo={<LogoIcon />} title="App" />
        </Sidebar>
      </Wrapper>,
    )
    expect(screen.queryByPlaceholderText('Filter...')).not.toBeInTheDocument()
  })

  // 11. renders FooterLinks with icons and labels
  it('renders FooterLinks with icon and label', () => {
    render(
      <Wrapper>
        <Sidebar>
          <Sidebar.Footer>
            <Sidebar.FooterLink
              icon={<span data-testid="footer-icon">ic</span>}
              label="Admin"
            />
          </Sidebar.Footer>
        </Sidebar>
      </Wrapper>,
    )
    expect(screen.getByTestId('footer-icon')).toBeInTheDocument()
    expect(screen.getByText('Admin')).toBeInTheDocument()
  })

  // 12. hides FooterLink labels when collapsed and sets title tooltip
  it('hides FooterLink label when collapsed and exposes title tooltip', () => {
    render(
      <Wrapper>
        <Sidebar collapsed>
          <Sidebar.Footer>
            <Sidebar.FooterLink
              icon={<span>ic</span>}
              label="Admin"
            />
          </Sidebar.Footer>
        </Sidebar>
      </Wrapper>,
    )
    expect(screen.queryByText('Admin')).not.toBeInTheDocument()

    // The clickable element should carry a title attribute for tooltip
    // (accessible name comes from icon content when label is hidden)
    const item = screen.getByTitle('Admin')
    expect(item).toHaveAttribute('title', 'Admin')
  })

  // 13. calls FooterLink onClick
  it('calls FooterLink onClick when clicked', async () => {
    const user = userEvent.setup()
    const onClick = vi.fn()

    render(
      <Wrapper>
        <Sidebar>
          <Sidebar.Footer>
            <Sidebar.FooterLink icon={<span>ic</span>} label="Admin" onClick={onClick} />
          </Sidebar.Footer>
        </Sidebar>
      </Wrapper>,
    )

    await user.click(screen.getByText('Admin'))
    expect(onClick).toHaveBeenCalledTimes(1)
  })

  // 14. renders Section as icon-rail item when sidebar collapsed
  it('renders Section as icon-rail item when sidebar is collapsed', () => {
    render(
      <Wrapper>
        <Sidebar collapsed>
          <Sidebar.Section
            icon={<span data-testid="section-icon">ic</span>}
            label="Settings"
            open={false}
            onToggle={vi.fn()}
          >
            <div>child</div>
          </Sidebar.Section>
        </Sidebar>
      </Wrapper>,
    )

    // Label text should not be visible (only as tooltip via title attr)
    expect(screen.queryByText('Settings')).not.toBeInTheDocument()

    // Rail item carries title attribute for tooltip
    // (accessible name comes from icon content when label is hidden)
    const railItem = screen.getByTitle('Settings')
    expect(railItem).toHaveAttribute('title', 'Settings')

    // Icon should still render
    expect(screen.getByTestId('section-icon')).toBeInTheDocument()

    // Section children should not be rendered
    expect(screen.queryByText('child')).not.toBeInTheDocument()
  })

  // 15. fires both onCollapseToggle and onToggle when icon-rail section clicked
  it('fires both onCollapseToggle and onToggle when icon-rail section is clicked', async () => {
    const user = userEvent.setup()
    const onCollapseToggle = vi.fn()
    const onToggle = vi.fn()

    render(
      <Wrapper>
        <Sidebar collapsed onCollapseToggle={onCollapseToggle}>
          <Sidebar.Section
            icon={<span>ic</span>}
            label="Settings"
            open={false}
            onToggle={onToggle}
          >
            <div>child</div>
          </Sidebar.Section>
        </Sidebar>
      </Wrapper>,
    )

    const railItem = screen.getByTitle('Settings')
    await user.click(railItem)

    expect(onCollapseToggle).toHaveBeenCalledTimes(1)
    expect(onToggle).toHaveBeenCalledTimes(1)
  })

  // 16. applies active highlight to FooterLink
  it('applies active highlight class to FooterLink when active', () => {
    render(
      <Wrapper>
        <Sidebar>
          <Sidebar.Footer>
            <Sidebar.FooterLink icon={<span>ic</span>} label="Admin" active />
          </Sidebar.Footer>
        </Sidebar>
      </Wrapper>,
    )

    const item = screen.getByText('Admin').closest('[role="button"]')!
    expect(item.className).toMatch(/bottomItemActive/)
  })
})
