import {
  Alert,
  Button,
  Checkbox,
  Divider,
  Input,
  Modal,
  Select,
  Space,
  Table,
  Tag,
  Tabs,
  Typography
} from 'antd'
import { ColumnsType } from 'antd/lib/table'
import { useCallback, useEffect, useMemo, useState } from 'react'

import {
  cloudConfigControllerCreateClusterConfigurationChange,
  cloudConfigControllerGetClusterConfiguration,
  cloudConfigControllerListAvailableComponentVersions,
  cloudConfigControllerPreviewClusterConfigurationChange
} from 'apiClient/services/devops'
import {
  CloudConfigControllerListAvailableComponentVersionsClusterType,
  CloudconfigChangeComponentReq,
  CloudconfigClusterComponentVersion,
  CloudconfigClusterType,
  ClusterGetClusterResp
} from 'apiClient/services/devops/interface'

import CurrentConfig from '../Configs/CurrentConfig/Current'

const { Text, Title } = Typography

type UpgradeScope = 'LOGICAL_CLUSTER' | 'SHARED_POOL'
type AvailableVersionsByRow = Record<
  string,
  CloudconfigClusterComponentVersion[]
>

type UpgradeComponentRow = {
  key: string
  scope: UpgradeScope
  component: string
  currentVersion?: string
  currentImageTag?: string
  targetVersion?: string
  defaultTargetImageTag?: string
  targetImageTag?: string
  enabled: boolean
  needConfigChange: boolean
  configChanges?: string
  loadingTarget?: boolean
}

type ClusterConfiguration = {
  version?: string
  components?: Record<
    string,
    {
      name?: string
      version?: string
      effective_image_tag?: string
      overwrite_image_tag?: string
    }
  >
}

const getErrorContent = (error: any) =>
  error?.response?.data?.errmsg || error?.message || String(error)

const getScopeLabel = (scope: UpgradeScope) =>
  scope === 'LOGICAL_CLUSTER' ? 'Premium Cluster' : 'Resource Pool'

const getAvailableVersionImageTag = (
  version?: CloudconfigClusterComponentVersion
) =>
  version?.effective_image_tag ||
  version?.overwrite_image_tag ||
  (version as { image_tag?: string } | undefined)?.image_tag

const getComponentReqs = (
  rows: UpgradeComponentRow[]
): CloudconfigChangeComponentReq[] =>
  rows
    .filter((row) => row.enabled && row.targetVersion)
    .map((row) => ({
      component: row.component,
      version: {
        to_version: row.targetVersion
      },
      overwrite_image_tag: row.targetImageTag
        ? {
            to_image_tag: row.targetImageTag
          }
        : undefined
    }))

