// @vitest-environment happy-dom
import { useState } from 'react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'

/**
 * TagInput — chip editor with autocomplete from the collection's in-use
 * vocabulary (`GET /collections/:slug/field-values`). Pins the dedupe
 * contract: adding "ai" when "AI" already exists in the collection resolves
 * to the canonical "AI", and tags already on the post are never duplicated.
 */

let fieldValues: Array<{ value: string; count: number }> = []

const cmsApi = vi.fn(async (endpoint: string, _options?: unknown) => {
  if (endpoint.startsWith('/collections/posts/field-values')) {
    return { data: { field: 'tags', values: fieldValues }, status: 200 }
  }
  return { data: {}, status: 200 }
})

vi.mock('../../lib/api.js', () => ({
  cmsApi: (e: string, o?: unknown) => cmsApi(e, o as never),
  ensureCsrfToken: vi.fn(async () => {}),
}))

const { TagInput } = await import('../../components/TagInput.js')

function Harness({ initial = [] as string[], maxTags }: { initial?: string[]; maxTags?: number }) {
  const [tags, setTags] = useState(initial)
  return (
    <div>
      <TagInput id="tags" value={tags} onChange={setTags} collection="posts" maxTags={maxTags} />
      <output data-testid="value">{JSON.stringify(tags)}</output>
    </div>
  )
}

function currentValue(): string[] {
  return JSON.parse(screen.getByTestId('value').textContent ?? '[]')
}

beforeEach(() => {
  cmsApi.mockClear()
  fieldValues = []
})

describe('TagInput', () => {
  it('adds a tag on Enter and clears the draft', () => {
    render(<Harness />)
    const input = screen.getByRole('combobox')
    fireEvent.change(input, { target: { value: 'Design' } })
    fireEvent.keyDown(input, { key: 'Enter' })
    expect(currentValue()).toEqual(['Design'])
    expect((input as HTMLInputElement).value).toBe('')
  })

  it('dedupes case-insensitively against tags already on the post', () => {
    render(<Harness initial={['AI']} />)
    const input = screen.getByRole('combobox')
    fireEvent.change(input, { target: { value: 'ai' } })
    fireEvent.keyDown(input, { key: 'Enter' })
    expect(currentValue()).toEqual(['AI'])
  })

  it('resolves new tags to the canonical casing already used in the collection', async () => {
    fieldValues = [{ value: 'AI', count: 7 }]
    render(<Harness />)
    const input = screen.getByRole('combobox')
    fireEvent.focus(input)
    await waitFor(() => expect(cmsApi).toHaveBeenCalled())
    fireEvent.change(input, { target: { value: 'ai' } })
    fireEvent.keyDown(input, { key: 'Enter' })
    expect(currentValue()).toEqual(['AI'])
  })

  it('shows suggestions on focus and adds one on click', async () => {
    fieldValues = [
      { value: 'Design', count: 3 },
      { value: 'DevOps', count: 1 },
    ]
    render(<Harness />)
    fireEvent.focus(screen.getByRole('combobox'))
    const option = await screen.findByText('Design')
    fireEvent.click(option)
    expect(currentValue()).toEqual(['Design'])
    // An added tag drops out of the suggestion list.
    expect(screen.queryByRole('option', { name: 'Design' })).toBeNull()
  })

  it('removes a tag via its remove button and the last tag via Backspace', () => {
    render(<Harness initial={['One', 'Two']} />)
    fireEvent.click(screen.getByRole('button', { name: 'Remove tag One' }))
    expect(currentValue()).toEqual(['Two'])
    fireEvent.keyDown(screen.getByRole('combobox'), { key: 'Backspace' })
    expect(currentValue()).toEqual([])
  })

  it('disables input at the maxTags cap', () => {
    render(<Harness initial={['a', 'b']} maxTags={2} />)
    expect((screen.getByRole('combobox') as HTMLInputElement).disabled).toBe(true)
  })
})
