import { describe, it, expect, vi, afterEach } from 'vitest'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { SymbolInput } from '../components/SymbolInput'

afterEach(() => vi.clearAllMocks())

describe('SymbolInput — emoji blocking', () => {
  it('typing ASCII chars updates value via onChange', async () => {
    const onChange = vi.fn()
    render(<SymbolInput value="" onChange={onChange} />)

    const input = screen.getByRole('textbox')
    await userEvent.type(input, '$')

    expect(onChange).toHaveBeenCalledWith('$')
  })

  it('typing an emoji does NOT call onChange with emoji content (emoji blocked)', async () => {
    const onChange = vi.fn()
    render(<SymbolInput value="" onChange={onChange} />)

    const input = screen.getByRole('textbox')
    // userEvent.type fires change events for each char; emoji chars may be split into surrogates.
    // The contract: onChange must never be called with a value containing an Extended_Pictographic char.
    await userEvent.type(input, '😀')

    const calls = onChange.mock.calls.map(([v]: [string]) => v)
    const hasEmoji = calls.some((v) => /\p{Extended_Pictographic}/u.test(v))
    expect(hasEmoji).toBe(false)
  })

  it('pasting a string with emoji — onChange NOT called with emoji content', async () => {
    const onChange = vi.fn()
    render(<SymbolInput value="" onChange={onChange} />)

    const input = screen.getByRole('textbox')
    await userEvent.click(input)
    // userEvent.paste triggers onPaste handler with the given text
    await userEvent.paste('😀')

    // onChange must NOT have been called with an emoji
    const calls = onChange.mock.calls.map(([v]: [string]) => v)
    const hasEmoji = calls.some((v) => /\p{Extended_Pictographic}/u.test(v))
    expect(hasEmoji).toBe(false)
  })

  it('pasting normal text (no emoji) allows value update', async () => {
    const onChange = vi.fn()
    render(<SymbolInput value="" onChange={onChange} />)

    const input = screen.getByRole('textbox')
    await userEvent.click(input)
    // Paste normal ASCII — should go through onChange
    await userEvent.paste('$')

    // onChange may be called with '$' or the merged result
    // The key assertion: no rejection for non-emoji
    const calls = onChange.mock.calls.map(([v]: [string]) => v)
    const allNonEmoji = calls.every((v) => !/\p{Extended_Pictographic}/u.test(v))
    expect(allNonEmoji).toBe(true)
  })

  it('value is capped at 4 characters — 5th char is rejected via onChange not called with 5+ chars', async () => {
    // Start with value of 4 chars already set
    const onChange = vi.fn()
    render(<SymbolInput value="$$$$" onChange={onChange} />)

    const input = screen.getByRole('textbox')
    // DOM value is controlled at 4 chars; any additional char should be blocked
    await userEvent.type(input, '$')

    // onChange should NOT be called with a 5-char string
    const calls = onChange.mock.calls.map(([v]: [string]) => v)
    const tooLong = calls.some((v) => v.length > 4)
    expect(tooLong).toBe(false)
  })
})
