import { notifier } from '@tidbcloud/uikit'
import { Modal, Button, Switch, SwitchProps, LoadingOverlay, Typography, Flex, Group, Box } from '@tidbcloud/uikit'
import { Form, FormTextInput, FormSelect } from '@tidbcloud/uikit/biz'
import { IconRefreshCw01 } from '@tidbcloud/uikit/icons'
import { observer } from 'mobx-react'
import React, { useCallback, useEffect, useState } from 'react'
import { useForm, useFormContext } from 'react-hook-form'

import cfg from 'config'
import { ActionSteps, SubmitAction } from 'dbaas/components/IntegrationCard'
import { Regions } from 'dbaas/screens/DataImportV2/constant'
import type { CentralAuditLogConfig, CentralGetAuditLogStatusResp } from 'dbaas/services'
import {
  clusterServiceGetAuditLogStatus,
  clusterServicePrecheckAuditLogConfig,
  clusterServiceUpdateAuditLogStatus
} from 'dbaas/services'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { getClusterType } from 'dbaas/utils/devtier'

import { AccessTable } from './AccessTable'
import { IAM } from './iam'

interface AuditLoggingProps {
  provider: string
  clusterId: string
  isDevTier?: boolean
  region: string
  disabled?: boolean
  visible: boolean
  onClose: () => void
}

enum DataSouceType {
  AWS = 1,
  GCS = 2
}

const DirtyChecker: React.FC<
  React.PropsWithChildren<{
    onChange: (isDirty: boolean) => void
  }>
> = ({ onChange }) => {
  const {
    formState: { isDirty }
  } = useFormContext()

  useEffect(() => {
    onChange(isDirty)
  }, [isDirty, onChange])
  return <></>
}

