import {
  ActionIcon,
  Alert,
  Box,
  Button,
  Code,
  CopyButton,
  Divider,
  Group,
  HoverCard,
  Loader,
  PrismProps,
  Select,
  SimpleGrid,
  Stack,
  Tabs,
  TextInput,
  Tooltip,
  Typography
} from '@tidbcloud/uikit'
import { CodeBlock } from '@tidbcloud/uikit/biz'
import {
  IconAlertTriangle,
  IconCheck,
  IconCopy01,
  IconFileMinus03,
  IconInfoCircle,
  IconTerminalSquare
} from '@tidbcloud/uikit/icons'
import { observer } from 'mobx-react'
import { useState } from 'react'

import { AccessDeniedHoverHint, useAccessCheck } from 'dbaas/contexts/access-control'
import { ConsoleBasicBranch as ServerlessBasicBranch } from 'dbaas/services/api-clients/legacy/serverless/interface'
import useStores from 'dbaas/stores/useStores'

import { PrivEndpointDesc } from './EndpointDesc'
import { FirewallRulesAlert } from './FirewallRules'
import {
  defaultOS,
  getCodeVarData,
  getConnCode,
  getCurrentBranchData,
  isParamsBlock,
  MAIN_BRANCH_NAME,
  osOptions,
  rootClientOptions,
  useBranchList,
  useCodeCopy,
  useConnections,
  useDatabaseList,
  useEndpoint,
  useGetCluster,
  useRootClient,
  useUpdateRootPwd
} from './helpers'
import { ConnectionModalFooter } from './ModalFooter'
import { ParamsBlock } from './ParamsBlock'
import { BranchScope, ContentType } from './types'

const icons = {
  terminal: <IconTerminalSquare size={16} />,
  file: <IconFileMinus03 size={16} />
}

interface ConnectionModalBodyProps {
  contentType: ContentType
  onToggleContent: (type?: ContentType) => void
  clusterId: string
  branchScope: BranchScope
  givenBranch?: ServerlessBasicBranch
}

