import { TextFileUploadProps } from '@tidb-cloud/ui-components'
import { Alert, Button, Card, Divider, Group, Stack } from '@tidbcloud/uikit'
import { IconAlertCircle } from '@tidbcloud/uikit/icons'
import { pickBy } from 'lodash-es'
import { observer } from 'mobx-react'
import { useMemo, useRef, useState } from 'react'
import { FormProvider, useForm } from 'react-hook-form'

import { FormErrorMessage } from 'dbaas/components/Form'
import { useProgressModal } from 'dbaas/screens/DataImportV2/useProgressModal'
import type { FormData } from 'dbaas/screens/DataMigration/CreateDataMigrationDedicated'
import { validateClusterConnectivity, validateRemoteDBConnectivity } from 'dbaas/services/dataflow'
import { ValidateRemoteDBConnectivityBodyBody } from 'dbaas/services/dataflow/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { eventTracking } from 'dbaas/utils/tracking'

import ConnectPreparationModal from './ConnectPreparationModal'
import { FormJobName } from './FormJobName'
import { FormSourceConnection } from './FormSourceConnection'
import { FormTargetConnection } from './FormTargetConnection'
import { useGetIpWhiteList } from './helper'
import PrivateLinkCheck from './PrivateLinkCheck'

export interface Step1FormFields {
  name: string
  data_source: string
  connect_method: string
  connect_method_display_name: string
  host?: string
  service_endpoint?: string
  port: number
  ssl: boolean
  username: string
  password: string
  cluster_username: string
  cluster_password: string
  ssl_ca?: string
  ssl_cert?: string
  ssl_key?: string
  ssl_ca_value?: TextFileUploadProps['value']
  ssl_cert_value?: TextFileUploadProps['value']
  ssl_key_value?: TextFileUploadProps['value']
}

