import { render, screen, fireEvent } from '@testing-library/react'
import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest'
import { ResponsiveDrawer, resolveOrigins } from './responsive-drawer'
import { ThemeProvider } from '@mui/material'
import { createTheme } from '@mui/material/styles'

// Create a basic theme for testing
const theme = createTheme()

describe('ResponsiveDrawer', () => {
  const mockOnChangeCollapsed = vi.fn()

  // A real anchor element: the desktop Popover only opens when `ref != null`
  // (the open-by-default guard), and positioning needs a DOM node.
  let anchor: HTMLElement

  const defaultProps = {
    collapsed: false,
    onChangeCollapsed: mockOnChangeCollapsed,
    position: 'top-left' as const,
    isMobile: false,
    children: <div data-testid='drawer-content'>Test Content</div>,
  }

  beforeEach(() => {
    mockOnChangeCollapsed.mockReset()
    anchor = document.createElement('div')
    document.body.appendChild(anchor)
  })

  afterEach(() => {
    anchor.remove()
  })

  test('renders in desktop mode when isMobile is false', () => {
    const { container } = render(
      <ThemeProvider theme={theme}>
        <ResponsiveDrawer ref={anchor} {...defaultProps} />
      </ThemeProvider>,
    )

    // Check if content is rendered
    const content = screen.getByTestId('drawer-content')
    expect(content).toBeDefined()

    // Make sure we're not using the mobile Drawer (no presentation role)
    const presentationElements = container.querySelectorAll(
      '[role="presentation"]',
    )
    expect(presentationElements.length).toBe(0)
  })

  test('does not open the desktop popover until the anchor ref resolves', () => {
    // Open-by-default guard: collapsed=false but ref=null → stays closed so the
    // popover never renders with a null anchorEl.
    render(
      <ThemeProvider theme={theme}>
        <ResponsiveDrawer ref={null} {...defaultProps} />
      </ThemeProvider>,
    )
    expect(screen.queryByTestId('drawer-content')).toBeNull()
  })

  test('renders in mobile mode when isMobile is true', () => {
    render(
      <ThemeProvider theme={theme}>
        <ResponsiveDrawer ref={anchor} {...defaultProps} isMobile={true} />
      </ThemeProvider>,
    )

    // Check if content is rendered
    const content = screen.getByTestId('drawer-content')
    expect(content).toBeDefined()

    // Mobile mode uses MUI Drawer which has a 'presentation' role
    const presentationElements = document.querySelectorAll(
      '[role="presentation"]',
    )
    expect(presentationElements.length).toBeGreaterThan(0)
  })

  test('hides content when collapsed is true in desktop mode', () => {
    render(
      <ThemeProvider theme={theme}>
        <ResponsiveDrawer ref={anchor} {...defaultProps} collapsed={true} />
      </ThemeProvider>,
    )

    // When collapsed, the Popover should be closed and content should not be in the document
    const content = screen.queryByTestId('drawer-content')
    expect(content).toBeNull()
  })

  test('closes drawer when clicking outside in mobile mode', () => {
    const { container } = render(
      <ThemeProvider theme={theme}>
        <ResponsiveDrawer ref={anchor} {...defaultProps} isMobile={true} />
      </ThemeProvider>,
    )

    // Find and click the backdrop to close the drawer
    const backdrop = container.querySelector('[role="presentation"] > div')
    expect(backdrop).toBeDefined()
    if (backdrop) {
      fireEvent.click(backdrop)

      // onChangeCollapsed should be called to close the drawer
      expect(mockOnChangeCollapsed).toHaveBeenCalledTimes(1)
      expect(mockOnChangeCollapsed).toHaveBeenCalledWith(true)
    }
  })

  // Each of the four `position` values produces a different
  // anchorOrigin/transformOrigin combination.
  test.each(['top-left', 'top-right', 'bottom-left', 'bottom-right'] as const)(
    'renders in desktop mode with position=%s',
    (position) => {
      expect(() =>
        render(
          <ThemeProvider theme={theme}>
            <ResponsiveDrawer
              ref={anchor}
              {...defaultProps}
              position={position}
            />
          </ThemeProvider>,
        ),
      ).not.toThrow()
    },
  )

  test('modal (default): renders an (invisible) backdrop', () => {
    render(
      <ThemeProvider theme={theme}>
        <ResponsiveDrawer ref={anchor} {...defaultProps} />
      </ThemeProvider>,
    )
    // The popover portals to document.body, so query the whole document.
    expect(document.querySelector('.MuiBackdrop-root')).not.toBeNull()
  })

  test('forwards slotProps to the underlying Popover', () => {
    const slotProps = { paper: { 'data-testid': 'paper-slot' } } as never
    expect(() =>
      render(
        <ThemeProvider theme={theme}>
          <ResponsiveDrawer
            ref={anchor}
            {...defaultProps}
            slotProps={slotProps}
          />
        </ThemeProvider>,
      ),
    ).not.toThrow()
  })

  test('defaults to portaling into document.body when container is omitted', () => {
    render(
      <ThemeProvider theme={theme}>
        <ResponsiveDrawer ref={anchor} {...defaultProps} />
      </ThemeProvider>,
    )
    const content = screen.getByTestId('drawer-content')
    const modalRoot = content.closest('.MuiModal-root')
    expect(modalRoot?.parentElement).toBe(document.body)
  })

  test('portals into the given container instead of document.body', () => {
    const customContainer = document.createElement('div')
    document.body.appendChild(customContainer)
    try {
      render(
        <ThemeProvider theme={theme}>
          <ResponsiveDrawer
            ref={anchor}
            {...defaultProps}
            container={customContainer}
          />
        </ThemeProvider>,
      )
      const content = screen.getByTestId('drawer-content')
      const modalRoot = content.closest('.MuiModal-root')
      expect(modalRoot?.parentElement).toBe(customContainer)
    } finally {
      customContainer.remove()
    }
  })

  test('caps the popover paper height at 90% of a measured container element', () => {
    const customContainer = document.createElement('div')
    Object.defineProperty(customContainer, 'clientHeight', {
      value: 500,
      configurable: true,
    })
    document.body.appendChild(customContainer)
    try {
      render(
        <ThemeProvider theme={theme}>
          <ResponsiveDrawer
            ref={anchor}
            {...defaultProps}
            container={customContainer}
          />
        </ThemeProvider>,
      )
      const paper = document.querySelector<HTMLElement>('.MuiPopover-paper')!
      expect(paper.style.maxHeight).toBe('450px')
      expect(paper.style.overflow).toBe('hidden')
      expect(paper.style.display).toBe('flex')
      expect(paper.style.flexDirection).toBe('column')

      const content = screen.getByTestId('drawer-content')
      const wrapper = content.parentElement!
      expect(wrapper.style.display).toBe('flex')
      expect(wrapper.style.flexDirection).toBe('column')
      expect(wrapper.style.flex).toBe('1 1 0%')
      expect(wrapper.style.minHeight).toBe('0')
      expect(wrapper.style.maxHeight).toBe('100%')
      expect(wrapper.style.overflow).toBe('hidden')
    } finally {
      customContainer.remove()
    }
  })

  test('leaves paper maxHeight unset when no container is given', () => {
    render(
      <ThemeProvider theme={theme}>
        <ResponsiveDrawer ref={anchor} {...defaultProps} />
      </ThemeProvider>,
    )
    const paper = document.querySelector<HTMLElement>('.MuiPopover-paper')!
    expect(paper.style.maxHeight).toBe('')
    expect(paper.style.display).toBe('')

    const content = screen.getByTestId('drawer-content')
    expect(content.parentElement!.style.display).toBe('')
  })

  test('leaves paper maxHeight unset when the container reports height 0', () => {
    const customContainer = document.createElement('div')
    Object.defineProperty(customContainer, 'clientHeight', {
      value: 0,
      configurable: true,
    })
    document.body.appendChild(customContainer)
    try {
      render(
        <ThemeProvider theme={theme}>
          <ResponsiveDrawer
            ref={anchor}
            {...defaultProps}
            container={customContainer}
          />
        </ThemeProvider>,
      )
      const paper = document.querySelector<HTMLElement>('.MuiPopover-paper')!
      expect(paper.style.maxHeight).toBe('')
    } finally {
      customContainer.remove()
    }
  })

  test('preserves slotProps.root when capping paper height under modal=true', () => {
    const customContainer = document.createElement('div')
    Object.defineProperty(customContainer, 'clientHeight', {
      value: 500,
      configurable: true,
    })
    document.body.appendChild(customContainer)
    try {
      render(
        <ThemeProvider theme={theme}>
          <ResponsiveDrawer
            ref={anchor}
            {...defaultProps}
            container={customContainer}
            slotProps={
              {
                root: { 'data-testid': 'drawer-root-slot' },
                paper: { 'data-testid': 'drawer-paper-slot' },
              } as never
            }
          />
        </ThemeProvider>,
      )
      expect(screen.getByTestId('drawer-root-slot')).toBeDefined()
      expect(screen.getByTestId('drawer-paper-slot')).toBeDefined()
      const paper = document.querySelector<HTMLElement>('.MuiPopover-paper')!
      expect(paper.style.maxHeight).toBe('450px')
    } finally {
      customContainer.remove()
    }
  })

  test('respects sx prop on the Popover', () => {
    expect(() =>
      render(
        <ThemeProvider theme={theme}>
          <ResponsiveDrawer
            ref={anchor}
            {...defaultProps}
            sx={{ zIndex: 9999 }}
          />
        </ThemeProvider>,
      ),
    ).not.toThrow()
  })

  // ── Non-modal mode (modal={false}) ─────────────────────────────────────────

  test('non-modal: renders no backdrop', () => {
    render(
      <ThemeProvider theme={theme}>
        <ResponsiveDrawer ref={anchor} {...defaultProps} modal={false} />
      </ThemeProvider>,
    )
    expect(document.querySelector('.MuiBackdrop-root')).toBeNull()
    // Content still renders.
    expect(screen.getByTestId('drawer-content')).toBeDefined()
  })

  test('non-modal: Escape does not close the panel', () => {
    render(
      <ThemeProvider theme={theme}>
        <ResponsiveDrawer ref={anchor} {...defaultProps} modal={false} />
      </ThemeProvider>,
    )
    // Fire on a node inside the Modal root so it reaches the Modal's key handler.
    fireEvent.keyDown(screen.getByTestId('drawer-content'), {
      key: 'Escape',
      code: 'Escape',
    })
    expect(mockOnChangeCollapsed).not.toHaveBeenCalled()
  })

  test('modal (default): Escape closes the panel', () => {
    render(
      <ThemeProvider theme={theme}>
        <ResponsiveDrawer ref={anchor} {...defaultProps} />
      </ThemeProvider>,
    )
    fireEvent.keyDown(screen.getByTestId('drawer-content'), {
      key: 'Escape',
      code: 'Escape',
    })
    expect(mockOnChangeCollapsed).toHaveBeenCalledWith(true)
  })
})

