import { Alert, Box, Button, Group, HoverCard, Loader, Select, SimpleGrid, Typography } from '@tidbcloud/uikit'
import { IconInfoCircle } from '@tidbcloud/uikit/icons'
import { isNil } from 'lodash-es'
import { observer } from 'mobx-react'
import { useEffect, useState } from 'react'

import { log } from 'common/log'
import { LOG_ERROR_SCENE } from 'common/log/logger'
import { PwdSetting } from 'dbaas/features/ClusterManagement/dedicated'
import { CloudProvider, CloudProviderLabelConst } from 'dbaas/features/Project'
import { useClusterServiceGetCluster } from 'dbaas/services/api-clients/v2023/cluster-management/v1beta1'
import { useTidbNodeGroupServiceGetTidbNodeGroup } from 'dbaas/services/api-clients/v2023/dedicated/v1beta1'
import useStores from 'dbaas/stores/useStores'

import { IpAccessEditor } from '../../Network/dedicated/IpAccess/IpAccessEditor'

import { ConnectionClientBlock, defaultRootClient } from './connection-client'
import { ConnectionTypeTips } from './ConnectionTypeTips'
import { useNetworkTypes, useNodeGroupId } from './helpers'
import { PrivateEndpointInit, PrivateInfoBlock, useNodeGroupPrivateBlock } from './private-endpoint'
import { PublicInfoBlock, usePublicBlock } from './public-endpoint'
import { ClusterAnnotation, ModalMode, NetworkType } from './types'
import { useVpcPeerBlock, VpcPeerInfoBlock, VpcPeerInit } from './vpc-peer'

interface ConnectionModalBodyProps {
  clusterId: string
  defaultNodeGroupId?: string
  nodeGroupFixed?: boolean
  titleHandlers: {
    update: (title: string) => void
    reset: () => void
  }
  onCancel: () => void
}

