import {
  Alert,
  Box,
  Button,
  Card,
  Divider,
  Group,
  PasswordInput,
  Stack,
  Tabs,
  TextInput,
  Typography
} from '@tidbcloud/uikit'
import { IconAlertCircle } from '@tidbcloud/uikit/icons'
import { useMemoizedFn } from 'ahooks'
import { useRef, useState } from 'react'
import { Controller, useFormContext } from 'react-hook-form'
import { useQuery } from 'react-query'

import { areValidEventFilters, useIsEditingChangefeed } from 'dbaas/features/Changefeed'
import { ColumnSelector } from 'dbaas/screens/Changefeed/CreateOrEditChangefeed/step2/ColumnSelector'
import { EventFilter } from 'dbaas/screens/Changefeed/CreateOrEditChangefeed/step2/EventFilter'
import { FormKafka } from 'dbaas/screens/Changefeed/CreateOrEditChangefeedPremium/step2/FormKafka'
import { FormMySQL } from 'dbaas/screens/Changefeed/CreateOrEditChangefeedPremium/step2/FormMySQL'
import { FormPulsar } from 'dbaas/screens/Changefeed/CreateOrEditChangefeedPremium/step2/FormPulsar'
import { FormS3OrGCS } from 'dbaas/screens/Changefeed/CreateOrEditChangefeedPremium/step2/FormS3OrGCS'
import { FormTiDBServerless } from 'dbaas/screens/Changefeed/CreateOrEditChangefeedPremium/step2/FormTiDBServerless'
import { SharedTiDB } from 'dbaas/screens/NextGen/shared/model/shared-tidb-state'
import { ngDescribeSchemaTable, useNgTestChangefeed } from 'dbaas/services/dataflow'
import {
  DataflowColumnSelector,
  DataflowConnectorType,
  DataflowEventFilterRule
} from 'dbaas/services/dataflow/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import { eventTracking } from 'dbaas/utils/tracking'

import { isTestEnv } from '../helper'
import type { CreateOrEditChangefeedFormData } from '../types'

import { buildStep2Payload } from './payloadBuiders'
import TableFilterV2 from './TableFilterV2'

