import { fireEvent, render, screen, within } from '@testing-library/react'
import { act } from 'react'
import { useState } from 'react'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { DetailDrawer } from './DetailDrawer'

function renderDrawer(onClose = vi.fn(), body?: React.ReactNode) {
  render(
    <div>
      <button type="button">Open trigger</button>
      <div data-testid="outside">Outside</div>
      <DetailDrawer eyebrow="Fixture detail" title="t1 · segment" titleId="fixture-detail-title" onClose={onClose}>
        {body ?? <p>Fixture body content</p>}
      </DetailDrawer>
    </div>,
  )
  return onClose
}

/** Movable drawer with a fixed geometry, so a move assertion is arithmetic rather than layout. */
function renderDraggable(): { dialog: HTMLElement; handle: HTMLElement } {
  render(
    <DetailDrawer draggable eyebrow="Fixture detail" title="t1 · segment" titleId="fixture-detail-title" onClose={vi.fn()}>
      <p>Fixture body content</p>
    </DetailDrawer>,
  )
  const dialog = screen.getByRole('dialog')
  Object.defineProperty(dialog, 'getBoundingClientRect', {
    value: () => ({ left: 100, top: 80, width: 448, height: 320 }),
  })
  return { dialog, handle: screen.getByRole('button', { name: 'Drag window' }) }
}

describe('DetailDrawer', () => {
  afterEach(() => window.history.replaceState({}, '', '/'))

  it('renders the dialog shell, header, body slot, and close control', () => {
    const onClose = vi.fn()
    render(
      <DetailDrawer eyebrow="Fixture detail" title="t1 · segment" titleId="fixture-detail-title" onClose={onClose}>
        <p>Fixture body content</p>
      </DetailDrawer>,
    )

    expect(screen.getByRole('dialog')).toHaveAttribute('aria-labelledby', 'fixture-detail-title')
    expect(screen.getByRole('dialog')).toHaveAttribute('aria-modal', 'true')
    expect(screen.getByText('Fixture detail')).toBeInTheDocument()
    expect(screen.getByRole('heading', { name: 't1 · segment' })).toHaveAttribute('id', 'fixture-detail-title')
    expect(screen.getByText('Fixture body content')).toBeInTheDocument()

    fireEvent.click(within(screen.getByRole('dialog')).getByRole('button', { name: 'Close' }))
    expect(onClose).toHaveBeenCalledTimes(1)
  })

  it('closes when Escape is pressed', () => {
    const onClose = renderDrawer()
    fireEvent.keyDown(screen.getByRole('dialog'), { key: 'Escape' })
    expect(onClose).toHaveBeenCalledTimes(1)
  })

  it('moves focus into the drawer when it opens', () => {
    const trigger = document.createElement('button')
    trigger.type = 'button'
    trigger.textContent = 'Open trigger'
    document.body.append(trigger)
    try {
      trigger.focus()

      render(
        <DetailDrawer eyebrow="Fixture detail" title="t1 · segment" titleId="fixture-detail-title" onClose={vi.fn()}>
          <p>Fixture body content</p>
        </DetailDrawer>,
      )

      const dialog = screen.getByRole('dialog')
      expect(dialog.contains(document.activeElement)).toBe(true)
      expect(within(dialog).getByRole('button', { name: 'Close' })).toHaveFocus()
    } finally {
      trigger.remove()
    }
  })

  it('restores focus to the trigger element when the drawer closes', () => {
    function Harness() {
      const [open, setOpen] = useState(false)
      return (
        <>
          <button type="button" onClick={() => setOpen(true)}>
            Open trigger
          </button>
          {open ? (
            <DetailDrawer
              eyebrow="Fixture detail"
              title="t1 · segment"
              titleId="fixture-detail-title"
              onClose={() => setOpen(false)}
            >
              <p>Fixture body content</p>
            </DetailDrawer>
          ) : null}
        </>
      )
    }

    render(<Harness />)
    const trigger = screen.getByRole('button', { name: 'Open trigger' })
    trigger.focus()
    fireEvent.click(trigger)

    fireEvent.keyDown(screen.getByRole('dialog'), { key: 'Escape' })
    expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
    expect(trigger).toHaveFocus()
  })

  it('does not steal focus back to the close control when the parent re-renders with a new onClose identity', () => {
    const drawer = (onClose: () => void) => (
      <DetailDrawer eyebrow="Fixture detail" title="t1 · segment" titleId="fixture-detail-title" onClose={onClose}>
        <button type="button">Body action</button>
      </DetailDrawer>
    )

    const view = render(drawer(() => {}))
    const bodyAction = screen.getByRole('button', { name: 'Body action' })
    bodyAction.focus()
    expect(bodyAction).toHaveFocus()

    view.rerender(drawer(() => {}))

    expect(bodyAction).toHaveFocus()
  })

  it('closes from the backdrop without clicking through to content behind the drawer', () => {
    const onClose = vi.fn()
    const onUnderlyingClick = vi.fn()
    render(
      <div>
        <button type="button" onClick={onUnderlyingClick}>Underlying action</button>
        <DetailDrawer eyebrow="Fixture detail" title="t1 · segment" titleId="fixture-detail-title" onClose={onClose}>
          <p>Fixture body content</p>
        </DetailDrawer>
      </div>,
    )

    fireEvent.click(screen.getByRole('dialog'))
    expect(onClose).toHaveBeenCalledTimes(1)
    expect(onUnderlyingClick).not.toHaveBeenCalled()
  })

  it('does not close when a pointerdown occurs inside the drawer', () => {
    const onClose = renderDrawer()
    fireEvent.pointerDown(screen.getByText('Fixture body content'))
    expect(onClose).not.toHaveBeenCalled()
  })

  it('moves when its drag handle is used', () => {
    const { dialog, handle } = renderDraggable()

    fireEvent.pointerDown(handle, { pointerId: 1, clientX: 120, clientY: 100 })
    fireEvent.pointerMove(handle, { pointerId: 1, buttons: 1, clientX: 220, clientY: 180 })
    fireEvent.pointerUp(handle, { pointerId: 1 })

    expect(dialog).toHaveStyle({ transform: 'translate(100px, 80px)' })
  })

  it('moves by a fixed step when the drag handle is nudged with an arrow key', () => {
    const { dialog, handle } = renderDraggable()

    fireEvent.keyDown(handle, { key: 'ArrowLeft' })
    expect(dialog).toHaveStyle({ transform: 'translate(-16px, 0px)' })

    fireEvent.keyDown(handle, { key: 'ArrowDown' })
    expect(dialog).toHaveStyle({ transform: 'translate(-16px, 16px)' })
  })

  it('leaves navigation available when used as a persistent workspace', () => {
    const onClose = vi.fn()
    render(
      <DetailDrawer draggable modal={false} eyebrow="Fixture detail" title="t1 · segment" titleId="fixture-detail-title" onClose={onClose}>
        <p>Fixture body content</p>
      </DetailDrawer>,
    )

    const dialog = screen.getByRole('dialog')
    expect(dialog).not.toHaveAttribute('aria-modal')
    expect(dialog.contains(document.activeElement)).toBe(true)

    // A persistent window must not swallow Escape for the page it floats over.
    fireEvent.keyDown(document.body, { key: 'Escape' })
    expect(onClose).not.toHaveBeenCalled()

    fireEvent.keyDown(screen.getByRole('dialog'), { key: 'Escape' })
    expect(onClose).toHaveBeenCalledTimes(1)
  })
})