import { InfoCircleOutlined } from '@ant-design/icons'
import { Alert, Button, Descriptions, Input, Popover, Spin } from 'antd'
import React, { useEffect, useMemo, useRef, useState } from 'react'

import { getCluster, getTenant } from 'apiClient/services/devops'
import {
  ClusterGetClusterResp,
  TenantGetTenantResp
} from 'apiClient/services/devops/interface'

import {
  getTenantLabel,
  isResourcePoolDeployType,
  shouldLoadTenant
} from '../../ResourceDescription/shared'

export interface ClusterSearchInputProps {
  value?: string
  onChange?: (value: string) => void
  disabled?: boolean
  placeholder?: string
  className?: string
  style?: React.CSSProperties
}

const DEFAULT_ERROR_MESSAGE = 'Failed to fetch cluster detail.'
const AUTO_OPEN_DELAY = 500

function getErrorMessage(error: unknown) {
  const axiosLikeError = error as {
    response?: { data?: { errmsg?: string } }
    message?: string
  }

  return (
    axiosLikeError?.response?.data?.errmsg ||
    axiosLikeError?.message ||
    DEFAULT_ERROR_MESSAGE
  )
}

const ClusterSearchInput: React.FC<ClusterSearchInputProps> = ({
  value,
  onChange,
  disabled,
  placeholder = 'Please input cluster id',
  className,
  style
}) => {
  const [innerValue, setInnerValue] = useState('')
  const [clusterInfo, setClusterInfo] = useState<ClusterGetClusterResp>()
  const [tenantInfo, setTenantInfo] = useState<TenantGetTenantResp>()
  const [curError, setCurError] = useState<string>()
  const [loading, setLoading] = useState(false)
  const [loadedClusterId, setLoadedClusterId] = useState<string>()
  const [isPopoverOpen, setIsPopoverOpen] = useState(false)
  const requestIdRef = useRef(0)

  const inputValue = value ?? innerValue
  const normalizedClusterId = useMemo(() => inputValue.trim(), [inputValue])

  useEffect(() => {
    requestIdRef.current += 1
    setClusterInfo(undefined)
    setTenantInfo(undefined)
    setCurError(undefined)
    setLoading(false)
    setLoadedClusterId(undefined)
    setIsPopoverOpen(false)
  }, [normalizedClusterId])

  useEffect(() => {
    if (disabled || !normalizedClusterId) {
      setIsPopoverOpen(false)
      return
    }

    const timer = window.setTimeout(() => {
      setIsPopoverOpen(true)
    }, AUTO_OPEN_DELAY)

    return () => {
      window.clearTimeout(timer)
    }
  }, [disabled, normalizedClusterId])

  const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const nextValue = event.target.value

    if (value === undefined) {
      setInnerValue(nextValue)
    }

    onChange?.(nextValue)
  }

  const fetchInfo = async () => {
    if (!normalizedClusterId) {
      return
    }

    const currentRequestId = requestIdRef.current + 1
    requestIdRef.current = currentRequestId

    setLoading(true)
    setCurError(undefined)

    try {
      const clusterData = (await getCluster(normalizedClusterId)).data

      if (currentRequestId !== requestIdRef.current) {
        return
      }

      setClusterInfo(clusterData)

      if (shouldLoadTenant(clusterData.tenant_id)) {
        const tenantData = (await getTenant(clusterData.tenant_id)).data

        if (currentRequestId !== requestIdRef.current) {
          return
        }

        setTenantInfo(tenantData)
      } else {
        setTenantInfo(undefined)
      }

      setLoadedClusterId(normalizedClusterId)
    } catch (error) {
      if (currentRequestId !== requestIdRef.current) {
        return
      }

      setClusterInfo(undefined)
      setTenantInfo(undefined)
      setCurError(getErrorMessage(error))
      setLoadedClusterId(normalizedClusterId)
    } finally {
      if (currentRequestId === requestIdRef.current) {
        setLoading(false)
      }
    }
  }

  useEffect(() => {
    if (!isPopoverOpen || disabled || !normalizedClusterId || loading) {
      return
    }

    if (loadedClusterId === normalizedClusterId) {
      return
    }

    fetchInfo()
  }, [
    disabled,
    fetchInfo,
    isPopoverOpen,
    loadedClusterId,
    loading,
    normalizedClusterId
  ])

  const handleOpenChange = (visible: boolean) => {
    setIsPopoverOpen(visible)
  }

  const renderPopoverContent = () => {
    if (curError) {
      return (
        <Alert
          message="Error"
          description={curError}
          type="error"
          showIcon
          style={{ width: '400px', marginBottom: '12px' }}
        />
      )
    }

    if (loading || !clusterInfo) {
      return (
        <div style={{ width: '400px', padding: '12px 0', textAlign: 'center' }}>
          <Spin />
        </div>
      )
    }

    return (
      <div style={{ width: '400px' }}>
        <Descriptions column={1} size="small">
          <Descriptions.Item
            label={
              isResourcePoolDeployType(clusterInfo.deploy_type)
                ? 'Resource Pool'
                : 'Cluster'
            }
          >
            {clusterInfo.name} ({clusterInfo.id})
          </Descriptions.Item>
          <Descriptions.Item label="Status">
            {clusterInfo.status
              ? clusterInfo.status.replace('USER_CLUSTER_STATUS_', '')
              : '-'}
          </Descriptions.Item>
          <Descriptions.Item label="Project">
            {clusterInfo.project_name} ({clusterInfo.project_id})
          </Descriptions.Item>
          <Descriptions.Item label="Tenant">
            {getTenantLabel(clusterInfo.tenant_id, tenantInfo)}
          </Descriptions.Item>
        </Descriptions>
      </div>
    )
  }

  return (
    <div className={className} style={{ display: 'flex', ...style }}>
      <Input
        value={inputValue}
        disabled={disabled}
        placeholder={placeholder}
        onChange={handleInputChange}
        style={{ borderRadius: '2px 0 0 2px' }}
      />
      <Popover
        content={renderPopoverContent()}
        trigger="hover"
        open={isPopoverOpen}
        onOpenChange={handleOpenChange}
        mouseEnterDelay={0.3}
      >
        <Button
          disabled={disabled || !normalizedClusterId}
          icon={<InfoCircleOutlined />}
          style={{ borderRadius: '0 2px 2px 0', marginLeft: '-1px' }}
        />
      </Popover>
    </div>
  )
}

export default ClusterSearchInput
export { ClusterSearchInput }
