import { notifier } from '@tidbcloud/uikit'
import { Group, Select, Box, Typography, Loader, Alert, AlertProps, Tooltip } from '@tidbcloud/uikit'
import { IconPlus, IconInfoCircle, IconCheckCircle, IconAlertCircle } from '@tidbcloud/uikit/icons'
import DOMPurify from 'dompurify'
import { observer } from 'mobx-react'
import React, { useEffect, useMemo, useState } from 'react'

import { useAccessCheck } from 'dbaas/contexts/access-control'
import { useFetchCreationDefaultName, useFetchProviders } from 'dbaas/features/ClusterManagement/common'
import { useCreateServerlessCluster } from 'dbaas/features/ClusterManagement/serverless'
import { CloudProviderConst } from 'dbaas/features/Project'
import { Commonv1beta1ClusterState } from 'dbaas/services/api-clients/v2023/cluster-management/v1beta1/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { eventTracking } from 'dbaas/utils/tracking'

import { useIsClusterReady, useStarterTutorialTiDBs } from '../../helpers'

interface ItemProps {
  value: string
  label: string
  tips?: string
  hidden?: boolean
  disabled?: boolean
  prefix?: React.ReactNode
}

const SelectItem = ({ label, hidden, tips, prefix: icon, value, ...others }: ItemProps) => {
  if (hidden) {
    return null
  }
  const elem = (
    <Box w="100%">
      {icon}
      {label}
    </Box>
  )
  return (
    <div {...others}>
      {tips ? (
        <Tooltip label={tips} position="top-start" withArrow>
          {elem}
        </Tooltip>
      ) : (
        elem
      )}
    </div>
  )
}

export interface ClusterSelectProps {
  value?: string
  clusterName?: string
  onChange?: (value: { id: string; name: string } | undefined) => void
  disabled?: boolean
}

