import {
  ActionIcon,
  Box,
  Button,
  Divider,
  Flex,
  HoverCard,
  Modal,
  MultiSelectProps,
  Tooltip,
  Typography,
  useMantineTheme
} from '@tidbcloud/uikit'
import { Form, FormPasswordInput, FormSelect, FormTextInput, FormMultiSelect } from '@tidbcloud/uikit/biz'
import { useClipboard } from '@tidbcloud/uikit/hooks'
import { IconInfoCircle, IconStar06, IconCopy03 } from '@tidbcloud/uikit/icons'
import generator from 'generate-password'
import { observer } from 'mobx-react'
import { useState } from 'react'
import { useForm, useFormContext } from 'react-hook-form'

import { createSqlUser, useQuerySqlUser } from 'dbaas/services/account'
import { CreateSqlUserBody, UpdateSqlUserBody } from 'dbaas/services/account/interface'
import useStores from 'dbaas/stores/useStores'

export const SQL_USER_AUTH_METHODS = {
  PASSWORD: 'mysql_native_password',
  LDAP: 'ldap'
} as const

export const SQL_USER_AUTH_METHOD_LABELS = {
  [SQL_USER_AUTH_METHODS.PASSWORD]: 'Password',
  [SQL_USER_AUTH_METHODS.LDAP]: 'LDAP'
}

export const SQL_USER_BUIlTIN_ROLES = {
  DBROLE_ADMIN: 'role_admin',
  DBROLE_READWRITE: 'role_readwrite',
  DBROLE_READONLY: 'role_readonly'
} as const

export const SQL_USER_BUIlTIN_ROLES_LABELS = {
  [SQL_USER_BUIlTIN_ROLES.DBROLE_ADMIN]: 'Database Admin',
  [SQL_USER_BUIlTIN_ROLES.DBROLE_READWRITE]: 'Database Read-Write',
  [SQL_USER_BUIlTIN_ROLES.DBROLE_READONLY]: 'Database Read-Only'
}

export const SQL_USER_BUIlTIN_ROLES_OPTIONS = Object.entries(SQL_USER_BUIlTIN_ROLES_LABELS).map((label) => ({
  label: label[1],
  value: label[0]
}))

const GeneratePassword: React.FC<React.PropsWithChildren<{ name: string }>> = ({ name }) => {
  const { setValue, getValues } = useFormContext()
  const clipboard = useClipboard({ timeout: 500 })

  const generatePassword = () => {
    const newPassword = generator.generate({
      length: 16,
      numbers: true,
      strict: true
    })
    setValue(name, newPassword, { shouldDirty: true })
  }

  const copy = () => {
    clipboard.copy(getValues(name))
  }

  return (
    <Flex>
      <Button variant="default" leftSection={<IconStar06 size={16} />} h={40} fw={600} onClick={generatePassword}>
        Autogenerate Secure Password
      </Button>
      <Tooltip label={clipboard.copied ? 'Copied' : 'Copy'} withArrow position="right">
        <ActionIcon aria-label="Copy password" variant="default" size={40} ml={16} bg="carbon.2" onClick={copy}>
          <IconCopy03 size={20} />
        </ActionIcon>
      </Tooltip>
    </Flex>
  )
}

type ActionType = 'add' | 'update'

interface SQLUserFormProps {
  type: ActionType
  namePrefix?: string
  entityName?: 'cluster' | 'instance'
  defaultValues?: UpdateSqlUserBody
  onSubmit: (data: UpdateSqlUserBody) => Promise<void>
  onCancel?: () => void
  clusterId: string
  isRoot?: boolean
}