export const ConfigureReplica = ({
  onFinish,
  cluster,
  onCancel,
  onGoPrev
}: {
  cluster: SharedTiDB
  onFinish: () => void
  onGoPrev: () => void
  onCancel: () => void
}) => {
  const methods = useFormContext<CreateOrEditChangefeedFormData>()
  const formData = methods.getValues() as unknown as CreateOrEditChangefeedFormData
  const containerRef = useRef<HTMLDivElement>(null)
  const [eventFilterValidateError, setEventFilterValidateError] = useState(false)
  const [columnSelectorsValidateError, setColumnSelectorsValidateError] = useState(false)
  const [filterTab, setFilterTab] = useState<'table-filter' | 'event-filter' | 'column-selectors'>('table-filter')
  const { control, handleSubmit, setValue, watch, getValues } = methods
  const isEditing = useIsEditingChangefeed()
  const filterRules = watch('table_config.filter_rule') ?? []
  const columnSelectors = watch('kafka_info.column_selectors')
  const kafkaDataFormat = watch('kafka_info.data_format.protocol')
  const caseSensitive = watch('table_config.case_sensitive') ?? false
  const defaultTableConfig = getValues('table_config')
  const dbUsername = watch('db_username')
  const dbPassword = watch('db_password')
  const [verifiedCredential, setVerifiedCredential] = useState<{ username: string; password: string } | null>(null)
  const [submittedFilterRules, setSubmittedFilterRules] = useState(filterRules)
  const [submittedCaseSensitive, setSubmittedCaseSensitive] = useState(caseSensitive)
  const [verifyApplyKey, setVerifyApplyKey] = useState(0)
  const [hasVerified, setHasVerified] = useState(false)
  const {
    data: schemaTableData,
    error: getTableError,
    isFetching: isFetchingSchemaTable
  } = useQuery(
    [
      '/ng/describeSchemaTable',
      cluster.tidbId,
      submittedFilterRules,
      submittedCaseSensitive,
      verifiedCredential?.username,
      verifiedCredential?.password,
      verifyApplyKey
    ],
    () =>
      ngDescribeSchemaTable(cluster.tidbId!, {
        filter_rule: submittedFilterRules,
        case_sensitive: submittedCaseSensitive,
        username: verifiedCredential?.username,
        password: verifiedCredential?.password
      }),
    {
      enabled:
        !!verifiedCredential?.username && !!verifiedCredential?.password && !!cluster.tidbId && verifyApplyKey > 0,
      refetchOnMount: false,
      refetchOnWindowFocus: false,
      retry: false,
      onSuccess() {
        setHasVerified(true)
      }
    }
  )
  const showCredentialInput = !hasVerified
  const testChangefeedMutation = useNgTestChangefeed({
    mutation: { retry: false },
    request: { timeout: 120 * 1000 }
  })
  const error = testChangefeedMutation.error
  const onTableFilterChange = useMemoizedFn((rules: string[]) => {
    setSubmittedFilterRules(rules)
    if (hasVerified && verifiedCredential) {
      setSubmittedCaseSensitive(caseSensitive)
      setVerifyApplyKey((value) => value + 1)
    }
  })
  const onEventFilterChange = useMemoizedFn((rules: DataflowEventFilterRule[]) => {
    setValue('table_config.event_filter_rule', rules)
  })
  const onColumnSelectorsChange = useMemoizedFn((selectors: DataflowColumnSelector[]) => {
    setValue('kafka_info.column_selectors', selectors)
  })
  const onVerifyTableFilterRules = useMemoizedFn(() => {
    setVerifiedCredential({
      username: dbUsername ?? '',
      password: dbPassword ?? ''
    })
    setSubmittedFilterRules(filterRules)
    setSubmittedCaseSensitive(caseSensitive)
    setVerifyApplyKey((value) => value + 1)
  })
  const dataFormat = formData.changefeed_type === DataflowConnectorType.KAFKA ? kafkaDataFormat : undefined

  const onSubmit = async (data: CreateOrEditChangefeedFormData) => {
    const payload = buildStep2Payload(data)
    const eventFiltersRules = data.table_config.event_filter_rule

    if (eventFiltersRules && eventFiltersRules.length > 0) {
      if (areValidEventFilters(eventFiltersRules)) {
        setEventFilterValidateError(false)
      } else {
        setFilterTab('event-filter')
        setEventFilterValidateError(true)
        containerRef.current?.scrollIntoView({ behavior: 'smooth' })
        return
      }
    }

    if (payload.changefeed_type === DataflowConnectorType.KAFKA) {
      const columnSelectors = payload.kafka_info?.column_selectors
      if (columnSelectors && columnSelectors?.length > 0) {
        if (
          columnSelectors.every(
            (i) => i.matcher && i.matcher.length > 0 && i.columns && i.columns.filter(Boolean).length > 0
          )
        ) {
          setColumnSelectorsValidateError(false)
        } else {
          setFilterTab('column-selectors')
          setColumnSelectorsValidateError(true)
          containerRef.current?.scrollIntoView({ behavior: 'smooth' })
          return
        }
      }
    }

    try {
      // Skip test connection in test environment
      if (isTestEnv()) {
        console.log(payload)
        onFinish()
        return
      }

      if (!isEditing) {
        await testChangefeedMutation.mutateAsync({
          clusterId: cluster.tidbId!,
          data: payload
        })
      }

      onFinish()
    } catch (_e) {
      eventTracking('Failed when precheck in kafka changefeed', {
        'Cluster Type': cluster.computed.clusterType
      })
    }
  }

  return (
    <Card p={0}>
      <form onSubmit={handleSubmit(onSubmit)}>
        {/* scroll to here when filters are invalid */}
        <Box ref={containerRef}></Box>

        <Tabs p={24} value={filterTab} onChange={(value) => setFilterTab(value as any)}>
          <Tabs.List>
            <Tabs.Tab value="table-filter">Table Filter</Tabs.Tab>

            <Tabs.Tab value="event-filter">Event Filter</Tabs.Tab>

            {formData.changefeed_type === DataflowConnectorType.KAFKA && (
              <Tabs.Tab value="column-selectors">Column Selector</Tabs.Tab>
            )}
          </Tabs.List>

          <Tabs.Panel value="table-filter" pt="xs">
            <Stack>
              <Controller
                name="table_config.mode"
                control={control}
                render={({ field }) => <input {...field} hidden />}
              />

              <Typography variant="body-md" c="carbon.7">
                One filter rule per line, filters are applied in order. The last match takes effect. Learn how{' '}
                <a href="https://docs.pingcap.com/tidb/stable/table-filter/#syntax" target="_blank" rel="noreferrer">
                  table filter rules work
                </a>
                .
              </Typography>

              <TableFilterV2
                defaultRules={filterRules}
                tablesWithValidKeys={schemaTableData?.replicate_allow_schema_table ?? []}
                tablesWithoutValidKeys={schemaTableData?.replicate_deny_schema_table ?? []}
                onChange={onTableFilterChange}
                onRulesTextChange={(rules) => setValue('table_config.filter_rule', rules)}
                loading={isFetchingSchemaTable}
                error={getTableError}
                caseSensitiveFieldName="table_config.case_sensitive"
                showResults={!showCredentialInput}
                verificationContent={
                  <>
                    <Card.Section withBorder py={8} px={12}>
                      <Typography variant="label-md" fw={500}>
                        Table Filter Verification
                      </Typography>
                    </Card.Section>
                    <Card.Section withBorder py={8} px={12}>
                      <Stack gap="sm">
                        <Typography variant="body-md">
                          Please provide credentials with read-only access to the tables. TiDB Cloud will use them to
                          verify the filter rules and tell you which tables are selected. These credentials are used for
                          verification only and will <b>NOT</b> be stored.
                        </Typography>
                        <Group grow align="flex-start">
                          <TextInput
                            placeholder="Enter the username"
                            value={dbUsername ?? ''}
                            onChange={(e) => setValue('db_username', e.target.value)}
                          />
                          <PasswordInput
                            placeholder="Enter the password"
                            value={dbPassword ?? ''}
                            onChange={(e) => setValue('db_password', e.target.value)}
                            onKeyDown={(event) => {
                              if (event.key !== 'Enter') return

                              event.preventDefault()
                              if (dbUsername && dbPassword && !isFetchingSchemaTable) {
                                onVerifyTableFilterRules()
                              }
                            }}
                          />
                        </Group>
                        {/* {getTableError ? <FormErrorMessage message={getErrorMessage(getTableError, true)} /> : null} */}
                        <Group>
                          <Button
                            type="button"
                            variant="default"
                            size="sm"
                            onClick={onVerifyTableFilterRules}
                            loading={isFetchingSchemaTable}
                            disabled={!dbUsername || !dbPassword}
                          >
                            Verify Table Filter Rules
                          </Button>
                        </Group>
                      </Stack>
                    </Card.Section>
                  </>
                }
              />

              <Alert color="peacock" icon={<IconAlertCircle />}>
                <Typography>
                  <b>Tables without valid keys</b> in the listed tables indicate that they <b>DON'T</b> have any primary
                  keys or unique indexes. Without a unique identifier for the downstream system to handle duplicate
                  events, data consistency may be compromised during replication.
                </Typography>
              </Alert>
            </Stack>
          </Tabs.Panel>

          <Tabs.Panel value="event-filter" pt="xs">
            <EventFilter
              defaultValue={defaultTableConfig.event_filter_rule!}
              onChange={onEventFilterChange}
              validateError={eventFilterValidateError}
            />
          </Tabs.Panel>

          <Tabs.Panel value="column-selectors" pt="xs">
            <ColumnSelector
              defaultValue={columnSelectors ?? []}
              onChange={onColumnSelectorsChange}
              validateError={columnSelectorsValidateError}
            />
          </Tabs.Panel>
        </Tabs>

        <Divider />

        {formData.changefeed_type === DataflowConnectorType.KAFKA && (
          <FormKafka cluster={cluster} schemaTableData={schemaTableData} />
        )}
        {formData.changefeed_type === DataflowConnectorType.MYSQL && <FormMySQL schemaTableData={schemaTableData} />}
        {formData.changefeed_type === DataflowConnectorType.SERVERLESS_TIDB && <FormTiDBServerless />}
        {formData.changefeed_type === DataflowConnectorType.S3 && (
          <FormS3OrGCS cluster={cluster} schemaTableData={schemaTableData} path="s3_info" />
        )}
        {formData.changefeed_type === DataflowConnectorType.GCS && (
          <FormS3OrGCS cluster={cluster} schemaTableData={schemaTableData} path="gcs_info" />
        )}
        {formData.changefeed_type === DataflowConnectorType.PULSAR && <FormPulsar />}

        {error && (
          <Alert icon={<IconAlertCircle />} color="red" m={24}>
            <span dangerouslySetInnerHTML={{ __html: getErrorMessage(error) }} />
          </Alert>
        )}

        <Group m={24} justify="flex-end" wrap="nowrap">
          <Group>
            <Button
              variant="default"
              type="button"
              data-mp-event="Configure Replica Cancel Button Clicked"
              onClick={onCancel}
            >
              Cancel
            </Button>
            <Button
              variant="outline"
              type="button"
              data-mp-event="Configure Replica Previous Button Clicked"
              onClick={onGoPrev}
            >
              Previous
            </Button>
            <Button
              type="submit"
              loading={testChangefeedMutation.isLoading}
              data-mp-event="Configure Replica Next Button Clicked"
              data-mp-plan="Premium"
              data-mp-changefeed-type={formData.changefeed_type}
              data-mp-format={dataFormat}
            >
              Next
            </Button>
          </Group>
        </Group>
      </form>
    </Card>
  )
}