export const ClusterSelect: React.FC<React.PropsWithChildren<ClusterSelectProps>> = observer(
  ({ value, clusterName, onChange, disabled }) => {
    const {
      store: { activeProjectId }
    } = useStores()
    const creationVisible = useAccessCheck('TidbxCreateInProject', {
      projectId: activeProjectId
    })
    const { data: tidbs, refetch } = useStarterTutorialTiDBs()

    const clusterList = useMemo(() => tidbs?.tidbs || [], [tidbs?.tidbs])
    const selectedProjectId = useMemo(
      () => clusterList.find((tidb) => tidb.cluster?.id === value)?.cluster?.computed.projectId || '',
      [clusterList, value]
    )

    const [newCluster, setNewCluster] = useState<{ id: string; name: string; projectId: string } | undefined>()
    const {
      query: { refetch: fetchClusterName }
    } = useFetchCreationDefaultName()
    const {
      ready: clusterReady,
      error: queryError,
      deleted,
      remove,
      creating: clusterCreating
    } = useIsClusterReady(newCluster?.id || value || '', true, newCluster?.projectId || selectedProjectId)
    const {
      query: { data: { cloud_providers: providers = [] } = {} }
    } = useFetchProviders({ tier: 'serverless' })
    const provider = providers.find((p) => p.name === CloudProviderConst.AWS) || { name: '', regions: [] }
    const region = provider.regions[0] || { name: '' }
    const {
      createCluster,
      isCreating: creationLoading,
      mutation: { error: creationError, reset: resetCreation }
    } = useCreateServerlessCluster({
      provider: provider.name,
      region: region.name
    })

    // detect whether the newly cluster is ready
    useEffect(() => {
      // if newly cluster is ready, need refresh
      if (clusterReady) {
        // if user switch back and return, we should not notice
        if (newCluster?.id) {
          notifier.success(`${clusterName} successfully created.`)
        }
        refetch()
      }
    }, [clusterName, clusterReady, newCluster, refetch])

    const error =
      getErrorMessage(creationError) ||
      getErrorMessage(queryError) ||
      (deleted
        ? `The instance you previously selected, ${clusterName}, has been deleted. To proceed, please select a different instance.`
        : '')

    const isCreating = ((creationLoading || (!!newCluster?.id && !clusterReady)) && !error) || clusterCreating

    const clustersOptions = useMemo(() => {
      return clusterList
        .filter((tidb) => {
          return tidb.cluster?.computed.isServerless && tidb.cluster.provider !== CloudProviderConst.ALICLOUD
        })
        .map((tidb) => {
          const cluster = tidb.cluster!
          const disabledStatus: Commonv1beta1ClusterState[] = [
            Commonv1beta1ClusterState.CREATING,
            Commonv1beta1ClusterState.IMPORTING
          ]
          const tips = {
            [Commonv1beta1ClusterState.CREATING]: 'Cluster is creating',
            [Commonv1beta1ClusterState.IMPORTING]: 'Try after current import task is completed'
          }
          return {
            value: cluster.id,
            label: cluster.displayName,
            disabled: disabledStatus.includes(cluster.state),
            tips: tips[cluster.state] || ''
          }
        })
    }, [clusterList])

    useEffect(() => {
      if (!value && !isCreating && clustersOptions.length) {
        const target = clustersOptions[0]
        onChange?.({ id: target.value, name: target.label })
      }
    }, [clustersOptions, isCreating, onChange, value])

    const handleCreateCluster = async () => {
      const { data } = await fetchClusterName()
      const clusterName = data?.name ?? ''

      setNewCluster({ id: '', name: clusterName, projectId: activeProjectId })
      const { clusterId } = await createCluster({
        provider: provider.name,
        region: region.name,
        name: clusterName
      })
      if (clusterId) {
        onChange?.({ id: clusterId, name: clusterName })
        setNewCluster({ id: clusterId, name: clusterName, projectId: activeProjectId })
        // after create cluster, need to refetch for the next loop
        refetch()
      }
    }

    useEffect(() => {
      // when switching cluster, should clear state
      if (value && (deleted || clusterReady) && !!newCluster?.id && newCluster?.id !== value) {
        setNewCluster(undefined)
        remove()
      }
    }, [clusterReady, deleted, newCluster?.id, remove, value])

    useEffect(() => {
      // reset creation error after cluster change
      resetCreation()
    }, [resetCreation, value])

    const alertStyles: AlertProps['styles'] = {
      icon: {
        marginRight: 8
      }
    }

    const selectOptions: ItemProps[] = creationVisible
      ? [
          ...clustersOptions,
          {
            value: 'create',
            label: '',
            prefix: (
              <Group align="center" gap={8} c="carbon.8">
                <IconPlus size={16} />
                <Typography variant="body-lg">Create new instance</Typography>
              </Group>
            )
          }
        ]
      : clustersOptions

    return (
      <div>
        <Typography variant="action-md" c="carbon.9">
          Select Instance (Starter)
        </Typography>
        <Select
          placeholder={isCreating ? clusterName : ''}
          disabled={isCreating || disabled}
          mt={16}
          onChange={(value) => {
            if (value === 'create') {
              eventTracking('Tutorials - Create New Cluster Clicked')
              return handleCreateCluster()
            }
            const selected = value === null ? undefined : clustersOptions.find((item) => item.value === value)
            onChange?.(selected ? { id: selected.value, name: selected.label } : undefined)
          }}
          rightSection={isCreating ? <Loader size="sm" /> : null}
          value={isCreating ? undefined : value}
          data={selectOptions}
          renderOption={({ option }: { option: ItemProps }) => <SelectItem {...option} />}
        ></Select>
        {isCreating && (
          <Alert icon={<IconInfoCircle size={14} />} color={'peacock'} styles={alertStyles} mt={16}>
            Creating Starter instance <b>{clusterName}</b>... This usually takes 5 seconds. Please wait.
          </Alert>
        )}
        {clusterReady && !!newCluster?.id && !error && (
          <Alert icon={<IconCheckCircle size={14} />} color={'green'} styles={alertStyles} mt={16}>
            {clusterName} successfully created.
          </Alert>
        )}
        {!!error && (
          <Alert icon={<IconAlertCircle size={14} />} color={'red'} styles={alertStyles} mt={16}>
            <span dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(error) }}></span>
          </Alert>
        )}
      </div>
    )
  }
)