const Step1 = ({
  onCancel,
  onFinish,
  formData
}: {
  formData: FormData
  onFinish: (data?: any, next?: boolean) => void
  onCancel: () => void
}) => {
  const {
    store: { tenantData, activeProjectId, currentCluster }
  } = useStores()
  const [confirmOpen, setConfirmOpen] = useState(false)
  const [privateLinkOpen, setPrivateLinkOpen] = useState(false)
  const [loading, setLoading] = useState(false)
  const method = useForm<Step1FormFields>({
    shouldUnregister: true,
    defaultValues: {
      name: formData.name || `migration_job_${Math.floor(Date.now() / 1000)}`,
      data_source: formData.data_source || '',
      connect_method: formData.connect_method || '',
      connect_method_display_name: formData.connect_method_display_name || '',
      host: formData.host || '',
      service_endpoint: formData.service_endpoint || '',
      port: formData.port || 3306,
      ssl: formData.ssl ?? true,
      ssl_ca: formData.ssl_ca_value?.content ?? '',
      ssl_cert: formData.ssl_cert_value?.content ?? '',
      ssl_key: formData.ssl_key_value?.content ?? '',
      ssl_ca_value: formData.ssl_ca_value,
      ssl_cert_value: formData.ssl_cert_value,
      ssl_key_value: formData.ssl_key_value,
      username: formData.username || '',
      password: formData.password || '',
      cluster_username: formData.cluster_username || '',
      cluster_password: formData.cluster_password || ''
    }
  })
  const { watch, handleSubmit, getValues } = method
  const source = watch('data_source')
  const connMethod = watch('connect_method') ?? ''
  const serviceEndpoint = watch('service_endpoint')

  const isPrivateConnect = useMemo(() => connMethod.toLowerCase().includes('private'), [connMethod])
  const isAzureMysql = useMemo(() => source?.toLowerCase().includes('azure') ?? false, [source])
  const ipWhitelist = useGetIpWhiteList({
    connMethod,
    isDedicated: !!currentCluster?.computed.isDedicated,
    isEssential: !!currentCluster?.computed.isEssential
  })

  const abortControllerRef = useRef<AbortController>()
  const [errorMessage, setErrorMessage] = useState('')
  const [validateConnectionError, setValidateConnectionError] = useState<{
    target: string
    source: string
  }>({
    target: '',
    source: ''
  })
  const progress = useProgressModal({
    error:
      errorMessage || [validateConnectionError.source, validateConnectionError.target].filter(Boolean).join('<br/>'),
    title: 'Validating',
    onCancel: () => abortControllerRef.current?.abort()
  })

  const onSubmit = async (data: Step1FormFields) => {
    eventTracking('Validate DataMigration job connections started', {
      connect_method: data.connect_method_display_name
    })
    if (data.ssl) {
      if (!data.ssl_ca_value && !data.ssl_cert_value && !data.ssl_key_value) {
        setErrorMessage('SSL certificate is needed if you use SSL.')
        return
      }

      if ((data.ssl_cert_value && !data.ssl_key_value) || (!data.ssl_cert_value && data.ssl_key_value)) {
        setErrorMessage('Client certificate and Client private key should be provided at the same time.')
        return
      }
    }

    if (!isPrivateConnect) {
      setConfirmOpen(true)
      return
    }

    // For private connect, open the PrivateLinkCheck modal to handle creation/polling
    setPrivateLinkOpen(true)
  }

  const onDone = async () => {
    setConfirmOpen(false)
    const data = getValues()
    setErrorMessage('')
    progress.start()
    const controller = new AbortController()
    abortControllerRef.current = controller
    const errors: string[] = []

    const requiredPayload: ValidateRemoteDBConnectivityBodyBody = {
      type: data.data_source,
      conn_method: data.connect_method,
      port: data.port,
      user: data.username,
      password: data.password
    }

    const optionalPayload = pickBy(
      {
        host: data.host,
        endpoint_service_name: data.service_endpoint,
        ssl_ca: data.ssl_ca,
        ssl_cert: data.ssl_cert,
        ssl_key: data.ssl_key
      },
      Boolean
    ) as Partial<ValidateRemoteDBConnectivityBodyBody>

    const payload: ValidateRemoteDBConnectivityBodyBody = { ...requiredPayload, ...optionalPayload }

    const results = await Promise.allSettled([
      validateClusterConnectivity(
        tenantData.id,
        activeProjectId,
        currentCluster?.id!,
        {
          user: data.cluster_username,
          password: data.cluster_password
        },
        { signal: controller.signal, timeout: 60 * 1000 }
      ),
      validateRemoteDBConnectivity(tenantData.id, activeProjectId, currentCluster?.id!, payload, {
        signal: controller.signal,
        timeout: 60 * 1000
      })
    ])

    if (results[0].status === 'rejected') {
      const reason = getErrorMessage(results[0].reason, true)
      errors.push(reason)
      setValidateConnectionError((prev) => ({
        ...prev,
        source: reason
      }))
    }
    if (results[1].status === 'rejected') {
      const reason = getErrorMessage(results[1].reason, true)
      errors.push(reason)
      setValidateConnectionError((prev) => ({
        ...prev,
        target: reason
      }))
    }

    setLoading(false)

    if (errors.length > 0) {
      eventTracking('Validating DataMigration job connections failed', {
        connect_method: data.connect_method_display_name
      })
      progress.pause()
    } else {
      eventTracking('Validating DataMigration job connections succeed', {
        connect_method: data.connect_method_display_name
      })
      progress.done()
      onFinish(data)
    }
  }

  return (
    <FormProvider {...method}>
      <Card p={0}>
        <form onSubmit={handleSubmit(onSubmit)}>
          <FormJobName />

          <Divider />

          <FormSourceConnection />

          <Divider />

          <FormTargetConnection
            region={currentCluster?.regionDisplayName!}
            id={currentCluster?.id!}
            name={currentCluster?.displayName!}
            cluster={currentCluster}
          />

          <Divider />

          <Stack m={24}>
            {!progress.isOpen && <FormErrorMessage message={errorMessage} autoScroll />}

            {validateConnectionError.target && (
              <Alert title="Connect to target failed" icon={<IconAlertCircle />} color="red">
                {validateConnectionError.target}
              </Alert>
            )}

            {validateConnectionError.source && (
              <Alert title="Connect to source failed" icon={<IconAlertCircle />} color="red">
                {validateConnectionError.source}
              </Alert>
            )}

            <Group justify="flex-end">
              <Button
                variant="default"
                type="button"
                onClick={onCancel}
                data-mp-event="Cancel on Data Migration Creation Step 1 Clicked"
              >
                Cancel
              </Button>
              <Button type="submit" loading={loading} data-mp-event="Submit on Data Migration Creation Step 1 Clicked">
                Validate Connection and Next
              </Button>
            </Group>
          </Stack>
        </form>

        {progress.content}

        <PrivateLinkCheck
          opened={privateLinkOpen}
          onClose={() => setPrivateLinkOpen(false)}
          onAvailable={onDone}
          serviceName={serviceEndpoint}
          isAzureMysql={isAzureMysql}
          onError={setErrorMessage}
        />

        <ConnectPreparationModal
          opened={confirmOpen}
          onClose={() => setConfirmOpen(false)}
          onContinue={onDone}
          ipWhitelist={ipWhitelist}
        />
      </Card>
    </FormProvider>
  )
}

export const DedicatedDMStep1 = observer(Step1)
