import { fireEvent, render, screen } from '@testing-library/react'
import { FormProvider, useForm, useFormContext } from 'react-hook-form'

import { getSharedTiDB } from 'dbaas/screens/NextGen/shared/model/shared-tidb-state'
import { SharedTiDBContext } from 'dbaas/screens/NextGen/tidb-mgmt-container/shared/tidb-context'
import { useGetPremiumDMSupportedSources, useNgListSinkEndpoints } from 'dbaas/services/dataflow'

import { FormSourceConnection } from './FormSourceConnection'
import { buildPremiumConnProfile } from './helper'
import type { PremiumStep1FormData } from './PremiumDMStep1'

jest.mock('@tidb-cloud/ui-components', () => ({ TextFileUpload: () => null }))
jest.mock('@tidbcloud/uikit', () => {
  const Container = ({ children }) => <div>{children}</div>
  return {
    useDisclosure: () => [false, {}],
    FormSelect: ({ name, label, data, onChange }) => {
      const { register } = useFormContext()
      const field = register(name)
      return (
        <label>
          {label}
          <select
            {...field}
            onChange={(event) => {
              field.onChange(event)
              onChange?.(event.target.value)
            }}
          >
            <option value="" />
            {data.map(({ value, label }) => (
              <option key={value} value={value}>
                {label}
              </option>
            ))}
          </select>
        </label>
      )
    },
    FormTextInput: ({ name, label }) => (
      <label>
        {label}
        <input name={name} />
      </label>
    ),
    FormNumberInput: () => null,
    FormPasswordInput: () => null,
    FormSwitch: () => null,
    LabelTooltip: () => null,
    Box: Container,
    Grid: Object.assign(Container, { Col: Container }),
    Group: Container,
    HoverCard: Object.assign(Container, { Target: Container, Dropdown: Container }),
    Input: { Label: Container, Description: Container },
    List: Object.assign(Container, { Item: Container }),
    Loader: () => null,
    Modal: () => null,
    Stack: Container,
    Typography: Container
  }
})
jest.mock('dbaas/screens/NextGen/premium/networking/private-endpoint-changefeed/Create', () => ({
  CreatePrivateEndpointForChangefeed: () => null
}))
jest.mock('dbaas/features/Common', () => ({ useOrgProjectClusterIds: () => ({}) }))
jest.mock('dbaas/features/Project', () => jest.requireActual('dbaas/features/Project/profile'))
jest.mock('dbaas/stores/useStores', () => ({ __esModule: true, default: () => ({ store: { currentCluster: null } }) }))
jest.mock('dbaas/services/api-clients/legacy/serverless', () => ({
  useMigrationServiceGetSupportedSources: () => ({})
}))
jest.mock('dbaas/services/dataflow', () => ({
  useGetDMSupportedSources: () => ({}),
  useGetPremiumDMSupportedSources: jest.fn(),
  useNgListSinkEndpoints: jest.fn()
}))

const sources = {
  sources: [{ source_type: 'MYSQL', display_name: 'MySQL', conn_methods: ['PUBLIC', 'PRIVATELINK'] }]
}

function Form({
  plan,
  method = 'PRIVATELINK',
  onSubmit
}: {
  plan: 'Premium' | 'Premium_Reserved' | 'Essential_V2'
  method?: PremiumStep1FormData['connect_method']
  onSubmit: (profile: ReturnType<typeof buildPremiumConnProfile>) => void
}) {
  const tidb = getSharedTiDB({
    servicePlan: plan,
    tidbId: 'tidb-test',
    displayName: 'Test',
    maxRcu: '4000',
    regionId: 'aws-us-west-2'
  })
  const form = useForm<PremiumStep1FormData>({
    defaultValues: { data_source: 'MYSQL', connect_method: method, port: 3306, username: 'user', password: 'password' }
  })
  return (
    <SharedTiDBContext.Provider value={tidb}>
      <FormProvider {...form}>
        <FormSourceConnection />
        <button onClick={() => onSubmit(buildPremiumConnProfile(form.getValues()))}>Submit</button>
      </FormProvider>
    </SharedTiDBContext.Provider>
  )
}

beforeEach(() => {
  jest.clearAllMocks()
  jest.mocked(useGetPremiumDMSupportedSources).mockImplementation(
    (_id, options) =>
      ({
        data: options?.query?.enabled ? sources : undefined
      }) as any
  )
  jest.mocked(useNgListSinkEndpoints).mockReturnValue({
    data: { sink_endpoints: [{ id: 'endpoint-1', endpoint_name: 'My source' }] },
    refetch: jest.fn()
  } as any)
})

it.each(['Premium', 'Premium_Reserved', 'Essential_V2'] as const)(
  '%s loads sources and submits the selected private endpoint without a legacy cluster',
  (plan) => {
    const onSubmit = jest.fn()
    render(<Form plan={plan} onSubmit={onSubmit} />)
    expect(screen.getByRole('option', { name: 'MySQL' })).not.toBeNull()
    expect(useNgListSinkEndpoints).toHaveBeenLastCalledWith(
      'tidb-test',
      { status: 'SINK_ENDPOINT_STATUS_AVAILABLE' },
      expect.objectContaining({ query: expect.objectContaining({ enabled: true }) })
    )
    expect(screen.queryByLabelText('Endpoint Service Name')).toBeNull()
    fireEvent.change(screen.getByLabelText('Private Endpoint'), { target: { value: 'endpoint-1' } })
    fireEvent.click(screen.getByText('Submit'))
    expect(onSubmit).toHaveBeenCalledWith({
      conn_type: 'PRIVATELINK',
      port: 3306,
      user: 'user',
      password: 'password',
      sink_endpoint_id: 'endpoint-1'
    })
  }
)

it('keeps public connections out of the private endpoint query', () => {
  render(<Form plan="Premium_Reserved" method="PUBLIC" onSubmit={jest.fn()} />)
  expect(screen.getByLabelText('Hostname or IP address')).not.toBeNull()
  expect(screen.queryByLabelText('Private Endpoint')).toBeNull()
  expect(useNgListSinkEndpoints).toHaveBeenLastCalledWith(
    'tidb-test',
    expect.anything(),
    expect.objectContaining({ query: expect.objectContaining({ enabled: false }) })
  )
})