export const ConnectionModalBody = observer(
  ({ clusterId, defaultNodeGroupId, nodeGroupFixed, onCancel, titleHandlers }: ConnectionModalBodyProps) => {
    const {
      store: {
        lastVisitLocation: { orgId, projectId }
      }
    } = useStores()

    /**
     * Query cluster data with node groups
     */
    const clusterQuery = useClusterServiceGetCluster(
      clusterId,
      {
        orgId,
        projectId
      },
      {
        query: {
          select: (data) => data?.dedicatedCluster,
          refetchOnWindowFocus: false
        }
      }
    )
    const { cloudProvider: provider = '' as CloudProvider, regionId = '' } = clusterQuery.data || {}
    const region = regionId.replace(/^(aws|gcp|azure)-/, '')
    const nodeGroupOptions =
      clusterQuery.data?.tidbNodeSetting?.tidbNodeGroups
        ?.filter((el) => el.state !== 'PAUSED' && el.tidbNodeGroupId)
        ?.map((el) => ({
          value: el.tidbNodeGroupId || '',
          label: el.displayName || ''
        })) || []
    const pwdInited = clusterQuery.data?.annotations?.[ClusterAnnotation.PwdSet] === 'true'

    /**
     * Node group ID
     */
    const { nodeGroupId, setNodeGroupId } = useNodeGroupId(nodeGroupOptions, defaultNodeGroupId)

    /**
     * Query endpoints data from a node group
     */
    const endpointsQuery = useTidbNodeGroupServiceGetTidbNodeGroup(
      clusterId,
      nodeGroupId,
      {
        orgId,
        projectId
      },
      {
        query: {
          select: (data) => data?.endpoints,
          refetchOnWindowFocus: false,
          refetchInterval: (data) => {
            const notReady = data?.some((el) => !el.host)

            return notReady ? 10 * 1000 : false
          }
        }
      }
    )

    /**
     * Network type
     */
    const { networkTypeOptions, networkType, onNetworkTypeChange } = useNetworkTypes(clusterQuery.data)

    /**
     * Connection client
     */
    const [client, setClient] = useState(defaultRootClient)

    /**
     * Modal content mode
     */
    const [mode, setMode] = useState<ModalMode>(ModalMode.Connect)
    const gotoConnect = () => {
      setMode(ModalMode.Connect)
      titleHandlers.reset()
      clusterQuery.refetch()
    }
    const gotoPublicInit = () => {
      setMode(ModalMode.InitPublic)
      titleHandlers.update('Configure IP')
    }
    const gotoPrivateInit = () => {
      setMode(ModalMode.InitPrivate)
      titleHandlers.update(`Create ${CloudProviderLabelConst[provider]} Private Endpoint Connection`)
    }
    const gotoVpcPeerInit = () => {
      setMode(ModalMode.InitPeering)
      titleHandlers.update(`Create ${CloudProviderLabelConst[provider]} VPC Peering`)
    }
    const gotoSetPwd = () => {
      setMode(ModalMode.SetPwd)
      titleHandlers.update('Set Root Password')
    }

    const privateBlockData = useNodeGroupPrivateBlock({
      clusterId,
      nodeGroupId,
      isTarget: networkType === 'private'
    })

    const peerBlockData = useVpcPeerBlock({
      clusterId,
      provider,
      region,
      isTarget: networkType === 'peering',
      cluster: clusterQuery.data,
      nodeGroupId
    })

    const publicBlockData = usePublicBlock({
      isTarget: networkType === 'public',
      mode: 'node-group',
      clusterId,
      nodeGroupId
    })

    useEffect(() => {
      if (clusterQuery.isFetched && isNil(clusterQuery.data)) {
        log.error(`Prepare cluster for dedicated connection error: cluster ${clusterQuery.data}`, {
          tags: {
            scene: LOG_ERROR_SCENE.CoreJourney
          }
        })
      }
    }, [clusterQuery])

    useEffect(() => {
      if (endpointsQuery.isFetched && isNil(endpointsQuery.data)) {
        log.error(`Get endpoint list on nodeGroup error when dedicated connection: endpoints ${endpointsQuery.data}`, {
          tags: {
            scene: LOG_ERROR_SCENE.CoreJourney
          }
        })
      }
    }, [endpointsQuery])

    if (mode === ModalMode.InitPublic && provider) {
      return (
        <IpAccessEditor
          cidrList={publicBlockData.cidrList ?? []}
          nodeGroupId={nodeGroupId || ''}
          onClose={onCancel}
          onBack={gotoConnect}
          onConfirm={() => {
            publicBlockData.query.refetch()
            gotoConnect()
          }}
        />
      )
    }

    if (mode === ModalMode.InitPrivate && provider) {
      return (
        <PrivateEndpointInit
          mode="node-group"
          clusterId={clusterId}
          nodeGroupId={nodeGroupId}
          provider={provider}
          onCancel={onCancel}
          onBack={gotoConnect}
          onConfirm={() => {
            endpointsQuery.refetch()
            privateBlockData.endpointsQuery.refetch()
            gotoConnect()
          }}
        />
      )
    }

    if (mode === ModalMode.InitPeering && provider && region) {
      return (
        <VpcPeerInit
          provider={provider}
          region={region}
          onCancel={onCancel}
          onBack={gotoConnect}
          onConfirm={gotoConnect}
        />
      )
    }

    if (mode === ModalMode.SetPwd) {
      return (
        <PwdSetting
          clusterId={clusterId}
          isEditMode={pwdInited}
          onDone={gotoConnect}
          onCancel={onCancel}
          renderActions={({ submitting, cancelBtn, saveBtn }) => (
            <>
              {cancelBtn}
              <Button
                variant="default"
                disabled={submitting}
                onClick={gotoConnect}
                data-mp-event="Go back button on setting root password modal clicked"
              >
                Back
              </Button>
              {saveBtn}
            </>
          )}
        />
      )
    }

    const networkReady = {
      [NetworkType.Public]: publicBlockData.enabled && publicBlockData.ready,
      [NetworkType.Private]: privateBlockData.ready,
      [NetworkType.VpcPeer]: peerBlockData.ready
    }[networkType]

    const publicClientVar = endpointsQuery.data?.find((el) => el.connectionType === 'PUBLIC')
    const clientVarData = {
      [NetworkType.Public]: {
        host: publicClientVar?.host,
        port: publicClientVar?.port,
        userToken: ''
      },
      [NetworkType.Private]: privateBlockData.connVarData,
      [NetworkType.VpcPeer]: peerBlockData.connVarData
    }[networkType]

    const clientVisible = !!clusterQuery.data && !!endpointsQuery.data && networkReady

    return (
      <>
        {clusterQuery.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={() => clusterQuery.refetch()}
                  loading={clusterQuery.isRefetching}
                  bg="transparent"
                  fw={600}
                  h={20}
                  px={4}
                  data-mp-event="Retry querying cluster for connection button clicked"
                >
                  Retry
                </Button>
              </Group>
            </Alert>
          </Box>
        )}
        <SimpleGrid cols={2} p="xl">
          <Select
            label={
              <Group gap={4}>
                <Typography variant="label-lg">TiDB Node Group</Typography>
                <HoverCard position="top">
                  <HoverCard.Target>
                    <Box component={IconInfoCircle} sx={{ cursor: 'pointer' }} size={16} c="carbon.7" />
                  </HoverCard.Target>
                  <HoverCard.Dropdown>
                    <Typography variant="body-lg" w={300}>
                      TiDB Node Group is to manage the grouping of TiDB nodes . DefaultGroup is automatically created
                      with cluster. You can create extra groups with different connection endpoints to isolate your
                      workload.
                    </Typography>
                  </HoverCard.Dropdown>
                </HoverCard>
              </Group>
            }
            value={nodeGroupId}
            data={nodeGroupOptions}
            disabled={nodeGroupFixed || clusterQuery.isLoading || !nodeGroupOptions.length}
            placeholder={clusterQuery.isLoading ? 'Loading...' : !nodeGroupOptions.length ? 'No node group' : undefined}
            rightSection={clusterQuery.isLoading ? <Loader size="xs" /> : undefined}
            onChange={(value) => setNodeGroupId(value || '')}
            data-autofocus
            data-testid="dedicated-connection-node-group-selector"
          />
          <Select
            label={<ConnectionTypeTips />}
            value={networkType}
            data={networkTypeOptions}
            disabled={clusterQuery.isLoading}
            rightSection={clusterQuery.isLoading ? <Loader size="xs" /> : undefined}
            onChange={onNetworkTypeChange}
            data-testid="dedicated-connection-network-type-selector"
          />
        </SimpleGrid>
        {networkType === 'public' && (
          <PublicInfoBlock
            enabled={publicBlockData.enabled}
            clusterId={clusterId}
            provider={provider}
            loading={clusterQuery.isLoading || publicBlockData.loading}
            ipList={publicBlockData.ipList}
            onCancel={onCancel}
            onInit={gotoPublicInit}
          />
        )}
        {networkType === 'private' && (
          <PrivateInfoBlock
            loading={privateBlockData.loading}
            clusterId={clusterId}
            provider={provider}
            fullData={privateBlockData.fullEndpoints}
            activeData={privateBlockData.activeEndpoints}
            endpointId={privateBlockData.endpointId}
            onEndpointChange={privateBlockData.onEndpointChange}
            onCancel={onCancel}
            onInit={gotoPrivateInit}
          />
        )}
        {networkType === 'peering' && (
          <VpcPeerInfoBlock
            supported={provider === 'aws' || provider === 'gcp'}
            loading={peerBlockData.loading}
            clusterId={clusterId}
            provider={provider}
            rawData={{
              aws: peerBlockData.awsPeerQuery.data,
              gcp: peerBlockData.gcpPeerQuery.data
            }}
            onCancel={onCancel}
            onInit={gotoVpcPeerInit}
          />
        )}
        {clientVisible && (
          <ConnectionClientBlock
            clusterId={clusterId}
            pwdInited={pwdInited}
            networkType={networkType}
            client={client}
            onClientChange={setClient}
            onPwdSet={gotoSetPwd}
            caRequired={networkType === 'public'}
            host={clientVarData.host}
            port={clientVarData.port}
            userToken={clientVarData.userToken}
          />
        )}
      </>
    )
  }
)
