import { describe, it, expect, vi } from 'vitest'
import { screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { renderWithI18n } from '@/test/render'
import { PropertyEditor } from './PropertyEditor'
import type { PropertyDef } from './types'

const baseProp = (overrides: Partial<PropertyDef> = {}): PropertyDef => ({
  id: 'prop-1',
  name: 'designator',
  kind: 'string',
  required: false,
  unique: false,
  ...overrides,
})

describe('PropertyEditor', () => {
  it('меняет name при вводе → onChange с обновлённым name', async () => {
    const user = userEvent.setup()
    const onChange = vi.fn()
    renderWithI18n(
      <PropertyEditor
        prop={baseProp({ name: '' })}
        index={0}
        total={1}
        onChange={onChange}
        onRemove={() => {}}
        onMoveUp={() => {}}
        onMoveDown={() => {}}
      />,
    )
    // @nstart/ui TextInput не биндит label через for/id — ищем по placeholder.
    const nameInput = screen.getByPlaceholderText('snake_case_key') as HTMLInputElement
    await user.type(nameInput, 'n')
    // Controlled — value не обновляется без re-render с новым prop, поэтому
    // достаточно проверить что onChange выстрелил с ожидаемым name.
    expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ name: 'n' }))
  })

  it('toggle required чекбокса → onChange c required:true', async () => {
    const user = userEvent.setup()
    const onChange = vi.fn()
    renderWithI18n(
      <PropertyEditor
        prop={baseProp({ required: false })}
        index={0}
        total={1}
        onChange={onChange}
        onRemove={() => {}}
        onMoveUp={() => {}}
        onMoveDown={() => {}}
      />,
    )
    await user.click(screen.getByLabelText('Обязательное'))
    expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ required: true }))
  })

  it('toggle unique → onChange c unique:true', async () => {
    const user = userEvent.setup()
    const onChange = vi.fn()
    renderWithI18n(
      <PropertyEditor
        prop={baseProp({ unique: false })}
        index={0}
        total={1}
        onChange={onChange}
        onRemove={() => {}}
        onMoveUp={() => {}}
        onMoveDown={() => {}}
      />,
    )
    // У Checkbox label включает description "Дубликаты по этому полю запрещены"
    // — getByLabelText использует match на полный текст label элемента.
    await user.click(screen.getByLabelText(/Уникальное/))
    expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ unique: true }))
  })

  it('для kind=string показывает minLength/maxLength/pattern', () => {
    renderWithI18n(
      <PropertyEditor
        prop={baseProp({ kind: 'string' })}
        index={0}
        total={1}
        onChange={() => {}}
        onRemove={() => {}}
        onMoveUp={() => {}}
        onMoveDown={() => {}}
      />,
    )
    // @nstart/ui TextInput не биндит label через for/id — проверяем по
    // visible-тексту labels.
    expect(screen.getByText('minLength')).toBeInTheDocument()
    expect(screen.getByText('maxLength')).toBeInTheDocument()
    expect(screen.getByText('Pattern (regex)')).toBeInTheDocument()
    // Pattern имеет уникальный placeholder — проверяем что input реально есть.
    expect(screen.getByPlaceholderText('^[A-Z]{2}$')).toBeInTheDocument()
  })

  it('для kind=integer показывает minimum/maximum (не string-поля)', () => {
    renderWithI18n(
      <PropertyEditor
        prop={baseProp({ kind: 'integer' })}
        index={0}
        total={1}
        onChange={() => {}}
        onRemove={() => {}}
        onMoveUp={() => {}}
        onMoveDown={() => {}}
      />,
    )
    expect(screen.getByText('minimum')).toBeInTheDocument()
    expect(screen.getByText('maximum')).toBeInTheDocument()
    expect(screen.queryByText('Pattern (regex)')).not.toBeInTheDocument()
  })

  it('для kind=enum показывает enum values input с pre-filled значением', () => {
    renderWithI18n(
      <PropertyEditor
        prop={baseProp({ kind: 'enum', enumValues: ['A', 'B'] })}
        index={0}
        total={1}
        onChange={() => {}}
        onRemove={() => {}}
        onMoveUp={() => {}}
        onMoveDown={() => {}}
      />,
    )
    expect(screen.getByText('Значения enum (через запятую)')).toBeInTheDocument()
    expect(screen.getByDisplayValue('A, B')).toBeInTheDocument()
  })

  it('для kind=array_string появляется uniqueItems checkbox', () => {
    renderWithI18n(
      <PropertyEditor
        prop={baseProp({ kind: 'array_string' })}
        index={0}
        total={1}
        onChange={() => {}}
        onRemove={() => {}}
        onMoveUp={() => {}}
        onMoveDown={() => {}}
      />,
    )
    expect(screen.getByLabelText('uniqueItems')).toBeInTheDocument()
  })

  it('moveUp disabled когда index=0', () => {
    renderWithI18n(
      <PropertyEditor
        prop={baseProp()}
        index={0}
        total={3}
        onChange={() => {}}
        onRemove={() => {}}
        onMoveUp={() => {}}
        onMoveDown={() => {}}
      />,
    )
    expect(screen.getByRole('button', { name: 'Выше' })).toBeDisabled()
  })

  it('moveDown disabled когда index=total-1', () => {
    renderWithI18n(
      <PropertyEditor
        prop={baseProp()}
        index={2}
        total={3}
        onChange={() => {}}
        onRemove={() => {}}
        onMoveUp={() => {}}
        onMoveDown={() => {}}
      />,
    )
    expect(screen.getByRole('button', { name: 'Ниже' })).toBeDisabled()
  })

  it('клик Удалить → onRemove', async () => {
    const user = userEvent.setup()
    const onRemove = vi.fn()
    renderWithI18n(
      <PropertyEditor
        prop={baseProp()}
        index={1}
        total={3}
        onChange={() => {}}
        onRemove={onRemove}
        onMoveUp={() => {}}
        onMoveDown={() => {}}
      />,
    )
    await user.click(screen.getByRole('button', { name: 'Удалить' }))
    expect(onRemove).toHaveBeenCalledTimes(1)
  })
})