export default function PremiumUpgrade({
  cluster,
  mode = 'premium',
  resourcePoolId: resourcePoolIdProp
}: {
  cluster: ClusterGetClusterResp
  reload: () => Promise<void>
  mode?: 'premium' | 'resource-pool'
  resourcePoolId?: string
}) {
  const [open, setOpen] = useState(false)
  const [rows, setRows] = useState<UpgradeComponentRow[]>([])
  const [availableVersionsByRow, setAvailableVersionsByRow] =
    useState<AvailableVersionsByRow>({})
  const [loading, setLoading] = useState(false)
  const [loadingVersions, setLoadingVersions] = useState(false)
  const [configOpen, setConfigOpen] = useState(false)

  const resourcePoolOnly = mode === 'resource-pool'
  const resourcePoolId = resourcePoolOnly
    ? resourcePoolIdProp || cluster.id
    : undefined
  const logicalClusterId = resourcePoolOnly ? undefined : cluster.id || ''
  const selectedRows = useMemo(() => rows.filter((row) => row.enabled), [rows])
  const allUpgradeSelected =
    rows.length > 0 && selectedRows.length === rows.length
  const partialUpgradeSelected =
    selectedRows.length > 0 && selectedRows.length < rows.length
  const logicalRows = useMemo(
    () => rows.filter((row) => row.scope === 'LOGICAL_CLUSTER'),
    [rows]
  )
  const resourcePoolRows = useMemo(
    () => rows.filter((row) => row.scope === 'SHARED_POOL'),
    [rows]
  )

  const reset = useCallback(() => {
    setRows([])
    setAvailableVersionsByRow({})
    setConfigOpen(false)
  }, [])

  const buildRows = useCallback(
    (
      logical?: ClusterConfiguration,
      resourcePool?: ClusterConfiguration,
      logicalCurrent?: ClusterConfiguration,
      resourcePoolCurrent?: ClusterConfiguration
    ) => {
      const nextRows: UpgradeComponentRow[] = []

      Object.entries(logical?.components || {}).forEach(([key, current]) => {
        const component = current?.name || key
        const original = logicalCurrent?.components?.[key]
        nextRows.push({
          key: `LOGICAL_CLUSTER:${component}`,
          scope: 'LOGICAL_CLUSTER',
          component,
          currentVersion:
            original?.version ||
            current?.version ||
            logicalCurrent?.version ||
            logical?.version,
          currentImageTag: current?.effective_image_tag,
          enabled: false,
          needConfigChange: false
        })
      })

      Object.entries(resourcePool?.components || {}).forEach(
        ([key, current]) => {
          const component = current?.name || key
          const original = resourcePoolCurrent?.components?.[key]
          nextRows.push({
            key: `SHARED_POOL:${component}`,
            scope: 'SHARED_POOL',
            component,
            currentVersion:
              original?.version ||
              current?.version ||
              resourcePoolCurrent?.version ||
              resourcePool?.version,
            currentImageTag: current?.effective_image_tag,
            enabled: false,
            needConfigChange: false
          })
        }
      )

      return nextRows
    },
    []
  )

  const loadAvailableVersions = useCallback(
    async (targetRows: UpgradeComponentRow[]) => {
      if (!targetRows.length) {
        setAvailableVersionsByRow({})
        return
      }

      setLoadingVersions(true)
      const entries = await Promise.all(
        targetRows.map(async (row) => {
          try {
            const resp =
              await cloudConfigControllerListAvailableComponentVersions(
                row.component,
                {
                  cluster_type:
                    row.scope as CloudConfigControllerListAvailableComponentVersionsClusterType
                }
              )

            return [row.key, resp.data.detail || []] as const
          } catch (error) {
            return [row.key, []] as const
          }
        })
      )

      setAvailableVersionsByRow(Object.fromEntries(entries))
      setLoadingVersions(false)
    },
    []
  )

  const loadCurrent = useCallback(async () => {
    if (!logicalClusterId && !resourcePoolId) {
      return
    }

    setLoading(true)
    try {
      let logical: ClusterConfiguration | undefined
      let logicalCurrent: ClusterConfiguration | undefined
      if (logicalClusterId) {
        const logicalResp = await cloudConfigControllerGetClusterConfiguration({
          cluster_id: logicalClusterId,
          cluster_type: CloudconfigClusterType.LOGICAL_CLUSTER
        })
        logicalCurrent = logicalResp.data.configuration as ClusterConfiguration

        const logicalPreviewResp =
          await cloudConfigControllerPreviewClusterConfigurationChange({
            cluster_id: logicalClusterId,
            cluster_type: CloudconfigClusterType.LOGICAL_CLUSTER,
            change_type: 'logicalcluster'
          })
        logical = logicalPreviewResp.data.configuration as ClusterConfiguration
      }

      let resourcePool: ClusterConfiguration | undefined
      let resourcePoolCurrent: ClusterConfiguration | undefined
      if (resourcePoolId) {
        const resourcePoolResp =
          await cloudConfigControllerGetClusterConfiguration({
            cluster_id: resourcePoolId,
            cluster_type: CloudconfigClusterType.SHARED_POOL
          })
        resourcePoolCurrent = resourcePoolResp.data
          .configuration as ClusterConfiguration

        const resourcePoolPreviewResp =
          await cloudConfigControllerPreviewClusterConfigurationChange({
            cluster_id: resourcePoolId,
            cluster_type: CloudconfigClusterType.SHARED_POOL,
            change_type: 'sharedpool'
          })
        resourcePool = resourcePoolPreviewResp.data
          .configuration as ClusterConfiguration
      }

      const nextRows = buildRows(
        logical,
        resourcePool,
        logicalCurrent,
        resourcePoolCurrent
      )
      setRows(nextRows)
      void loadAvailableVersions(nextRows)
    } catch (error) {
      Modal.error({
        title: 'Failed to load premium upgrade data',
        content: getErrorContent(error)
      })
    } finally {
      setLoading(false)
    }
  }, [buildRows, loadAvailableVersions, logicalClusterId, resourcePoolId])

  useEffect(() => {
    if (open) {
      void loadCurrent()
    }
  }, [loadCurrent, open])

  const updateRow = useCallback(
    (key: string, patch: Partial<UpgradeComponentRow>) => {
      setRows((current) =>
        current.map((row) => (row.key === key ? { ...row, ...patch } : row))
      )
    },
    []
  )

  const updateRowTargetVersion = useCallback(
    (key: string, targetVersion?: string) => {
      setRows((current) =>
        current.map((row) => {
          if (row.key !== key) {
            return row
          }

          const target = (availableVersionsByRow[row.key] || []).find(
            (item) => item.version === targetVersion
          )
          const targetImageTag = getAvailableVersionImageTag(target)

          return {
            ...row,
            targetVersion,
            defaultTargetImageTag: targetImageTag,
            targetImageTag
          }
        })
      )
    },
    [availableVersionsByRow]
  )

  const updateAllRows = useCallback((enabled: boolean) => {
    setRows((current) => current.map((row) => ({ ...row, enabled })))
  }, [])

  const logicalComponentsReq = useMemo(
    () => getComponentReqs(logicalRows),
    [logicalRows]
  )
  const resourcePoolComponentsReq = useMemo(
    () => getComponentReqs(resourcePoolRows),
    [resourcePoolRows]
  )
  const hasLogicalUpgrade = logicalComponentsReq.length > 0
  const hasResourcePoolUpgrade = resourcePoolComponentsReq.length > 0
  const configDefaultActiveKey = hasLogicalUpgrade ? 'logical' : 'resource-pool'

  const handleNext = useCallback(() => {
    const missingVersion = selectedRows.filter((row) => !row.targetVersion)
    if (missingVersion.length) {
      Modal.warning({
        title: 'Target version is required',
        content: missingVersion
          .map((row) => `${getScopeLabel(row.scope)}/${row.component}`)
          .join(', ')
      })
      return
    }

    const missing = selectedRows.filter((row) => !row.targetImageTag)
    if (missing.length) {
      Modal.warning({
        title: 'Target image tag is required',
        content: missing
          .map((row) => `${getScopeLabel(row.scope)}/${row.component}`)
          .join(', ')
      })
      return
    }

    setOpen(false)
    setConfigOpen(true)
  }, [selectedRows])

  const columns: ColumnsType<UpgradeComponentRow> = [
    {
      title: (
        <Checkbox
          checked={allUpgradeSelected}
          indeterminate={partialUpgradeSelected}
          onChange={(event) => updateAllRows(event.target.checked)}
        >
          Upgrade
        </Checkbox>
      ),
      width: 96,
      fixed: 'left',
      render: (_, record) => (
        <Checkbox
          checked={record.enabled}
          onChange={(event) =>
            updateRow(record.key, { enabled: event.target.checked })
          }
        />
      )
    },
    {
      title: 'Scope',
      dataIndex: 'scope',
      width: 120,
      render: (scope: UpgradeScope) => (
        <Tag color={scope === 'LOGICAL_CLUSTER' ? 'blue' : 'purple'}>
          {getScopeLabel(scope)}
        </Tag>
      )
    },
    {
      title: 'Component',
      dataIndex: 'component',
      width: 380,
      render: (component: string, record) => (
        <div>
          <div>{component}</div>
          {record.scope === 'SHARED_POOL' &&
            component.toLowerCase() === 'tidb' && (
              <Text
                style={{
                  color: '#fa8c16',
                  display: 'block',
                  fontSize: 13,
                  lineHeight: '18px',
                  maxWidth: 360,
                  whiteSpace: 'normal'
                }}
              >
                Upgrades the default TiDB version for new logical clusters;
                <br />
                existing logical clusters are unaffected.
              </Text>
            )}
        </div>
      )
    },
    {
      title: 'Current Version',
      dataIndex: 'currentVersion',
      width: 130,
      render: (value?: string) => value || '-'
    },
    {
      title: 'Current Image Tag',
      dataIndex: 'currentImageTag',
      width: 200,
      render: (value?: string) => value || '-'
    },
    {
      title: 'Target Version',
      dataIndex: 'targetVersion',
      width: 170,
      render: (_: unknown, record) => (
        <Select
          showSearch
          allowClear
          disabled={!record.enabled}
          loading={loadingVersions}
          value={record.targetVersion}
          placeholder="Select target version"
          style={{ width: '100%' }}
          options={(availableVersionsByRow[record.key] || [])
            .map((version) => version.version)
            .filter((version): version is string => Boolean(version))
            .map((version) => ({
              label: version,
              value: version
            }))}
          onChange={(value) => updateRowTargetVersion(record.key, value)}
        />
      )
    },
    {
      title: 'Target Image Tag',
      width: 260,
      render: (_, record) => (
        <Input
          disabled={!record.enabled}
          value={record.targetImageTag}
          placeholder={
            record.loadingTarget
              ? 'Loading...'
              : record.defaultTargetImageTag || 'target image tag'
          }
          onChange={(event) =>
            updateRow(record.key, { targetImageTag: event.target.value })
          }
        />
      )
    }
  ]

  return (
    <>
      <a
        onClick={() => {
          reset()
          setOpen(true)
        }}
        style={{
          color: '#1890ff',
          display: 'block',
          width: '100%',
          textAlign: 'left'
        }}
      >
        Upgrade
      </a>
      <Modal
        open={open}
        title={resourcePoolOnly ? 'Resource Pool Upgrade' : 'Premium Upgrade'}
        width={1400}
        onCancel={() => setOpen(false)}
        destroyOnClose
        footer={[
          <Button key="cancel" onClick={() => setOpen(false)}>
            Cancel
          </Button>,
          <Button key="reload" onClick={loadCurrent} disabled={loading}>
            Reload
          </Button>,
          <Button
            key="next"
            type="primary"
            disabled={!selectedRows.length || loading}
            onClick={handleNext}
          >
            Next
          </Button>
        ]}
      >
        <Space direction="vertical" size="middle" style={{ width: '100%' }}>
          <Alert
            type="warning"
            showIcon
            description={
              resourcePoolOnly
                ? 'This creates a resource pool config change and does not apply it automatically.'
                : 'This creates a premium cluster config change and does not apply it automatically.'
            }
          />

          <Divider style={{ margin: '4px 0' }} />

          <Space direction="vertical" size={4}>
            <Title level={5} style={{ margin: 0 }}>
              Components
            </Title>
            <Text type="secondary">
              Disable rows that should not be upgraded in this run. You can
              modify Target Image Tag to use the expected image tag. Click Next
              to modify config and preview the final change.
            </Text>
          </Space>

          <Table
            rowKey="key"
            loading={loading}
            columns={columns}
            dataSource={rows}
            pagination={
              resourcePoolOnly
                ? { pageSize: 10, showSizeChanger: false }
                : false
            }
            scroll={{ x: 1500, y: 520 }}
            size="small"
          />
        </Space>
      </Modal>
      <Modal
        open={configOpen}
        title={
          resourcePoolOnly
            ? 'Resource Pool Upgrade Config'
            : 'Premium Upgrade Config'
        }
        width={1200}
        onCancel={() => setConfigOpen(false)}
        destroyOnClose
        footer={null}
      >
        <Tabs defaultActiveKey={configDefaultActiveKey}>
          {hasLogicalUpgrade && (
            <Tabs.TabPane tab="Premium Cluster Config" key="logical">
              <CurrentConfig
                getAPI={cloudConfigControllerGetClusterConfiguration}
                getParam={{
                  cluster_type: CloudconfigClusterType.LOGICAL_CLUSTER,
                  cluster_id: logicalClusterId || ''
                }}
                cacheAPI={
                  cloudConfigControllerPreviewClusterConfigurationChange
                }
                cacheParam={{
                  cluster_type: CloudconfigClusterType.LOGICAL_CLUSTER,
                  cluster_id: logicalClusterId || '',
                  change_type: 'logicalcluster',
                  components: logicalComponentsReq
                }}
                previewAPI={
                  cloudConfigControllerPreviewClusterConfigurationChange
                }
                previewParam={{
                  cluster_type: CloudconfigClusterType.LOGICAL_CLUSTER,
                  cluster_id: logicalClusterId || '',
                  change_type: 'logicalcluster',
                  components: logicalComponentsReq
                }}
                createAPI={
                  cloudConfigControllerCreateClusterConfigurationChange
                }
                createParam={{
                  cluster_type: CloudconfigClusterType.LOGICAL_CLUSTER,
                  cluster_id: logicalClusterId || '',
                  change_type: 'logicalcluster',
                  components: logicalComponentsReq
                }}
                type="upgrade"
                upgradeForm={setConfigOpen}
                clusterType={CloudconfigClusterType.LOGICAL_CLUSTER}
              />
            </Tabs.TabPane>
          )}
          {resourcePoolId && hasResourcePoolUpgrade && (
            <Tabs.TabPane tab="Resource Pool Config" key="resource-pool">
              <CurrentConfig
                getAPI={cloudConfigControllerGetClusterConfiguration}
                getParam={{
                  cluster_type: CloudconfigClusterType.SHARED_POOL,
                  cluster_id: resourcePoolId
                }}
                cacheAPI={
                  cloudConfigControllerPreviewClusterConfigurationChange
                }
                cacheParam={{
                  cluster_type: CloudconfigClusterType.SHARED_POOL,
                  cluster_id: resourcePoolId,
                  change_type: 'sharedpool',
                  components: resourcePoolComponentsReq
                }}
                previewAPI={
                  cloudConfigControllerPreviewClusterConfigurationChange
                }
                previewParam={{
                  cluster_type: CloudconfigClusterType.SHARED_POOL,
                  cluster_id: resourcePoolId,
                  change_type: 'sharedpool',
                  components: resourcePoolComponentsReq
                }}
                createAPI={
                  cloudConfigControllerCreateClusterConfigurationChange
                }
                createParam={{
                  cluster_type: CloudconfigClusterType.SHARED_POOL,
                  cluster_id: resourcePoolId,
                  change_type: 'sharedpool',
                  components: resourcePoolComponentsReq
                }}
                type="upgrade"
                upgradeForm={setConfigOpen}
                clusterType={CloudconfigClusterType.SHARED_POOL}
              />
            </Tabs.TabPane>
          )}
        </Tabs>
      </Modal>
    </>
  )
}