// ── resolveOrigins (pure) ─────────────────────────────────────────────────────

describe('resolveOrigins', () => {
  test('side placement opens beside the trigger (horizontal flip)', () => {
    expect(resolveOrigins('bottom-right', 'side')).toEqual({
      anchorOrigin: { vertical: 'bottom', horizontal: 'left' },
      transformOrigin: { vertical: 'bottom', horizontal: 'right' },
    })
    expect(resolveOrigins('top-left', 'side')).toEqual({
      anchorOrigin: { vertical: 'top', horizontal: 'right' },
      transformOrigin: { vertical: 'top', horizontal: 'left' },
    })
  })

  test('overlay placement pins corner-to-corner (panel sits on top of the anchor)', () => {
    // anchorOrigin === transformOrigin for every corner → the panel lands on the
    // anchor's own corner (it's hidden while open).
    for (const position of [
      'top-left',
      'top-right',
      'bottom-left',
      'bottom-right',
    ] as const) {
      const { anchorOrigin, transformOrigin } = resolveOrigins(
        position,
        'overlay',
      )
      expect(anchorOrigin).toEqual(transformOrigin)
    }
    expect(resolveOrigins('bottom-right', 'overlay').anchorOrigin).toEqual({
      vertical: 'bottom',
      horizontal: 'right',
    })
    expect(resolveOrigins('top-left', 'overlay').anchorOrigin).toEqual({
      vertical: 'top',
      horizontal: 'left',
    })
  })

  test('transformOrigin is independent of placement (panel keeps its own corner)', () => {
    for (const position of [
      'top-left',
      'top-right',
      'bottom-left',
      'bottom-right',
    ] as const) {
      const side = resolveOrigins(position, 'side')
      const overlay = resolveOrigins(position, 'overlay')
      expect(overlay.transformOrigin).toEqual(side.transformOrigin)
    }
  })
})