const SQLUserForm: React.FC<React.PropsWithChildren<SQLUserFormProps>> = observer(
  ({ type, onSubmit, onCancel, namePrefix, entityName = 'cluster', defaultValues, clusterId, isRoot }) => {
    const {
      store: { tenantData, activeProjectId }
    } = useStores()
    const theme = useMantineTheme()

    const form = useForm({
      reValidateMode: 'onChange',
      defaultValues: defaultValues || { user_name: '', custom_role: [], builtin_role: '', password: '' }
    })

    const isEditing = type === 'update'

    const { formState, watch } = form
    const { isDirty } = formState

    const confirmText = isEditing ? 'Update' : 'Create'

    const { data: roles } = useQuerySqlUser(tenantData.id, activeProjectId, clusterId, {
      'filter.query_type': 'ROLE'
    })

    const customRoleOptions: MultiSelectProps['data'] = roles?.roles?.map((r) => ({ label: r, value: r })) || []

    const password = watch('password')

    return (
      <Form
        form={form}
        onSubmit={onSubmit}
        onCancel={onCancel}
        layoutProps={{ gap: 24 }}
        actionsProps={{
          confirmText: isEditing ? 'Update' : 'Create',
          confirmProps: {
            disabled: isEditing ? !isDirty : undefined,
            'data-mp-event': `${confirmText} SQL User`,
            'data-testid': isEditing ? 'sql-users-submit-update' : 'sql-users-submit-create'
          },
          cancelProps: { bg: 'carbon.2', 'data-mp-event': `Cancel ${confirmText} SQL User` }
        }}
      >
        <FormTextInput
          label={
            <Flex align="center">
              SQL User Name
              {!!namePrefix && (
                <HoverCard width={320} withArrow shadow="md">
                  <HoverCard.Target>
                    <IconInfoCircle size={16} style={{ marginLeft: 8 }} />
                  </HoverCard.Target>
                  <HoverCard.Dropdown>
                    <Typography variant="body-lg">
                      Your {entityName} requires a unique prefix for distinction, which must be included in the SQL user
                      name.
                    </Typography>
                  </HoverCard.Dropdown>
                </HoverCard>
              )}
            </Flex>
          }
          name="user_name"
          labelProps={{ fw: 700 }}
          autoComplete="new-name"
          disabled={isEditing}
          data-autofocus={isEditing ? false : true}
          leftLabel={namePrefix ? <span data-testid="sql-users-name-prefix">{namePrefix}</span> : undefined}
          rules={{
            required: 'Required',
            maxLength: {
              value: 32,
              message: 'SQL User Name length must be less than 32.'
            }
          }}
        />
        <Box>
          <FormPasswordInput
            placeholder={isEditing ? '********' : 'Create a password for user'}
            label="Password"
            name="password"
            labelProps={{ fw: 700 }}
            autoComplete="new-password"
            mb={16}
            styles={{
              section: {
                display: isEditing && !password ? 'none' : undefined
              }
            }}
            rules={{ required: isEditing ? undefined : 'Required' }}
          />
          <GeneratePassword name="password" />
        </Box>
        <div>
          <Flex align="center">
            <Typography variant="title-md">SQL User Roles</Typography>
            <HoverCard width={320} withArrow shadow="md">
              <HoverCard.Target>
                <IconInfoCircle size={16} style={{ marginLeft: 8 }} />
              </HoverCard.Target>
              <HoverCard.Dropdown>
                <Typography variant="body-lg">
                  Configure role based access control by assigning SQL user a mix of one built-in role, multiple custom
                  roles if there is any.{' '}
                  <a href="https://docs.pingcap.com/tidbcloud/configure-sql-users" target="_blank" rel="noreferrer">
                    Learn more about SQL user roles
                  </a>
                  .
                </Typography>
              </HoverCard.Dropdown>
            </HoverCard>
          </Flex>
          <FormSelect
            label="Built-in Role"
            name="builtin_role"
            placeholder="Select one built-in role"
            rules={{ required: isEditing && isRoot ? undefined : 'Required' }}
            disabled={isEditing && isRoot}
            data={
              namePrefix
                ? SQL_USER_BUIlTIN_ROLES_OPTIONS.map((option) => ({
                    ...option,
                    value:
                      option.value === SQL_USER_BUIlTIN_ROLES.DBROLE_ADMIN
                        ? SQL_USER_BUIlTIN_ROLES.DBROLE_ADMIN
                        : `${namePrefix}${option.value}`
                  }))
                : SQL_USER_BUIlTIN_ROLES_OPTIONS
            }
            mt={8}
          />
          {!!customRoleOptions?.length && (
            <FormMultiSelect
              label="Custom Roles"
              name="custom_role"
              styles={{
                input: {
                  // @TODO sync to the uikit
                  '&:disabled': {
                    '&::placeholder': {
                      color: theme.colors.dark[2]
                    }
                  }
                }
                // disabled: {
                //   opacity: 1
                // }
              }}
              disabled={isEditing && isRoot}
              placeholder="Select custom roles"
              labelProps={{ mb: 8 }}
              data={customRoleOptions || []}
              mt={16}
            />
          )}
        </div>
        <Divider mx={-24} />
      </Form>
    )
  }
)

interface SQLUserFormModalProps extends SQLUserFormProps {
  title: string
  opened: boolean
  onClose: () => void
}

export const SQLUserFormModal: React.FC<React.PropsWithChildren<SQLUserFormModalProps>> = ({
  title,
  type,
  opened,
  onClose,
  onSubmit,
  ...rest
}) => {
  return (
    <Modal
      title={title}
      opened={opened}
      onClose={onClose}
      centered
      size={480}
      styles={(theme) => ({
        header: {
          borderBottom: `1px solid ${theme.colors.carbon[4]}`
        }
      })}
    >
      <SQLUserForm type={type} onSubmit={onSubmit} onCancel={onClose} {...rest} />
    </Modal>
  )
}

export const CreateSQLUser: React.FC<
  React.PropsWithChildren<{
    clusterId: string
    isServerless?: boolean
    token?: string
    onCreate?: () => void
    disabled?: boolean
  }>
> = observer(({ clusterId, isServerless, token, onCreate, disabled }) => {
  const [visible, setVisible] = useState(false)
  const {
    store: { tenantData, activeProjectId }
  } = useStores()

  const closeModalForm = () => {
    setVisible(false)
  }

  const handleCreate = async (data: CreateSqlUserBody) => {
    await createSqlUser(tenantData.id, activeProjectId, clusterId, {
      ...data,
      user_name: isServerless && token ? `${token}${data.user_name}` : data.user_name,
      auth_method: SQL_USER_AUTH_METHODS.PASSWORD
    })
    setVisible(false)
    onCreate?.()
  }

  return (
    <div>
      <Button
        size="sm"
        px={12}
        data-mp-event="Create SQL User Button Clicked"
        data-testid="sql-users-create"
        disabled={disabled}
        onClick={() => setVisible(true)}
      >
        Create SQL User
      </Button>
      <SQLUserFormModal
        title="Create SQL User"
        type="add"
        namePrefix={isServerless ? token : undefined}
        entityName={isServerless ? 'instance' : 'cluster'}
        opened={visible}
        clusterId={clusterId}
        onClose={closeModalForm}
        onSubmit={handleCreate}
      />
    </div>
  )
})
