import type * as CherryStudioUi from '@cherrystudio/ui'
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
import type { ReactNode } from 'react'
import type * as ReactI18next from 'react-i18next'
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'

const MODEL = vi.hoisted(
  () =>
    ({
      id: 'provider::dialog-model',
      providerId: 'provider',
      name: 'Dialog Model',
      capabilities: [],
      supportsStreaming: true,
      isEnabled: true,
      isHidden: false
    }) as const
)
const modelSelectorPropsMock = vi.hoisted(() => vi.fn())

vi.mock('@renderer/components/Selector/model', () => ({
  ModelSelector: (props: {
    trigger: ReactNode
    onSelect: (model: typeof MODEL | undefined) => void
    portalContainer?: HTMLElement | null
  }) => {
    modelSelectorPropsMock(props)

    return (
      <div>
        {props.trigger}
        <button type="button" onClick={() => props.onSelect(MODEL)}>
          Pick model
        </button>
      </div>
    )
  }
}))

vi.mock('@renderer/components/EmojiPicker', () => ({
  default: ({ onEmojiClick }: { onEmojiClick: (emoji: string) => void }) => (
    <button type="button" onClick={() => onEmojiClick('🎓')}>
      Choose emoji
    </button>
  )
}))

vi.mock('@cherrystudio/ui', async (importOriginal) => {
  const actual = await importOriginal<typeof CherryStudioUi>()
  return actual
})

vi.mock('react-i18next', async (importOriginal) => {
  const actual = await importOriginal<typeof ReactI18next>()
  return {
    ...actual,
    useTranslation: () => ({
      t: (key: string) =>
        ({
          'common.avatar': 'Avatar',
          'common.cancel': 'Cancel',
          'common.description': 'Description',
          'common.model': 'Model',
          'common.name': 'Name',
          'library.config.dialogs.create.agent_title': 'New Agent',
          'library.config.dialogs.create.assistant_title': 'New Assistant',
          'library.config.dialogs.create.avatar_aria': 'Pick avatar',
          'library.config.dialogs.create.dialog_description': 'Create a lightweight resource from the selector.',
          'library.config.dialogs.create.description_placeholder': 'Describe this resource',
          'library.config.dialogs.create.model_placeholder': 'Select a model',
          'library.config.dialogs.create.model_required': 'Please select a model',
          'library.config.dialogs.create.name_placeholder': 'Name this resource',
          'library.config.dialogs.create.name_required': 'Please enter a name',
          'library.config.dialogs.create.submit': 'Create',
          'library.config.dialogs.create.submit_failed': 'Create failed'
        })[key] ?? key
    })
  }
})

import { ResourceCreateDialog } from '../ResourceCreateDialog'

beforeAll(() => {
  if (!HTMLElement.prototype.hasPointerCapture) {
    HTMLElement.prototype.hasPointerCapture = () => false
  }
  if (!HTMLElement.prototype.releasePointerCapture) {
    HTMLElement.prototype.releasePointerCapture = () => {}
  }
  if (!HTMLElement.prototype.setPointerCapture) {
    HTMLElement.prototype.setPointerCapture = () => {}
  }
})

afterEach(() => {
  cleanup()
  vi.clearAllMocks()
})