export const AuditLogging: React.FC<React.PropsWithChildren<AuditLoggingProps>> = observer(
  ({ provider, clusterId, isDevTier, region, visible, onClose: onModalClose }) => {
    const {
      store: { tenantData, activeProjectId }
    } = useStores()
    const defaultSettings: CentralAuditLogConfig = {
      data_source_type: provider === 'aws' ? DataSouceType.AWS : DataSouceType.GCS,
      region,
      role_arn: '',
      bucket_url: ''
    }

    const [testing, setTesting] = useState(false)
    const [action, setAction] = useState<'initial' | 'switching' | 'switched' | 'restarting' | 'restarted'>('initial')
    const [step, setStep] = useState<ActionSteps>('initial')
    const [isDirty, setIsDirty] = useState(false)
    const [auditStatus, setAuditStatus] = useState<CentralGetAuditLogStatusResp>({
      enabled: false,
      audit_log_config: { ...defaultSettings }
    })
    const [updateError, setUpdateError] = useState('')
    const [loaded, setLoaded] = useState(false)

    const readOnly = step !== 'initial'

    const form = useForm<CentralAuditLogConfig>({
      mode: 'onChange',
      reValidateMode: 'onChange',
      defaultValues: {
        bucket_url: auditStatus.audit_log_config.bucket_url,
        region: auditStatus.audit_log_config.region,
        role_arn: auditStatus.audit_log_config.role_arn
      }
    })

    const onSubmit = async (data: CentralAuditLogConfig) => {
      if (testing) {
        return
      }
      if (step !== 'initial') {
        return
      }
      setTesting(true)
      setUpdateError('')

      if (step === 'initial') {
        try {
          await clusterServicePrecheckAuditLogConfig(
            tenantData.id,
            activeProjectId,
            clusterId,
            {
              org_id: tenantData.id,
              project_id: activeProjectId,
              cluster_id: clusterId,
              audit_log_config: {
                ...data,
                data_source_type: auditStatus.audit_log_config.data_source_type
              }
            },
            {
              timeout: cfg.AUDIT_LOG_TIMEOUT
            }
          )
          setTesting(false)
          setStep('passed')
        } catch (e) {
          setTesting(false)
          setUpdateError(getErrorMessage(e as Error, true))
        }
      }
    }

    const updateAuditLog = (config: CentralAuditLogConfig, enabled: boolean, restart: boolean) => {
      setUpdateError('')
      return clusterServiceUpdateAuditLogStatus(
        tenantData.id,
        activeProjectId,
        clusterId,
        {
          org_id: tenantData.id,
          project_id: activeProjectId,
          cluster_id: clusterId,
          enabled,
          restart,
          audit_log_config: {
            ...config,
            data_source_type: auditStatus.audit_log_config.data_source_type
          }
        },
        {
          timeout: cfg.AUDIT_LOG_TIMEOUT
        }
      )
        .then((data) => {
          setAuditStatus({ ...auditStatus, enabled: data.enabled })
        })
        .catch((e) => {
          const msg = getErrorMessage(e, true)
          setUpdateError(msg)
          throw e
        })
    }

    const fetcher = useCallback(() => {
      clusterServiceGetAuditLogStatus(tenantData.id, activeProjectId, clusterId, {
        timeout: cfg.AUDIT_LOG_TIMEOUT
      })
        .then((data) => {
          const config = data.audit_log_config ? data.audit_log_config : { ...defaultSettings }
          setAuditStatus({
            enabled: data.enabled,
            audit_log_config: config
          })
          form.setValue('bucket_url', config.bucket_url)
          form.setValue('region', config.region)
          form.setValue('role_arn', config.role_arn)
          if (config.bucket_url) {
            form.trigger()
          }
          setTimeout(() => {
            setLoaded(true)
          }, 0)
        })
        .catch((_e) => {
          setLoaded(true)
        })
      // eslint-disable-next-line
    }, [activeProjectId, tenantData.id, clusterId, loaded])

    const onClose = () => {
      setLoaded(false)
      setStep('initial')
      onModalClose?.()
    }

    const onSwitch: SwitchProps['onChange'] = async (e) => {
      const checked = e.currentTarget.checked
      const data = form.getValues()
      if (!data) {
        return
      }
      setAction('switching')
      // turn on
      if (checked) {
        try {
          await updateAuditLog(data, true, false)
          notifier.info('Audit Logging is turned on.')
          setAction('switched')
        } catch (_e) {
          setAction('switched')
        }
      } else {
        // turn off
        try {
          await updateAuditLog(data, false, false)
          notifier.info('Audit Logging is turned off.')
          setAction('switched')
        } catch (_e) {
          setAction('switched')
        }
      }
    }

    const onRestart = async () => {
      const data = form.getValues()
      if (!data) {
        return
      }
      try {
        setAction('restarting')
        await updateAuditLog(data, true, true)
        notifier.info('Audit Logging is restarted.')
        setAction('restarted')
      } catch (_e) {
        setAction('restarted')
      }
    }

    useEffect(() => {
      if (visible) {
        fetcher()
        setUpdateError('')
      }
    }, [fetcher, visible])

    return (
      <Modal
        size="1000"
        styles={{ title: { width: '100%' }, body: { maxWidth: 1000, minWidth: 1000 } }}
        centered
        opened={visible}
        onClose={onClose}
        title="Audit Logging"
      >
        <Box mih={600}>
          <LoadingOverlay visible={!loaded} />
          <Flex justify="space-between" align="center" mb={16}>
            <Typography>
              Refer to the{' '}
              <a
                href="https://docs.pingcap.com/tidbcloud/tidb-cloud-auditing"
                target="_blank"
                rel="noreferrer"
                data-mp-event="Documentation Link Clicked"
                data-mp-source="Audit Log"
                data-mp-content="about user access and history"
              >
                documentation
              </a>{' '}
              to learn about user access and history audit logging.{' '}
            </Typography>
            <Flex align="center">
              <Switch
                size="lg"
                onLabel="ON"
                offLabel="OFF"
                disabled={
                  action === 'switching' || action === 'restarting'
                    ? true
                    : testing
                      ? true
                      : auditStatus.enabled
                        ? isDirty && step !== 'passed'
                        : step !== 'passed'
                }
                // loading={action === 'switching'}
                onChange={onSwitch}
                checked={auditStatus.enabled}
                mr={16}
              />
              <Button
                disabled={
                  action === 'restarting' || action === 'switching'
                    ? true
                    : testing
                      ? true
                      : auditStatus.enabled
                        ? isDirty && step !== 'passed'
                        : step !== 'passed'
                }
                loading={action === 'restarting'}
                data-mp-event="Audit Restart Button Clicked"
                data-mp-cluster_type={getClusterType(isDevTier)}
                onClick={onRestart}
                leftSection={<IconRefreshCw01 size={16} />}
              >
                Restart
              </Button>
            </Flex>
          </Flex>
          <Form<CentralAuditLogConfig> form={form} onSubmit={onSubmit} withActions={false}>
            <Group justify="space-between" align="flex-start" grow gap={16}>
              <FormTextInput
                name="bucket_url"
                placeholder={`Example: ${provider === 'gcp' ? 'gcs' : 's3'}://bucket-name/path/to/dump/`}
                label="Bucket URI"
                withAsterisk
                readOnly={readOnly}
                rules={{ required: 'Required' }}
              />
              <FormSelect
                name="region"
                label="Bucket Region"
                placeholder="Select a region"
                required
                data={Regions.map((it) => ({ label: it.text, value: it.value }))}
                disabled={readOnly}
                rules={{ required: 'Required' }}
              />
            </Group>
            <IAM provider={provider} clusterId={clusterId} readOnly={readOnly}>
              <SubmitAction
                loading={testing}
                step={step}
                onCancel={() => onClose()}
                disabled={step !== 'initial'}
                stepTexts={{
                  initial: 'Test Connectivity',
                  confirmed: 'Pass',
                  passed: 'Pass'
                }}
              />
            </IAM>
            {!!updateError && (
              <Form.ErrorMessage message={updateError} onDismiss={() => setUpdateError('')} mb={16}></Form.ErrorMessage>
            )}
            <DirtyChecker onChange={(isDirty) => setIsDirty(isDirty)} />
          </Form>
          <AccessTable clusterId={clusterId} />
        </Box>
      </Modal>
    )
  }
)

export default AuditLogging
