import { Box, Button, Center, Divider, Flex, Group, Typography, notifier } from '@tidbcloud/uikit'
import { Form, FormActions, FormErrorMessage, FormSelect, FormTextInput } from '@tidbcloud/uikit/biz'
import { useMemoizedFn } from 'ahooks'
import { observer } from 'mobx-react'
import { useState } from 'react'
import { useForm, useWatch } from 'react-hook-form'

import { useAccessCheck } from 'dbaas/contexts/access-control'
import { PageShell } from 'dbaas/layouts/v5/page'
import { useCreateConsoleApiKey, useUpdateConsoleApiKey } from 'dbaas/services/account'
import { CentralConsoleApiKey, CreateConsoleApiKeyBodyBody } from 'dbaas/services/account/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'

import { AdvancedRolesSelect, orgRoleOptions, renderRoleOption } from '../../users/shared/roles'
import { APIKeyItem, APIKeyOrgRoles } from '../shared/helpers'
import { mpEvents } from '../tracker'

import Result from './Result'

enum EditorStep {
  Form = 'form',
  Result = 'result'
}

interface APIKeyEditorProps {
  onClose: () => void
  onFinish?: () => void
  data?: CentralConsoleApiKey
}

const APIKeyEditor = observer(({ onClose, onFinish, data }: APIKeyEditorProps) => {
  const isUpdate = !!data
  const {
    store: {
      tenantData: { id: orgId }
    }
  } = useStores()

  const accessible = useAccessCheck('OrgAPIKeysV2Action')

  // TODO: Update when API interface is ready
  const projectRoles = (data as any)?.project_roles
  const clusterRoles = (data as any)?.cluster_roles
  const [step, setStep] = useState<EditorStep>(EditorStep.Form)
  const [result, setResult] = useState<APIKeyItem | undefined>()

  const {
    mutateAsync: createAPIKey,
    isLoading: isCreating,
    isError: isCreateError,
    error: createError,
    reset: resetCreate
  } = useCreateConsoleApiKey({
    mutation: {
      onSuccess: (data) => {
        if (!data) {
          notifier.error('Fail to create API Key, please try again later.')
          return
        }
        setResult(data)
        setStep(EditorStep.Result)
        onFinish?.()
      }
    }
  })

  const {
    mutateAsync: updateAPIKey,
    isLoading: isUpdating,
    isError: isUpdateError,
    error: updateError,
    reset: resetUpdate
  } = useUpdateConsoleApiKey()

  const isLoading = isCreating || isUpdating
  const isError = isCreateError || isUpdateError
  const error = createError || updateError
  const reset = () => {
    resetCreate()
    resetUpdate()
  }

  const form = useForm<CentralConsoleApiKey>({
    defaultValues: {
      description: data?.description || '',
      org_role: data?.org_role ?? {
        rbac_role: APIKeyOrgRoles.OrgOwner
      },
      project_roles: projectRoles ?? [],
      cluster_roles: clusterRoles ?? []
    }
  })

  const orgRole = form.watch('org_role.rbac_role')
  const watchedProjectRoles = useWatch({ control: form.control, name: 'project_roles' })
  const watchedClusterRoles = useWatch({ control: form.control, name: 'cluster_roles' })
  const totalRoles = 1 + (watchedProjectRoles?.length || 0) + (watchedClusterRoles?.length || 0)

  const withAdvancedRoles = orgRole !== APIKeyOrgRoles.OrgOwner

  const onSubmit = useMemoizedFn(async (values: CentralConsoleApiKey) => {
    const projectRoles = withAdvancedRoles ? values.project_roles : undefined
    const clusterRoles = withAdvancedRoles ? values.cluster_roles : undefined
    if (isUpdate) {
      // TODO: Update API interface when backend is ready
      const updateData: CreateConsoleApiKeyBodyBody = {
        description: values.description,
        org_role: values.org_role?.rbac_role,
        api_key_id: data?.id,
        project_roles: projectRoles,
        cluster_roles: clusterRoles
      }

      await updateAPIKey({
        orgId: orgId,
        data: updateData
      })
      onFinish?.()
      onClose()
    } else {
      // TODO: Update API interface when backend is ready
      const createData: CreateConsoleApiKeyBodyBody = {
        description: values.description,
        org_role: values.org_role?.rbac_role,
        project_roles: projectRoles,
        cluster_roles: clusterRoles
      }

      await createAPIKey({
        orgId,
        data: createData
      })
      // success handled in onSuccess
    }
  })

  return (
    <PageShell
      title={isUpdate ? 'Update Role' : 'Create API Key'}
      headerProps={{
        withBack: true,
        onBackClick: onClose
      }}
    >
      {step === EditorStep.Form ? (
        <Center mt={24}>
          <Form<CentralConsoleApiKey>
            w={1000}
            layout="none"
            form={form}
            onSubmit={onSubmit}
            onCancel={onClose}
            withActions={false}
          >
            <Flex
              direction="column"
              px={24}
              py={24}
              sx={(theme) => ({ borderRadius: theme.defaultRadius, border: `1px solid ${theme.colors.carbon[4]}` })}
            >
              {isUpdate && (
                <>
                  <Typography variant="title-lg">{data?.public_key}</Typography>
                  <Divider mx="-xl" my="xl" />
                </>
              )}

              <FormTextInput
                name="description"
                label="Description"
                labelProps={{
                  fz: 16,
                  fw: 700
                }}
                maxLength={255}
                rules={{
                  required: 'Please enter a description',
                  maxLength: {
                    value: 255,
                    message: 'Description must be 1~255 characters'
                  },
                  validate: (value: string) => {
                    if (value.trim().length < value.length) {
                      return 'Description cannot start or end with blank characters.'
                    }
                  }
                }}
                data-autofocus
              />
              <Divider mx={-24} my="xl" />
              <Flex justify="space-between" align="center" mb={16}>
                <Typography variant="title-md">Add Role and scope</Typography>
                <Typography variant="body-md" c="carbon.7" fw={500}>
                  {totalRoles} {totalRoles === 1 ? 'role' : 'roles'}
                </Typography>
              </Flex>
              <Flex direction="column" gap={16}>
                <Box>
                  <Typography variant="label-lg" mb="sm" c="carbon.8" fw={700}>
                    Organization Access
                  </Typography>
                  <FormSelect
                    name="org_role.rbac_role"
                    data={orgRoleOptions}
                    h={40}
                    styles={{ input: { height: 40, minHeight: 40 } }}
                    renderOption={renderRoleOption}
                  />
                </Box>
                {withAdvancedRoles && (
                  <>
                    <Divider />
                    <AdvancedRolesSelect />
                  </>
                )}
              </Flex>
              {isError && <FormErrorMessage mt={16} message={getErrorMessage(error)} onDismiss={() => reset()} />}
              <Divider mx={-24} mt="xl" mb="md" />
              <FormActions
                mt={0}
                mb={-8}
                onCancel={onClose}
                loading={isLoading}
                confirmText={isUpdate ? 'Update' : 'Generate API Key'}
                confirmProps={{
                  'data-mp-event': isUpdate ? mpEvents.finishEdit : mpEvents.startCreation,
                  disabled: isUpdate ? !accessible : undefined
                }}
                cancelProps={{
                  'data-mp-event': isUpdate ? mpEvents.finishEdit : mpEvents.startCreation
                }}
              />
            </Flex>
          </Form>
        </Center>
      ) : (
        <Center mt={24}>
          <Flex
            direction="column"
            w={1000}
            px={24}
            py={24}
            sx={(theme) => ({ borderRadius: theme.defaultRadius, border: `1px solid ${theme.colors.carbon[4]}` })}
          >
            <Result data={result} />
            <Divider mx={-24} mt={24} />
            <Group justify="flex-end" mt={16} mb={-8}>
              <Button onClick={onClose} data-mp-event={mpEvents.finishCreation}>
                Done
              </Button>
            </Group>
          </Flex>
        </Center>
      )}
    </PageShell>
  )
})

export default APIKeyEditor
