import type { ModerationConfig } from '@/models/debug'
import { act, fireEvent, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import * as i18n from 'react-i18next'
import { renderWithConsoleQuery as render } from '@/test/console/query-data'
import { withSelectorKey } from '@/test/i18n-mock'
import ModerationSettingModal from '../moderation-setting-modal'

const mockNotify = vi.fn()
vi.mock('@langgenius/dify-ui/toast', () => ({
  toast: {
    error: (message: string) => mockNotify({ type: 'error', message }),
  },
}))

const mockSetShowAccountSettingModal = vi.fn()
vi.mock('@/context/modal-context', () => ({
  useModalContext: () => ({
    setShowAccountSettingModal: mockSetShowAccountSettingModal,
  }),
}))

let mockCodeBasedExtensions: { data: { data: Record<string, unknown>[] } } = { data: { data: [] } }
let mockModelProvidersData: {
  data: { data: Record<string, unknown>[] }
  isPending: boolean
  refetch: ReturnType<typeof vi.fn>
} = {
  data: {
    data: [
      {
        provider: 'langgenius/openai/openai',
        system_configuration: {
          enabled: true,
          current_quota_type: 'paid',
          quota_configurations: [{ quota_type: 'paid', is_valid: true }],
        },
        custom_configuration: { status: 'active' },
      },
    ],
  },
  isPending: false,
  refetch: vi.fn(),
}

vi.mock('@/service/use-common', () => ({
  useCodeBasedExtensions: () => mockCodeBasedExtensions,
  useModelProviders: () => mockModelProvidersData,
}))

vi.mock('@/app/components/header/account-setting/model-provider-page/declarations', () => ({
  CustomConfigurationStatusEnum: { active: 'active' },
}))

vi.mock('@/app/components/header/account-setting/constants', () => ({
  ACCOUNT_SETTING_TAB: { PROVIDER: 'provider' },
}))

vi.mock('@/app/components/header/account-setting/api-based-extension-page/selector', () => ({
  ApiBasedExtensionSelector: ({ onChange }: { value: string; onChange: (v: string) => void }) => (
    <div data-testid="api-selector">
      <button data-testid="select-api" onClick={() => onChange('api-ext-1')}>
        Select API
      </button>
    </div>
  ),
}))

const defaultData: ModerationConfig = {
  enabled: true,
  type: 'keywords',
  config: {
    keywords: 'bad\nword',
    inputs_config: { enabled: true, preset_response: 'Input blocked' },
    outputs_config: { enabled: false, preset_response: '' },
  },
}

describe('ModerationSettingModal', () => {
  const onSave = vi.fn()
  const renderModal = async (ui: React.ReactElement) => {
    await act(async () => {
      render(ui)
      await Promise.resolve()
    })
  }

  beforeEach(() => {
    vi.clearAllMocks()
    mockCodeBasedExtensions = { data: { data: [] } }
    mockModelProvidersData = {
      data: {
        data: [
          {
            provider: 'langgenius/openai/openai',
            system_configuration: {
              enabled: true,
              current_quota_type: 'paid',
              quota_configurations: [{ quota_type: 'paid', is_valid: true }],
            },
            custom_configuration: { status: 'active' },
          },
        ],
      },
      isPending: false,
      refetch: vi.fn(),
    }
  })

  afterEach(() => {
    vi.restoreAllMocks()
  })

  it('should render the modal title', async () => {
    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={vi.fn()} onSave={onSave} />,
    )

    expect(screen.getByText(/feature\.moderation\.modal\.title/))!.toBeInTheDocument()
  })

  it('should render provider options', async () => {
    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={vi.fn()} onSave={onSave} />,
    )

    expect(screen.getByText(/feature\.moderation\.modal\.provider\.openai/))!.toBeInTheDocument()
    // Keywords text appears both as provider option and section label
    expect(
      screen.getAllByText(/feature\.moderation\.modal\.provider\.keywords/).length,
    ).toBeGreaterThanOrEqual(1)
    expect(screen.getByText(/apiBasedExtension\.selector\.title/))!.toBeInTheDocument()
  })

  it('should show keywords textarea when keywords type is selected', async () => {
    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={vi.fn()} onSave={onSave} />,
    )

    const textarea = screen.getByPlaceholderText(
      /feature\.moderation\.modal\.keywords\.placeholder/,
    ) as HTMLTextAreaElement
    expect(textarea)!.toBeInTheDocument()
    expect(textarea)!.toHaveValue('bad\nword')
  })

  it('should render cancel and save buttons', async () => {
    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={vi.fn()} onSave={onSave} />,
    )

    expect(screen.getByText(/operation\.cancel/))!.toBeInTheDocument()
    expect(screen.getByText(/operation\.save/))!.toBeInTheDocument()
  })

  it('should call onCancel when cancel is clicked', async () => {
    const onCancel = vi.fn()
    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={onCancel} onSave={onSave} />,
    )

    fireEvent.click(screen.getByText(/operation\.cancel/))

    expect(onCancel).toHaveBeenCalled()
  })

  it('should call onCancel when close icon receives Enter key', async () => {
    const onCancel = vi.fn()
    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={onCancel} onSave={onSave} />,
    )

    const user = userEvent.setup()
    const closeButton = screen.getByRole('button', { name: 'common.operation.close' })
    closeButton.focus()
    await user.keyboard('{Enter}')

    expect(onCancel).toHaveBeenCalledTimes(1)
  })

  it('should call onCancel when close icon receives Space key', async () => {
    const onCancel = vi.fn()
    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={onCancel} onSave={onSave} />,
    )

    const user = userEvent.setup()
    const closeButton = screen.getByRole('button', { name: 'common.operation.close' })
    closeButton.focus()
    await user.keyboard(' ')

    expect(onCancel).toHaveBeenCalledTimes(1)
  })

  it('should not call onCancel when close icon receives non-action key', async () => {
    const onCancel = vi.fn()
    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={onCancel} onSave={onSave} />,
    )

    const closeButton = screen.getByRole('button', { name: 'common.operation.close' })
    closeButton.focus()
    fireEvent.keyDown(closeButton, { key: 'Escape' })

    expect(onCancel).not.toHaveBeenCalled()
  })

  it('should show error when saving without inputs or outputs enabled', async () => {
    const data: ModerationConfig = {
      ...defaultData,
      config: {
        keywords: 'test',
        inputs_config: { enabled: false, preset_response: '' },
        outputs_config: { enabled: false, preset_response: '' },
      },
    }
    await renderModal(<ModerationSettingModal data={data} onCancel={vi.fn()} onSave={onSave} />)

    fireEvent.click(screen.getByText(/operation\.save/))

    expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({ type: 'error' }))
  })

  it('should show error when keywords type has no keywords', async () => {
    const data: ModerationConfig = {
      ...defaultData,
      config: {
        keywords: '',
        inputs_config: { enabled: true, preset_response: 'blocked' },
        outputs_config: { enabled: false, preset_response: '' },
      },
    }
    await renderModal(<ModerationSettingModal data={data} onCancel={vi.fn()} onSave={onSave} />)

    fireEvent.click(screen.getByText(/operation\.save/))

    expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({ type: 'error' }))
  })

  it('should call onSave with formatted data when valid', async () => {
    const data: ModerationConfig = {
      ...defaultData,
      config: {
        keywords: 'bad\nword',
        inputs_config: { enabled: true, preset_response: 'blocked' },
        outputs_config: { enabled: false, preset_response: '' },
      },
    }
    await renderModal(<ModerationSettingModal data={data} onCancel={vi.fn()} onSave={onSave} />)

    fireEvent.click(screen.getByText(/operation\.save/))

    expect(onSave).toHaveBeenCalledWith(
      expect.objectContaining({
        type: 'keywords',
        enabled: true,
        config: expect.objectContaining({
          keywords: 'bad\nword',
          inputs_config: expect.objectContaining({ enabled: true }),
        }),
      }),
    )
  })

  it('should save the latest preset response when content textarea changes', async () => {
    const data: ModerationConfig = {
      ...defaultData,
      config: {
        keywords: 'bad',
        inputs_config: { enabled: true, preset_response: 'blocked' },
        outputs_config: { enabled: false, preset_response: '' },
      },
    }
    await renderModal(<ModerationSettingModal data={data} onCancel={vi.fn()} onSave={onSave} />)

    fireEvent.change(
      screen.getByRole('textbox', { name: /feature\.moderation\.modal\.content\.preset/ }),
      {
        target: { value: 'updated blocked response' },
      },
    )
    fireEvent.click(screen.getByText(/operation\.save/))

    expect(onSave).toHaveBeenCalledWith(
      expect.objectContaining({
        config: expect.objectContaining({
          inputs_config: expect.objectContaining({
            preset_response: 'updated blocked response',
          }),
        }),
      }),
    )
  })

  it('should show api selector when api type is selected', async () => {
    await renderModal(
      <ModerationSettingModal
        data={{
          ...defaultData,
          type: 'api',
          config: { inputs_config: { enabled: true, preset_response: '' } },
        }}
        onCancel={vi.fn()}
        onSave={onSave}
      />,
    )

    expect(screen.getByTestId('api-selector'))!.toBeInTheDocument()
  })

  it('should switch provider type when clicked', async () => {
    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={vi.fn()} onSave={onSave} />,
    )

    // Click on openai_moderation provider
    fireEvent.click(screen.getByText(/feature\.moderation\.modal\.provider\.openai/))

    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    // The keywords textarea should no longer be visible since type changed
    expect(
      screen.queryByPlaceholderText(/feature\.moderation\.modal\.keywords\.placeholder/),
    ).not.toBeInTheDocument()
  })

  it('should update keywords on textarea change', async () => {
    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={vi.fn()} onSave={onSave} />,
    )

    const textarea = screen.getByPlaceholderText(
      /feature\.moderation\.modal\.keywords\.placeholder/,
    ) as HTMLTextAreaElement
    fireEvent.change(textarea, { target: { value: 'new\nkeywords' } })

    expect(textarea)!.toHaveValue('new\nkeywords')
  })

  it('should render moderation content sections', async () => {
    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={vi.fn()} onSave={onSave} />,
    )

    expect(screen.getByText(/feature\.moderation\.modal\.content\.input/))!.toBeInTheDocument()
    expect(screen.getByText(/feature\.moderation\.modal\.content\.output/))!.toBeInTheDocument()
  })

  it('should show error when inputs enabled but no preset_response for keywords type', async () => {
    const data: ModerationConfig = {
      ...defaultData,
      config: {
        keywords: 'test',
        inputs_config: { enabled: true, preset_response: '' },
        outputs_config: { enabled: false, preset_response: '' },
      },
    }
    await renderModal(<ModerationSettingModal data={data} onCancel={vi.fn()} onSave={onSave} />)

    fireEvent.click(screen.getByText(/operation\.save/))

    expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({ type: 'error' }))
  })

  it('should show error when api type has no api_based_extension_id', async () => {
    const data: ModerationConfig = {
      enabled: true,
      type: 'api',
      config: {
        inputs_config: { enabled: true, preset_response: '' },
        outputs_config: { enabled: false, preset_response: '' },
      },
    }
    await renderModal(<ModerationSettingModal data={data} onCancel={vi.fn()} onSave={onSave} />)

    fireEvent.click(screen.getByText(/operation\.save/))

    expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({ type: 'error' }))
  })

  it('should save with api_based_extension_id in formatted data for api type', async () => {
    const data: ModerationConfig = {
      enabled: true,
      type: 'api',
      config: {
        api_based_extension_id: 'ext-1',
        inputs_config: { enabled: true, preset_response: '' },
        outputs_config: { enabled: false, preset_response: '' },
      },
    }
    await renderModal(<ModerationSettingModal data={data} onCancel={vi.fn()} onSave={onSave} />)

    // api type doesn't require preset_response, so save should succeed
    fireEvent.click(screen.getByText(/operation\.save/))

    expect(onSave).toHaveBeenCalledWith(
      expect.objectContaining({
        type: 'api',
        config: expect.objectContaining({
          api_based_extension_id: 'ext-1',
        }),
      }),
    )
  })

  it('should show error when outputs enabled but no preset_response for keywords type', async () => {
    const data: ModerationConfig = {
      ...defaultData,
      config: {
        keywords: 'test',
        inputs_config: { enabled: false, preset_response: '' },
        outputs_config: { enabled: true, preset_response: '' },
      },
    }
    await renderModal(<ModerationSettingModal data={data} onCancel={vi.fn()} onSave={onSave} />)

    fireEvent.click(screen.getByText(/operation\.save/))

    expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({ type: 'error' }))
  })

  it('should toggle input moderation content', async () => {
    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={vi.fn()} onSave={onSave} />,
    )

    const switches = screen.getAllByRole('switch')
    expect(
      screen.getAllByPlaceholderText(/feature\.moderation\.modal\.content\.placeholder/),
    ).toHaveLength(1)

    fireEvent.click(switches[0]!)

    expect(
      screen.queryAllByPlaceholderText(/feature\.moderation\.modal\.content\.placeholder/),
    ).toHaveLength(0)
  })

  it('should toggle output moderation content', async () => {
    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={vi.fn()} onSave={onSave} />,
    )

    const switches = screen.getAllByRole('switch')
    expect(
      screen.getAllByPlaceholderText(/feature\.moderation\.modal\.content\.placeholder/),
    ).toHaveLength(1)

    fireEvent.click(switches[1]!)

    expect(
      screen.getAllByPlaceholderText(/feature\.moderation\.modal\.content\.placeholder/),
    ).toHaveLength(2)
  })

  it('should select api extension via api selector', async () => {
    await renderModal(
      <ModerationSettingModal
        data={{
          ...defaultData,
          type: 'api',
          config: { inputs_config: { enabled: true, preset_response: '' } },
        }}
        onCancel={vi.fn()}
        onSave={onSave}
      />,
    )

    fireEvent.click(screen.getByTestId('select-api'))

    // Trigger save and confirm the chosen extension id is passed through
    fireEvent.click(screen.getByText(/operation\.save/))
    expect(onSave).toHaveBeenCalledWith(
      expect.objectContaining({
        config: expect.objectContaining({ api_based_extension_id: 'api-ext-1' }),
      }),
    )
  })

  it('should save with openai_moderation type when configured', async () => {
    await renderModal(
      <ModerationSettingModal
        data={{
          enabled: true,
          type: 'openai_moderation',
          config: {
            inputs_config: { enabled: true, preset_response: 'blocked' },
            outputs_config: { enabled: false, preset_response: '' },
          },
        }}
        onCancel={vi.fn()}
        onSave={onSave}
      />,
    )

    fireEvent.click(screen.getByText(/operation\.save/))

    expect(onSave).toHaveBeenCalledWith(
      expect.objectContaining({
        type: 'openai_moderation',
      }),
    )
  })

  it('should handle keyword truncation to 100 chars per line and 100 lines', async () => {
    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={vi.fn()} onSave={onSave} />,
    )

    const textarea = screen.getByPlaceholderText(
      /feature\.moderation\.modal\.keywords\.placeholder/,
    )
    // Create a long keyword that exceeds 100 chars
    const longWord = 'a'.repeat(150)
    fireEvent.change(textarea, { target: { value: longWord } })

    // Value should be truncated to 100 chars
    expect((textarea as HTMLTextAreaElement).value.length).toBeLessThanOrEqual(100)
  })

  it('should save with formatted outputs_config when both enabled', async () => {
    const data: ModerationConfig = {
      ...defaultData,
      config: {
        keywords: 'test',
        inputs_config: { enabled: true, preset_response: 'input blocked' },
        outputs_config: { enabled: true, preset_response: 'output blocked' },
      },
    }
    await renderModal(<ModerationSettingModal data={data} onCancel={vi.fn()} onSave={onSave} />)

    fireEvent.click(screen.getByText(/operation\.save/))

    expect(onSave).toHaveBeenCalledWith(
      expect.objectContaining({
        config: expect.objectContaining({
          inputs_config: expect.objectContaining({ enabled: true }),
          outputs_config: expect.objectContaining({ enabled: true }),
        }),
      }),
    )
  })

  it('should switch from keywords to api type', async () => {
    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={vi.fn()} onSave={onSave} />,
    )

    // Click api provider
    fireEvent.click(screen.getByText(/apiBasedExtension\.selector\.title/))

    // API selector should now be visible, keywords textarea should be hidden
    // API selector should now be visible, keywords textarea should be hidden
    expect(screen.getByTestId('api-selector'))!.toBeInTheDocument()
    expect(
      screen.queryByPlaceholderText(/feature\.moderation\.modal\.keywords\.placeholder/),
    ).not.toBeInTheDocument()
  })

  it('should handle empty lines in keywords', async () => {
    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={vi.fn()} onSave={onSave} />,
    )

    const textarea = screen.getByPlaceholderText(
      /feature\.moderation\.modal\.keywords\.placeholder/,
    ) as HTMLTextAreaElement
    fireEvent.change(textarea, { target: { value: 'word1\n\nword2\n\n' } })

    expect(textarea.value).toBe('word1\n\nword2\n')
  })

  it('should show OpenAI not configured warning when OpenAI provider is not set up', async () => {
    mockModelProvidersData = {
      data: {
        data: [
          {
            provider: 'langgenius/openai/openai',
            system_configuration: {
              enabled: false,
              current_quota_type: 'free',
              quota_configurations: [],
            },
            custom_configuration: { status: 'no-configure' },
          },
        ],
      },
      isPending: false,
      refetch: vi.fn(),
    }

    await renderModal(
      <ModerationSettingModal
        data={{
          ...defaultData,
          type: 'openai_moderation',
          config: { inputs_config: { enabled: true, preset_response: '' } },
        }}
        onCancel={vi.fn()}
        onSave={onSave}
      />,
    )

    expect(
      screen.getByText(/feature\.moderation\.modal\.openaiNotConfig\.before/),
    )!.toBeInTheDocument()
  })

  it('should open settings modal when provider link is clicked in OpenAI warning', async () => {
    mockModelProvidersData = {
      data: {
        data: [
          {
            provider: 'langgenius/openai/openai',
            system_configuration: {
              enabled: false,
              current_quota_type: 'free',
              quota_configurations: [],
            },
            custom_configuration: { status: 'no-configure' },
          },
        ],
      },
      isPending: false,
      refetch: vi.fn(),
    }

    await renderModal(
      <ModerationSettingModal
        data={{
          ...defaultData,
          type: 'openai_moderation',
          config: { inputs_config: { enabled: true, preset_response: '' } },
        }}
        onCancel={vi.fn()}
        onSave={onSave}
      />,
    )

    fireEvent.click(screen.getByText(/settings\.provider/))

    expect(mockSetShowAccountSettingModal).toHaveBeenCalled()

    expect(mockSetShowAccountSettingModal).toHaveBeenCalledWith({
      payload: 'provider',
      onCancelCallback: expect.any(Function),
    })
  })

  it('should not save when OpenAI type is selected but not configured', async () => {
    mockModelProvidersData = {
      data: {
        data: [
          {
            provider: 'langgenius/openai/openai',
            system_configuration: {
              enabled: false,
              current_quota_type: 'free',
              quota_configurations: [],
            },
            custom_configuration: { status: 'no-configure' },
          },
        ],
      },
      isPending: false,
      refetch: vi.fn(),
    }

    await renderModal(
      <ModerationSettingModal
        data={{
          ...defaultData,
          type: 'openai_moderation',
          config: {
            inputs_config: { enabled: true, preset_response: 'blocked' },
            outputs_config: { enabled: false, preset_response: '' },
          },
        }}
        onCancel={vi.fn()}
        onSave={onSave}
      />,
    )

    fireEvent.click(screen.getByText(/operation\.save/))

    expect(onSave).not.toHaveBeenCalled()
  })

  it('should render code-based extension providers', async () => {
    mockCodeBasedExtensions = {
      data: {
        data: [
          {
            name: 'custom-ext',
            label: { 'en-US': 'Custom Extension', 'zh-Hans': '自定义扩展' },
            form_schema: [
              {
                variable: 'api_url',
                label: { 'en-US': 'API URL', 'zh-Hans': 'API 地址' },
                type: 'text-input',
                required: true,
                default: '',
                placeholder: 'Enter URL',
                options: [],
                max_length: 200,
              },
            ],
          },
        ],
      },
    }

    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={vi.fn()} onSave={onSave} />,
    )

    expect(screen.getByText('Custom Extension'))!.toBeInTheDocument()
  })

  it('should show form generation when code-based extension is selected', async () => {
    mockCodeBasedExtensions = {
      data: {
        data: [
          {
            name: 'custom-ext',
            label: { 'en-US': 'Custom Extension', 'zh-Hans': '自定义扩展' },
            form_schema: [
              {
                variable: 'api_url',
                label: { 'en-US': 'API URL', 'zh-Hans': 'API 地址' },
                type: 'text-input',
                required: true,
                default: '',
                placeholder: 'Enter URL',
                options: [],
                max_length: 200,
              },
            ],
          },
        ],
      },
    }

    await renderModal(
      <ModerationSettingModal
        data={{
          ...defaultData,
          type: 'custom-ext',
          config: { inputs_config: { enabled: true, preset_response: '' } },
        }}
        onCancel={vi.fn()}
        onSave={onSave}
      />,
    )

    expect(screen.getByText('API URL'))!.toBeInTheDocument()
    expect(screen.getByPlaceholderText('Enter URL'))!.toBeInTheDocument()
  })

  it('should initialize config from form schema when switching to code-based extension', async () => {
    mockCodeBasedExtensions = {
      data: {
        data: [
          {
            name: 'custom-ext',
            label: { 'en-US': 'Custom Extension', 'zh-Hans': '自定义扩展' },
            form_schema: [
              {
                variable: 'api_url',
                label: { 'en-US': 'API URL', 'zh-Hans': 'API 地址' },
                type: 'text-input',
                required: true,
                default: 'https://default.com',
                placeholder: '',
                options: [],
                max_length: 200,
              },
            ],
          },
        ],
      },
    }

    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={vi.fn()} onSave={onSave} />,
    )

    // Click on the custom extension provider
    fireEvent.click(screen.getByText('Custom Extension'))

    // The form input should use the default value from form schema
    // The form input should use the default value from form schema
    expect(screen.getByDisplayValue('https://default.com'))!.toBeInTheDocument()
  })

  it('should show error when required form schema field is empty on save', async () => {
    mockCodeBasedExtensions = {
      data: {
        data: [
          {
            name: 'custom-ext',
            label: { 'en-US': 'Custom Extension', 'zh-Hans': '自定义扩展' },
            form_schema: [
              {
                variable: 'api_url',
                label: { 'en-US': 'API URL', 'zh-Hans': 'API 地址' },
                type: 'text-input',
                required: true,
                default: '',
                placeholder: '',
                options: [],
                max_length: 200,
              },
            ],
          },
        ],
      },
    }

    await renderModal(
      <ModerationSettingModal
        data={{
          ...defaultData,
          type: 'custom-ext',
          config: { inputs_config: { enabled: true, preset_response: 'blocked' } },
        }}
        onCancel={vi.fn()}
        onSave={onSave}
      />,
    )

    fireEvent.click(screen.getByText(/operation\.save/))

    expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({ type: 'error' }))
  })

  it('should save with code-based extension config when valid', async () => {
    mockCodeBasedExtensions = {
      data: {
        data: [
          {
            name: 'custom-ext',
            label: { 'en-US': 'Custom Extension', 'zh-Hans': '自定义扩展' },
            form_schema: [
              {
                variable: 'api_url',
                label: { 'en-US': 'API URL', 'zh-Hans': 'API 地址' },
                type: 'text-input',
                required: true,
                default: '',
                placeholder: '',
                options: [],
                max_length: 200,
              },
            ],
          },
        ],
      },
    }

    await renderModal(
      <ModerationSettingModal
        data={{
          ...defaultData,
          type: 'custom-ext',
          config: {
            api_url: 'https://example.com',
            inputs_config: { enabled: true, preset_response: 'blocked' },
            outputs_config: { enabled: false, preset_response: '' },
          },
        }}
        onCancel={vi.fn()}
        onSave={onSave}
      />,
    )

    fireEvent.click(screen.getByText(/operation\.save/))

    expect(onSave).toHaveBeenCalledWith(
      expect.objectContaining({
        type: 'custom-ext',
        config: expect.objectContaining({
          api_url: 'https://example.com',
        }),
      }),
    )
  })

  it('should update code-based extension form value and save updated config', async () => {
    mockCodeBasedExtensions = {
      data: {
        data: [
          {
            name: 'custom-ext',
            label: { 'en-US': 'Custom Extension', 'zh-Hans': '自定义扩展' },
            form_schema: [
              {
                variable: 'api_url',
                label: { 'en-US': 'API URL', 'zh-Hans': 'API 地址' },
                type: 'text-input',
                required: true,
                default: '',
                placeholder: 'Enter URL',
                options: [],
                max_length: 200,
              },
            ],
          },
        ],
      },
    }

    await renderModal(
      <ModerationSettingModal
        data={{
          ...defaultData,
          type: 'custom-ext',
          config: {
            inputs_config: { enabled: true, preset_response: 'blocked' },
            outputs_config: { enabled: false, preset_response: '' },
          },
        }}
        onCancel={vi.fn()}
        onSave={onSave}
      />,
    )

    fireEvent.change(screen.getByPlaceholderText('Enter URL'), {
      target: { value: 'https://changed.com' },
    })
    fireEvent.click(screen.getByText(/operation\.save/))

    expect(onSave).toHaveBeenCalledWith(
      expect.objectContaining({
        type: 'custom-ext',
        config: expect.objectContaining({
          api_url: 'https://changed.com',
        }),
      }),
    )
  })

  it('should show doc link for api type', async () => {
    await renderModal(
      <ModerationSettingModal
        data={{
          ...defaultData,
          type: 'api',
          config: { inputs_config: { enabled: true, preset_response: '' } },
        }}
        onCancel={vi.fn()}
        onSave={onSave}
      />,
    )

    expect(screen.getByText(/apiBasedExtension\.link/))!.toBeInTheDocument()
  })

  it('should fallback missing inputs_config to disabled in formatted save data', async () => {
    await renderModal(
      <ModerationSettingModal
        data={{
          enabled: true,
          type: 'api',
          config: {
            api_based_extension_id: 'ext-fallback',
            outputs_config: { enabled: true, preset_response: '' },
          },
        }}
        onCancel={vi.fn()}
        onSave={onSave}
      />,
    )

    fireEvent.click(screen.getByText(/operation\.save/))

    expect(onSave).toHaveBeenCalledWith(
      expect.objectContaining({
        type: 'api',
        config: expect.objectContaining({
          inputs_config: expect.objectContaining({ enabled: false }),
          outputs_config: expect.objectContaining({ enabled: true }),
        }),
      }),
    )
  })

  it('should fallback to empty translated strings for optional placeholders and titles', async () => {
    const useTranslationSpy = vi.spyOn(i18n, 'useTranslation').mockReturnValue({
      t: withSelectorKey((key: string) =>
        [
          'feature.moderation.modal.keywords.placeholder',
          'feature.moderation.modal.content.input',
          'feature.moderation.modal.content.output',
        ].includes(key)
          ? ''
          : key,
      ),
      i18n: { language: 'en-US' },
    } as unknown as ReturnType<typeof i18n.useTranslation>)

    await renderModal(
      <ModerationSettingModal data={defaultData} onCancel={vi.fn()} onSave={onSave} />,
    )

    const textarea = screen.getAllByRole('textbox')[0]
    expect(textarea)!.toHaveAttribute('placeholder', '')
    useTranslationSpy.mockRestore()
  })
})
