// @vitest-environment jsdom
import { describe, it, expect, vi } from 'vitest'
import { useState } from 'react'
import { render, screen, fireEvent } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import CodeEditor from './CodeEditor.jsx'

// 이 파일이 하는 일: 코드 에디터의 "제어된 입력", 줄번호, Tab 들여쓰기(+커서 위치 버그 수정)를
//   실제 DOM(jsdom)에서 눌러 보며 검증한다.

// CodeEditor는 제어 컴포넌트(value를 부모가 쥔다)라, 실제처럼 상태를 쥔 껍데기로 감싸 테스트한다.
function Harness({ initial = '', minRows }) {
  const [value, setValue] = useState(initial)
  return <CodeEditor value={value} onChange={setValue} minRows={minRows} />
}

describe('CodeEditor', () => {
  it('전달받은 값을 textarea에 보여준다', () => {
    render(<CodeEditor value="print(1)" onChange={() => {}} />)
    expect(screen.getByRole('textbox')).toHaveValue('print(1)')
  })

  it('타이핑하면 부모 상태가 갱신돼 글자가 누적된다(제어 왕복)', async () => {
    const user = userEvent.setup()
    render(<Harness />)
    const textarea = screen.getByRole('textbox')
    await user.type(textarea, 'hello')
    expect(textarea).toHaveValue('hello')
  })

  it('줄번호는 최소 minRows개를 보장한다(빈 값이어도 창이 안 쪼그라듦)', () => {
    const { container } = render(<CodeEditor value="" onChange={() => {}} minRows={5} />)
    const lines = container.querySelectorAll('.code-editor__gutter > div')
    expect(lines).toHaveLength(5)
  })

  it('줄 수가 minRows보다 많으면 줄 수만큼 줄번호가 늘어난다', () => {
    const { container } = render(<CodeEditor value={'a\nb\nc\nd'} onChange={() => {}} minRows={2} />)
    const lines = container.querySelectorAll('.code-editor__gutter > div')
    expect(lines).toHaveLength(4) // 4줄 > minRows 2
  })

  it('Tab을 누르면 브라우저 기본동작 대신 공백 2칸을 커서 위치에 끼워 넣는다', () => {
    const onChange = vi.fn()
    render(<CodeEditor value="ab" onChange={onChange} />)
    const textarea = screen.getByRole('textbox')
    textarea.setSelectionRange(1, 1) // 커서를 a와 b 사이에 둔다
    fireEvent.keyDown(textarea, { key: 'Tab' })
    expect(onChange).toHaveBeenCalledWith('a  b') // a + 공백2 + b
  })

  it('텍스트를 선택한 채 Tab을 누르면 선택 영역이 공백 2칸으로 치환되고 커서가 그 뒤에 온다', async () => {
    // 학습 포인트: 예전엔 커서를 selectionEnd+2로 잘못 계산해, 선택 후 Tab이면 커서가 밀렸다.
    //   올바른 값은 selectionStart+2. 이 회귀를 못 박는 테스트다.
    render(<Harness initial="abcd" />)
    const textarea = screen.getByRole('textbox')
    textarea.setSelectionRange(1, 3) // 'bc'를 선택
    fireEvent.keyDown(textarea, { key: 'Tab' })
    expect(textarea).toHaveValue('a  d') // 선택('bc')이 공백 2칸으로 치환

    await new Promise((resolve) => requestAnimationFrame(resolve)) // 커서 이동은 다음 프레임에
    expect(textarea.selectionStart).toBe(3) // selectionStart(1) + 2  — 버그판이면 5였다
    expect(textarea.selectionEnd).toBe(3)
  })
})
