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

/**
 * PostFieldsModal — control selection is driven by the post type's field
 * config: a `select` category renders a structured dropdown (UpChat taxonomy
 * feedback P1), an `array` tags field renders the chip input (P2). Free-text
 * configs keep their legacy inputs so existing sites are unaffected.
 */

const cmsApi = vi.fn(async () => ({ data: { field: 'tags', values: [] }, status: 200 }))

vi.mock('../../lib/api.js', () => ({
  cmsApi: (...args: unknown[]) => cmsApi(...(args as [])),
  ensureCsrfToken: vi.fn(async () => {}),
}))

// The rich text editor pulls in heavy dependencies; stub it.
vi.mock('../../fields/RichTextField.js', () => ({
  RichTextField: () => <div data-testid="rich-text" />,
}))

vi.mock('../../lib/coauthor-client.js', () => ({
  coauthorText: vi.fn(async () => ({
    ok: true,
    result: { action: 'compress', text: 'AI excerpt' },
  })),
}))

const { PostFieldsModal } = await import('../../views/post-editor/PostFieldsModal.js')
type ModalProps = Parameters<typeof PostFieldsModal>[0]

function basePost(over: Partial<ModalProps['post']> = {}): ModalProps['post'] {
  return {
    id: 'p1',
    postType: 'posts',
    title: 'Hello',
    slug: 'hello',
    excerpt: '',
    featuredImage: '',
    body: '',
    category: '',
    tags: [],
    status: 'DRAFT',
    publishDate: null,
    scheduledAt: null,
    scheduledUnpublishAt: null,
    sections: [],
    seoTitle: '',
    seoDescription: '',
    publishedAt: null,
    updatedAt: null,
    ...over,
  }
}

const SELECT_FIELDS: ModalProps['fields'] = {
  category: {
    type: 'select',
    options: [
      { label: 'News', value: 'news' },
      { label: 'Tutorials', value: 'tutorials' },
    ],
  },
  tags: { type: 'array', maxRows: 12 },
}

beforeEach(() => {
  cmsApi.mockClear()
})

describe('PostFieldsModal', () => {
  it('renders a category dropdown when the field config is a select', () => {
    render(
      <PostFieldsModal
        open
        post={basePost()}
        canEdit
        fields={SELECT_FIELDS}
        onClose={() => {}}
        onSave={() => {}}
      />,
    )
    const select = screen.getByLabelText('Category') as HTMLSelectElement
    expect(select.tagName).toBe('SELECT')
    const labels = [...select.options].map((o) => o.textContent)
    expect(labels).toEqual(['No category', 'News', 'Tutorials'])
  })

  it('keeps a legacy free-text category selectable instead of discarding it', () => {
    render(
      <PostFieldsModal
        open
        post={basePost({ category: 'Hand Typed' })}
        canEdit
        fields={SELECT_FIELDS}
        onClose={() => {}}
        onSave={() => {}}
      />,
    )
    const select = screen.getByLabelText('Category') as HTMLSelectElement
    expect(select.value).toBe('Hand Typed')
  })

  it('falls back to a free-text category input without field config', () => {
    render(<PostFieldsModal open post={basePost()} canEdit onClose={() => {}} onSave={() => {}} />)
    const input = screen.getByLabelText('Category') as HTMLInputElement
    expect(input.tagName).toBe('INPUT')
    // No tags config → no tags control.
    expect(screen.queryByLabelText('Tags')).toBeNull()
  })

  it('saves selected category and edited tags in the patch', () => {
    const onSave = vi.fn()
    render(
      <PostFieldsModal
        open
        post={basePost({ tags: ['AI'] })}
        canEdit
        fields={SELECT_FIELDS}
        onClose={() => {}}
        onSave={onSave}
      />,
    )
    fireEvent.change(screen.getByLabelText('Category'), { target: { value: 'tutorials' } })
    const tagInput = screen.getByLabelText('Tags')
    fireEvent.change(tagInput, { target: { value: 'CMS' } })
    fireEvent.keyDown(tagInput, { key: 'Enter' })
    fireEvent.click(screen.getByRole('button', { name: 'Done' }))
    expect(onSave).toHaveBeenCalledWith(
      expect.objectContaining({ category: 'tutorials', tags: ['AI', 'CMS'] }),
    )
  })

  it('derives slug from title until the slug field is edited', () => {
    render(
      <PostFieldsModal
        open
        post={basePost({ title: '', slug: '' })}
        canEdit
        onClose={() => {}}
        onSave={() => {}}
      />,
    )
    fireEvent.change(screen.getByLabelText(/^Title/), { target: { value: 'Hello World' } })
    expect((screen.getByLabelText(/^Slug/) as HTMLInputElement).value).toBe('hello-world')
    fireEvent.change(screen.getByLabelText(/^Slug/), { target: { value: 'custom-slug' } })
    fireEvent.change(screen.getByLabelText(/^Title/), { target: { value: 'Changed Title' } })
    expect((screen.getByLabelText(/^Slug/) as HTMLInputElement).value).toBe('custom-slug')
  })

  it('exposes an AI generate control for the excerpt field', () => {
    render(<PostFieldsModal open post={basePost()} canEdit onClose={() => {}} onSave={() => {}} />)
    expect(screen.getByRole('button', { name: 'Generate excerpt from content' })).toBeTruthy()
    expect(screen.queryByLabelText('SEO title')).toBeNull()
    expect(screen.queryByLabelText('SEO description')).toBeNull()
  })

  it('prompts before discarding when Cancel is clicked with unsaved edits', () => {
    const onClose = vi.fn()
    render(<PostFieldsModal open post={basePost()} canEdit onClose={onClose} onSave={() => {}} />)
    fireEvent.change(screen.getByLabelText(/^Title/), { target: { value: 'Changed title' } })
    fireEvent.click(screen.getByRole('button', { name: 'Cancel' }))
    expect(onClose).not.toHaveBeenCalled()
    expect(screen.getByRole('dialog', { name: 'Discard unsaved changes?' })).toBeTruthy()
  })

  it('closes without prompting when nothing changed', () => {
    const onClose = vi.fn()
    render(<PostFieldsModal open post={basePost()} canEdit onClose={onClose} onSave={() => {}} />)
    fireEvent.click(screen.getByRole('button', { name: 'Cancel' }))
    expect(onClose).toHaveBeenCalledTimes(1)
    expect(screen.queryByRole('dialog', { name: 'Discard unsaved changes?' })).toBeNull()
  })
})