describe('ResourceCreateDialog', () => {
  it('validates required name and model fields', async () => {
    const onSubmit = vi.fn()
    render(<ResourceCreateDialog kind="assistant" open onOpenChange={vi.fn()} onSubmit={onSubmit} />)

    fireEvent.click(screen.getByRole('button', { name: 'Create' }))

    expect(await screen.findByText('Please enter a name')).toBeInTheDocument()
    expect(screen.getByText('Please select a model')).toBeInTheDocument()
    expect(onSubmit).not.toHaveBeenCalled()
  })

  it('submits avatar, name, model, and description', async () => {
    const onSubmit = vi.fn().mockResolvedValue(undefined)
    render(<ResourceCreateDialog kind="assistant" open onOpenChange={vi.fn()} onSubmit={onSubmit} />)

    fireEvent.click(screen.getByRole('button', { name: 'Pick avatar' }))
    fireEvent.click(screen.getByRole('button', { name: 'Choose emoji' }))
    fireEvent.change(screen.getByPlaceholderText('Name this resource'), { target: { value: 'Study Assistant' } })
    fireEvent.click(screen.getByRole('button', { name: 'Pick model' }))
    fireEvent.change(screen.getByPlaceholderText('Describe this resource'), {
      target: { value: 'Helps with notes' }
    })
    fireEvent.click(screen.getByRole('button', { name: 'Create' }))

    await waitFor(() =>
      expect(onSubmit).toHaveBeenCalledWith({
        avatar: '🎓',
        name: 'Study Assistant',
        modelId: MODEL.id,
        description: 'Helps with notes'
      })
    )
  })

  it('submits the selected model for agent creation', async () => {
    const onSubmit = vi.fn().mockResolvedValue(undefined)
    render(<ResourceCreateDialog kind="agent" open onOpenChange={vi.fn()} onSubmit={onSubmit} />)

    fireEvent.change(screen.getByPlaceholderText('Name this resource'), { target: { value: 'Build Agent' } })
    fireEvent.click(screen.getByRole('button', { name: 'Pick model' }))
    fireEvent.click(screen.getByRole('button', { name: 'Create' }))

    await waitFor(() =>
      expect(onSubmit).toHaveBeenCalledWith({
        avatar: '🤖',
        name: 'Build Agent',
        modelId: MODEL.id,
        description: ''
      })
    )
  })

  it('anchors the model selector portal inside the dialog content', async () => {
    const onSubmit = vi.fn()
    render(<ResourceCreateDialog kind="assistant" open onOpenChange={vi.fn()} onSubmit={onSubmit} />)

    await waitFor(() =>
      expect(modelSelectorPropsMock).toHaveBeenLastCalledWith(
        expect.objectContaining({ portalContainer: expect.any(HTMLDivElement) })
      )
    )
    const lastCall = modelSelectorPropsMock.mock.calls.at(-1)?.[0]

    expect(lastCall.portalContainer).toContainElement(screen.getByRole('dialog'))
  })

  it('disables actions while submitting and shows localized submit errors', async () => {
    const onSubmit = vi.fn().mockRejectedValue(new Error('Network down'))
    const { rerender } = render(
      <ResourceCreateDialog kind="agent" open isSubmitting onOpenChange={vi.fn()} onSubmit={onSubmit} />
    )

    expect(screen.getByRole('button', { name: 'Create' })).toBeDisabled()
    expect(screen.getByRole('button', { name: 'Cancel' })).toBeDisabled()

    rerender(<ResourceCreateDialog kind="agent" open onOpenChange={vi.fn()} onSubmit={onSubmit} />)
    fireEvent.change(screen.getByPlaceholderText('Name this resource'), { target: { value: 'Build Agent' } })
    fireEvent.click(screen.getByRole('button', { name: 'Pick model' }))
    fireEvent.click(screen.getByRole('button', { name: 'Create' }))

    expect(await screen.findByText('Create failed')).toBeInTheDocument()
    expect(screen.queryByText('Network down')).not.toBeInTheDocument()
  })

  it('blocks outside and escape closes while submitting', () => {
    const onOpenChange = vi.fn()
    render(<ResourceCreateDialog kind="assistant" open isSubmitting onOpenChange={onOpenChange} onSubmit={vi.fn()} />)

    fireEvent.keyDown(screen.getByRole('dialog'), { key: 'Escape' })
    fireEvent.pointerDown(document.body)

    expect(onOpenChange).not.toHaveBeenCalled()
  })

  it('resets fields when the same instance reopens', async () => {
    const onOpenChange = vi.fn()
    const { rerender } = render(
      <ResourceCreateDialog kind="assistant" open onOpenChange={onOpenChange} onSubmit={vi.fn()} />
    )

    fireEvent.click(screen.getByRole('button', { name: 'Pick avatar' }))
    fireEvent.click(screen.getByRole('button', { name: 'Choose emoji' }))
    fireEvent.change(screen.getByPlaceholderText('Name this resource'), { target: { value: 'Study Assistant' } })
    fireEvent.click(screen.getByRole('button', { name: 'Pick model' }))
    fireEvent.change(screen.getByPlaceholderText('Describe this resource'), {
      target: { value: 'Helps with notes' }
    })

    rerender(<ResourceCreateDialog kind="assistant" open={false} onOpenChange={onOpenChange} onSubmit={vi.fn()} />)
    rerender(<ResourceCreateDialog kind="assistant" open onOpenChange={onOpenChange} onSubmit={vi.fn()} />)

    expect(screen.getByPlaceholderText('Name this resource')).toHaveValue('')
    expect(screen.getByText('Select a model')).toBeInTheDocument()
    expect(screen.getByPlaceholderText('Describe this resource')).toHaveValue('')
    expect(screen.getByRole('button', { name: 'Pick avatar' })).toHaveTextContent('💬')
  })

  it('clears a previous submit error before retrying', async () => {
    const onSubmit = vi.fn().mockRejectedValueOnce(new Error('Network down')).mockResolvedValueOnce(undefined)
    render(<ResourceCreateDialog kind="agent" open onOpenChange={vi.fn()} onSubmit={onSubmit} />)

    fireEvent.change(screen.getByPlaceholderText('Name this resource'), { target: { value: 'Build Agent' } })
    fireEvent.click(screen.getByRole('button', { name: 'Pick model' }))
    fireEvent.click(screen.getByRole('button', { name: 'Create' }))

    expect(await screen.findByText('Create failed')).toBeInTheDocument()

    fireEvent.change(screen.getByPlaceholderText('Name this resource'), { target: { value: 'Build Agent 2' } })
    fireEvent.click(screen.getByRole('button', { name: 'Create' }))

    await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(2))
    expect(screen.queryByText('Create failed')).not.toBeInTheDocument()
  })
})