export const ConnectionModalBody = observer(
  ({ contentType, onToggleContent, clusterId, branchScope, givenBranch }: ConnectionModalBodyProps) => {
    const { data: clusterData, ...clusterQuery } = useGetCluster(clusterId)
    const initializing = clusterQuery.isLoading

    /**
     * endpoint
     */
    const {
      endpointOptions,
      endpoint,
      onEndpointChange,
      isPrivate: isPrivConn
    } = useEndpoint(clusterData?.endpoints, clusterData?.servicePlan)

    /**
     * branch
     */
    const isAllBranch = branchScope === BranchScope.All
    const [branch, setBranch] = useState(isAllBranch ? MAIN_BRANCH_NAME : givenBranch?.id!)
    const {
      options: branchOptions,
      list: branchList,
      queryResult: branchQuery
    } = useBranchList(
      { clusterId, scope: branchScope, givenBranch },
      {
        onSuccess: (data) => {
          if (data.find((item) => item.id === branch)) {
            return
          }

          setBranch(isAllBranch ? MAIN_BRANCH_NAME : data?.[0]?.id)
        }
      }
    )
    const isMainBranch = branch === MAIN_BRANCH_NAME
    const currentBranch = getCurrentBranchData({
      cluster: clusterData,
      branchList,
      branchId: branch
    })

    /**
     * Root user password
     */
    const {
      stage: pwdStage,
      updating: pwdUpdating,
      rootPwd,
      update: updateRootPwd,
      cancelUpdate: cancelRootPwdUpdate
    } = useUpdateRootPwd({
      initializing: initializing || branchQuery.isLoading,
      clusterId,
      branchId: branch,
      rootPwdSet: currentBranch.rootPwdSet,
      onFinish: contentType === ContentType.Connect ? undefined : () => onToggleContent(ContentType.Connect),
      onCancel: contentType === ContentType.Connect ? undefined : () => onToggleContent(ContentType.Connect)
    })

    /**
     * database
     */
    const dbListAccessible = useAccessCheck('TidbxRead', { tidbId: clusterId })
    const [db, setDB] = useState('')
    const { dbOptions, queryResult: dbQuery } = useDatabaseList(
      { clusterId, branchId: branch },
      {
        onSuccess: (data) => setDB(data?.[0])
      }
    )
    const onDBChange = (db: string) => setDB(db)
    const onBranchChange = (branch: string) => {
      setBranch(branch)
      // Reset db if branch changed
      setDB('')
    }

    /**
     * os
     */
    const [os, setOS] = useState(defaultOS)
    const onOSChange = (os: string) => setOS(os)

    /**
     * connection client and codes
     */
    const { rootClientName, rootClient, onRootClientChange } = useRootClient()
    const { withSubClient, rootConn, subConns, currentConn, activeSubConnId, onSubConnIdChange } = useConnections({
      endpoint,
      rootClient
    })
    const caTipsVisible = currentConn?.download_ca?.includes(os)
    const codeVars = getCodeVarData({
      endpointData: isPrivConn ? currentBranch.endpointsData.private : currentBranch.endpointsData.public,
      userToken: currentBranch.userToken,
      pwd: rootPwd,
      db,
      os
    })
    const {
      store: { userProfile }
    } = useStores()
    const codeHandlers = useCodeCopy({
      branch,
      endpoint,
      os,
      rootClientName,
      mpEvents: {
        freeCopy: 'Connection string copied',
        btnCopy: 'Connection copy button clicked',
        email: userProfile.email
      }
    })

    if (contentType === ContentType.Reset) {
      return (
        <Stack p="xl">
          <Typography variant="body-lg">
            Resetting password can't be undone. The previous password will be invalid forever. Are you sure you want to
            reset your password?
          </Typography>
          {pwdStage.resetError && <Alert color="red">Failed to reset password. Please try again later.</Alert>}
          <Group justify="flex-end">
            <Button
              variant="default"
              onClick={cancelRootPwdUpdate}
              data-mp-event="Cancel resetting password Button Clicked"
              data-mp-is-main-branch={isMainBranch}
              data-testid="connection-reset-password-cancel-btn"
            >
              Keep the old one
            </Button>
            <Button
              color="red"
              variant="light"
              loading={pwdUpdating}
              onClick={updateRootPwd}
              data-mp-event="Confirm resetting password Button Clicked"
              data-mp-is-main-branch={isMainBranch}
              data-testid="connection-reset-password-confirm-btn"
            >
              Sure, reset it
            </Button>
          </Group>
        </Stack>
      )
    }

    const clusterProvider = clusterData?.region?.provider || ''

    return (
      <>
        {(clusterQuery.isError || branchQuery.isError || dbQuery.isError) && (
          <Box p="xl" pb={0}>
            <Alert color="red">
              <Group gap={0}>
                Oops, something went wrong when getting connection configurations.
                <Button
                  variant="white"
                  color="peacock"
                  onClick={() => {
                    if (clusterQuery.isError) {
                      return clusterQuery.refetch()
                    } else if (branchQuery.isError) {
                      return branchQuery.refetch()
                    } else if (dbQuery.isError) {
                      return dbQuery.refetch()
                    }
                  }}
                  loading={clusterQuery.isRefetching || branchQuery.isRefetching || dbQuery.isRefetching}
                  bg="transparent"
                  fw={600}
                  h={20}
                  px={4}
                  data-mp-event="Retry getting connection configurations button clicked"
                  data-mp-is-main-branch={isMainBranch}
                >
                  Retry
                </Button>
              </Group>
            </Alert>
          </Box>
        )}
        {!isPrivConn && clusterData?.servicePlan === 'Essential' && (
          <Box p="xl" pb={0}>
            <Alert color="yellow" icon={<IconAlertTriangle size={14} />}>
              Public endpoints may incur additional network charges.{' '}
              <a
                href="https://docs.pingcap.com/tidbcloud/connect-to-tidb-cluster-serverless#network"
                target="_blank"
                rel="noreferrer"
              >
                Learn more
              </a>
            </Alert>
          </Box>
        )}
        <SimpleGrid cols={2} p="xl">
          <Select
            label={
              <Group gap={4}>
                Connection Type
                <HoverCard width={400} shadow="md" position="top">
                  <HoverCard.Target>
                    <ActionIcon variant="transparent" size="xs" color="carbon.7">
                      <IconInfoCircle size={16} strokeWidth={2} />
                    </ActionIcon>
                  </HoverCard.Target>
                  <HoverCard.Dropdown>
                    <Typography variant="body-lg">
                      You can configure connectivity to your instance in two ways:
                    </Typography>
                    <Typography variant="body-lg">
                      1. <b>Public</b>: A publicly resolvable DNS address.
                    </Typography>
                    <Typography variant="body-lg">
                      2. <b>Private Endpoint</b>: Secure access for SQL clients in your VPC via Private Link.
                    </Typography>
                    <Typography variant="body-lg">
                      Learn more about{' '}
                      <a
                        href="https://docs.pingcap.com/tidbcloud/connect-to-tidb-cluster-serverless#network"
                        target="_blank"
                        rel="noreferrer"
                      >
                        connection types
                      </a>
                      .
                    </Typography>
                  </HoverCard.Dropdown>
                </HoverCard>
              </Group>
            }
            value={endpoint}
            data={endpointOptions}
            allowDeselect={false}
            disabled={initializing}
            rightSection={initializing ? <Loader size="xs" /> : undefined}
            onChange={onEndpointChange}
            data-autofocus
            data-testid="connection-endpoint-selector"
          />
          {isAllBranch ? (
            <Select
              label="Branch"
              value={branch}
              data={branchOptions}
              allowDeselect={false}
              disabled={initializing || branchQuery.isLoading}
              rightSection={initializing || branchQuery.isLoading ? <Loader size="xs" /> : undefined}
              onChange={onBranchChange}
              data-testid="connection-branch-selector"
            />
          ) : (
            <TextInput
              label="Branch"
              value={givenBranch?.display_name}
              disabled
              data-testid="connection-branch-disabled-input"
            />
          )}
          <Select
            label="Database"
            value={db}
            data={dbOptions}
            allowDeselect={false}
            placeholder={
              initializing || branchQuery.isFetching || dbQuery.isFetching ? 'Loading...' : 'Select a database'
            }
            disabled={initializing || branchQuery.isFetching || dbQuery.isFetching || !dbListAccessible}
            rightSection={
              initializing || branchQuery.isFetching || dbQuery.isFetching ? <Loader size="xs" /> : undefined
            }
            onChange={onDBChange}
            data-testid="connection-db-selector"
          />
          {isPrivConn ? (
            <TextInput label="Operating System" value="-" disabled data-testid="connection-os-disabled-input" />
          ) : (
            <Select
              label="Operating System"
              value={os}
              data={osOptions}
              allowDeselect={false}
              onChange={onOSChange}
              data-testid="connection-os-selector"
            />
          )}
        </SimpleGrid>
        <Divider color="carbon.4" />
        <Stack
          p="xl"
          bg="carbon.3"
          sx={(theme) => ({
            borderBottomLeftRadius: theme.defaultRadius,
            borderBottomRightRadius: theme.defaultRadius
          })}
        >
          {!isPrivConn && <FirewallRulesAlert clusterId={clusterId} />}
          {isPrivConn && (
            <PrivEndpointDesc
              serviceName={clusterData?.endpoints?.private?.[clusterProvider]?.serviceName}
              az={clusterData?.endpoints?.private?.[clusterProvider]?.availabilityZone?.join(' ')}
              region={clusterData?.region?.name}
              provider={clusterData?.region?.provider || ''}
              servicePlan={clusterData?.servicePlan}
            />
          )}
          <SimpleGrid cols={2} verticalSpacing="xl">
            <Group gap={0} wrap="nowrap">
              <TextInput
                placeholder="Connect With"
                readOnly
                styles={(theme) => ({
                  wrapper: {
                    width: 'min-content',
                    minWidth: 128,
                    maxWidth: 'min-content'
                  },
                  input: {
                    borderTopRightRadius: 0,
                    borderBottomRightRadius: 0,
                    borderRight: 'none',
                    '&:focus, &:focus-within': {
                      borderColor: theme.colors.carbon[5]
                    },
                    '&::placeholder': {
                      color: theme.colors.carbon[7]
                    }
                  }
                })}
              />
              <Select
                value={rootClient}
                data={rootClientOptions}
                allowDeselect={false}
                onChange={onRootClientChange}
                styles={{
                  root: {
                    flexGrow: 1
                  },
                  input: {
                    borderTopLeftRadius: 0,
                    borderBottomLeftRadius: 0
                  }
                }}
                data-testid="connection-client-selector"
              />
            </Group>
            <Box ta="right">
              {(pwdStage.newUnset || pwdStage.newError) && (
                <AccessDeniedHoverHint id="TidbxRootPasswordUpdate" scopeProps={{ tidbId: clusterId }}>
                  {({ accessible }) => (
                    <Button
                      variant="default"
                      disabled={!accessible}
                      loading={pwdUpdating}
                      onClick={updateRootPwd}
                      data-mp-event="Create password Button Clicked"
                      data-mp-is-main-branch={isMainBranch}
                      data-testid="connection-create-password-btn"
                    >
                      Generate Password
                    </Button>
                  )}
                </AccessDeniedHoverHint>
              )}
            </Box>
          </SimpleGrid>
          {pwdStage.newUnset && (
            <Typography variant="body-lg">
              You don't have a password yet. Please generate one at first and keep it in a safe place.
            </Typography>
          )}
          {pwdStage.newError && <Alert color="red">Failed to generate password. Please try again later.</Alert>}
          {pwdStage.newSuccess && (
            <Alert color="green">
              <Box>
                Generated! Password:{' '}
                <Group gap={0} display="inline-flex">
                  <Code bg="transparent" c="carbon.9">
                    {rootPwd}
                  </Code>
                  <CopyButton value={rootPwd} timeout={2000}>
                    {({ copied, copy }) => (
                      <Tooltip label={copied ? 'Copied' : 'Copy'} withArrow position="right">
                        <ActionIcon
                          variant="transparent"
                          size="sm"
                          c="carbon.8"
                          onClick={copy}
                          data-mp-event="Copy password Button Clicked"
                          data-mp-is-main-branch={isMainBranch}
                          data-testid="connection-copy-password-btn"
                        >
                          {copied ? <IconCheck size={14} /> : <IconCopy01 size={14} strokeWidth={2} onClick={copy} />}
                        </ActionIcon>
                      </Tooltip>
                    )}
                  </CopyButton>
                </Group>
              </Box>
              <Box>Password cannot be displayed next time for safety. Please keep it in a safe place.</Box>
            </Alert>
          )}
          {pwdStage.resetUnset && (
            <Typography variant="body-lg">
              Existing connections are based on password you've set before.{' '}
              <AccessDeniedHoverHint id="TidbxRootPasswordUpdate" scopeProps={{ tidbId: clusterId }}>
                {({ accessible }) => (
                  <>
                    Forget it?{' '}
                    <Button
                      variant="white"
                      color="peacock"
                      disabled={!accessible}
                      onClick={() => onToggleContent(ContentType.Reset)}
                      bg="transparent"
                      fw={600}
                      h={20}
                      px={4}
                      data-mp-event="Reset password Button Clicked"
                      data-mp-is-main-branch={isMainBranch}
                      data-testid="connection-reset-password-btn"
                    >
                      Reset Password
                    </Button>
                  </>
                )}
              </AccessDeniedHoverHint>
            </Typography>
          )}
          {pwdStage.resetSuccess && (
            <Alert color="green">
              <Box>
                Reset successfully! Password:{' '}
                <Group gap={0} display="inline-flex">
                  <Code bg="transparent" c="carbon.9">
                    {rootPwd}
                  </Code>
                  <CopyButton value={rootPwd} timeout={2000}>
                    {({ copied, copy }) => (
                      <Tooltip label={copied ? 'Copied' : 'Copy'} withArrow position="right">
                        <ActionIcon
                          variant="transparent"
                          size="sm"
                          c="carbon.8"
                          onClick={copy}
                          data-mp-event="Copy password Button Clicked"
                          data-mp-is-main-branch={isMainBranch}
                          data-testid="connection-copy-password-btn"
                        >
                          {copied ? <IconCheck size={14} /> : <IconCopy01 size={14} strokeWidth={2} onClick={copy} />}
                        </ActionIcon>
                      </Tooltip>
                    )}
                  </CopyButton>
                </Group>
              </Box>
              <Box>Password cannot be displayed next time for safety. Please keep it in a safe place.</Box>
            </Alert>
          )}

          {caTipsVisible && (
            <Alert>
              Download the{' '}
              <a href="https://letsencrypt.org/certs/isrgrootx1.pem" target="_blank" rel="noopener noreferrer">
                CA cert
              </a>{' '}
              for server certificate verification and replace {'<'}CA_PATH{'>'} with your local path.
            </Alert>
          )}
          {!withSubClient &&
            rootConn &&
            (isParamsBlock(rootConn.type) ? (
              <Box
                sx={(theme) => ({
                  backgroundColor: theme.colors.carbon[1],
                  border: `1px solid ${theme.colors.carbon[5]}`,
                  borderRadius: theme.defaultRadius
                })}
              >
                <ParamsBlock endpoint={rootConn.endpoint} vars={codeVars} servicePlan={clusterData?.servicePlan} />
              </Box>
            ) : (
              <CodeBlock
                language={rootConn.lang as PrismProps['language']}
                codeHighlightProps={{
                  onCopy: () => codeHandlers.onSelectionCopy()
                }}
                onCopyClick={codeHandlers.onCopyBtnClick}
                sx={(theme) => ({
                  '&>div:first-of-type': {
                    backgroundColor: theme.colors.carbon[1]
                  }
                })}
                data-testid="connection-codeblock"
              >
                {getConnCode(rootConn?.code || '', codeVars)}
              </CodeBlock>
            ))}
          {withSubClient && (
            <Tabs
              value={activeSubConnId}
              onChange={onSubConnIdChange}
              styles={(theme) => ({
                root: {
                  backgroundColor: theme.colors.carbon[1],
                  border: `1px solid ${theme.colors.carbon[5]}`,
                  borderRadius: theme.defaultRadius
                },
                tabsList: {
                  gap: 24,
                  borderBottom: `1px solid ${theme.colors.carbon[4]}`
                },
                tabIcon: {
                  '&:not(:only-child)': {
                    marginRight: 4
                  }
                }
              })}
              data-testid="connection-codeblock"
            >
              <Tabs.List mx="md">
                {subConns?.map((item) => (
                  <Tabs.Tab key={item.id} value={item.id} leftSection={icons[item.iconType]} pt={16} mb={-1}>
                    {item.label}
                  </Tabs.Tab>
                ))}
              </Tabs.List>
              {subConns?.map((item) => (
                <Tabs.Panel key={item.id} value={item.id}>
                  {isParamsBlock(item.type) ? (
                    <ParamsBlock endpoint={item.endpoint} vars={codeVars} servicePlan={clusterData?.servicePlan} />
                  ) : (
                    <CodeBlock
                      language={item.lang as PrismProps['language']}
                      codeHighlightProps={{
                        onCopy: () => codeHandlers.onSelectionCopy(item.label)
                      }}
                      onCopyClick={() => codeHandlers.onCopyBtnClick(item.label)}
                      sx={{
                        '&>div:first-of-type': {
                          backgroundColor: 'transparent',
                          borderWidth: 0,
                          borderRadius: 'initial'
                        }
                      }}
                    >
                      {getConnCode(item.code, codeVars)}
                    </CodeBlock>
                  )}
                </Tabs.Panel>
              ))}
            </Tabs>
          )}
          <ConnectionModalFooter docLink={currentConn?.doc} rootClientName={rootClientName} endpoint={endpoint} />
        </Stack>
      </>
    )
  }
)
