import type { ComponentProps } from 'react'
import { describe, expect, it, vi } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import { FieldsProvider } from './provider.js'
import type { FieldsClient, ResolvedFieldGroup } from './client.js'
import { FieldValuesForm } from './values-form.js'

const group: ResolvedFieldGroup = {
  id: 'g1',
  key: 'attrs',
  label: 'Attributes',
  location: { entityType: 'content' },
  origin: 'db',
  fields: [{ key: 'subtitle', type: 'text', label: 'Subtitle' }],
}

const groupTwo: ResolvedFieldGroup = {
  id: 'g2',
  key: 'meta',
  label: 'Meta',
  location: { entityType: 'content' },
  origin: 'db',
  fields: [{ key: 'subtitle', type: 'text', label: 'Meta subtitle' }],
}

const stubClient = {
  getEntityValues: vi.fn(async () => ({})),
  setEntityValues: vi.fn(async () => {}),
  resolveGroups: vi.fn(async () => [group]),
  listGroups: vi.fn(async () => [group]),
  createGroup: vi.fn(),
  updateGroup: vi.fn(),
  deleteGroup: vi.fn(),
} as unknown as FieldsClient

function renderForm(
  props: Partial<ComponentProps<typeof FieldValuesForm>> = {},
) {
  const onSubmit = vi.fn()
  render(
    <FieldsProvider client={stubClient}>
      <FieldValuesForm
        groups={[group]}
        initialValues={{ g1: {} }}
        onSubmit={onSubmit}
        {...props}
      />
    </FieldsProvider>,
  )
  return { onSubmit }
}

describe('FieldValuesForm', () => {
  it('renders one labelled input per field definition', () => {
    renderForm()
    expect(screen.getByLabelText('Subtitle')).toBeTruthy()
  })

  it('submits changed values via onSubmit', () => {
    const { onSubmit } = renderForm()
    fireEvent.change(screen.getByLabelText('Subtitle'), { target: { value: 'Hello' } })
    fireEvent.click(screen.getByRole('button', { name: 'Save fields' }))
    expect(onSubmit).toHaveBeenCalledWith([{ groupId: 'g1', values: { subtitle: 'Hello' } }])
  })

  it('renders field-level errors in a focusable role=alert', () => {
    renderForm({ errors: { g1: { subtitle: 'Subtitle is required' } } })
    const alert = screen.getByRole('alert')
    expect(alert.textContent).toContain('Subtitle is required')
    expect(alert.getAttribute('tabindex')).toBe('0')
  })

  it('disables all inputs when disabled', () => {
    renderForm({ disabled: true })
    expect((screen.getByLabelText('Subtitle') as HTMLInputElement).disabled).toBe(true)
    expect((screen.getByRole('button', { name: 'Save fields' }) as HTMLButtonElement).disabled).toBe(
      true,
    )
  })

  it('keeps same field keys in separate groups without collision on submit', () => {
    const onSubmit = vi.fn()
    render(
      <FieldsProvider client={stubClient}>
        <FieldValuesForm
          groups={[group, groupTwo]}
          initialValues={{ g1: {}, g2: {} }}
          onSubmit={onSubmit}
        />
      </FieldsProvider>,
    )
    fireEvent.change(screen.getByLabelText('Subtitle'), { target: { value: 'First group' } })
    fireEvent.change(screen.getByLabelText('Meta subtitle'), { target: { value: 'Second group' } })
    fireEvent.click(screen.getByRole('button', { name: 'Save fields' }))
    expect(onSubmit).toHaveBeenCalledWith([
      { groupId: 'g1', values: { subtitle: 'First group' } },
      { groupId: 'g2', values: { subtitle: 'Second group' } },
    ])
  })

  it('falls back to group key as groupId for a code-defined group (no id)', () => {
    const codeGroup: ResolvedFieldGroup = {
      key: 'attrs',
      label: 'Attributes',
      location: { entityType: 'content' },
      origin: 'code',
      fields: [{ key: 'subtitle', type: 'text', label: 'Subtitle' }],
    }
    const onSubmit = vi.fn()
    render(
      <FieldsProvider client={stubClient}>
        <FieldValuesForm
          groups={[codeGroup]}
          initialValues={{ attrs: { subtitle: 'Seed' } }}
          errors={{ attrs: { subtitle: 'Subtitle is required' } }}
          onSubmit={onSubmit}
        />
      </FieldsProvider>,
    )
    // error keyed by the fallback (key) groupId is rendered
    expect(screen.getByRole('alert').textContent).toContain('Subtitle is required')
    // initialValues keyed by the fallback (key) groupId seeds the input
    expect((screen.getByLabelText('Subtitle') as HTMLInputElement).value).toBe('Seed')
    fireEvent.change(screen.getByLabelText('Subtitle'), { target: { value: 'Edited' } })
    fireEvent.click(screen.getByRole('button', { name: 'Save fields' }))
    expect(onSubmit).toHaveBeenCalledWith([{ groupId: 'attrs', values: { subtitle: 'Edited' } }])
  })
})
