import {
  ApartmentOutlined,
  CheckCircleOutlined,
  ExclamationCircleOutlined,
  FilterOutlined,
  SearchOutlined,
  SafetyCertificateOutlined
} from '@ant-design/icons'
import {
  Alert,
  Button,
  Card,
  Checkbox,
  Col,
  Collapse,
  Descriptions,
  Form,
  Input,
  PageHeader,
  Radio,
  Row,
  Select,
  Space,
  Spin,
  Statistic,
  Steps,
  Table,
  Tag,
  Typography,
  message
} from 'antd'
import type { ColumnsType } from 'antd/lib/table'
import { useEffect, useMemo, useState } from 'react'
import { useQuery } from 'react-query'
import { Link, useHistory } from 'react-router-dom'
import useProfileContext from 'useProfileContext'

import {
  cloudConfigControllerListAvailableClusterVersions,
  cloudConfigControllerListAvailableComponentVersions,
  getCluster,
  listClusters,
  premiumUpgradeControllerCheckTargetConflicts as checkPremiumUpgradeTargetConflicts,
  premiumUpgradeControllerCreatePlan as createPremiumUpgradePlan,
  premiumUpgradeControllerListComponentPolicies as listPremiumUpgradeComponentPolicies,
  premiumUpgradeControllerPreviewPlan as previewPremiumUpgradePlan
} from 'apiClient/services/devops'
import type {
  ApiComponentImageTag as PremiumUpgradeComponentImageTag,
  ApiComponentPolicy as PremiumUpgradeComponentPolicy,
  ApiComponentSelector as PremiumUpgradeComponentSelector,
  ApiPreviewPlanResult as PremiumUpgradePreview,
  ApiPreviewStep as PremiumUpgradePreviewStep,
  ApiPreviewTarget as PremiumUpgradePreviewTarget,
  ApiTargetConflict as PremiumUpgradeTargetConflict,
  ApiUpgradeScope as PremiumUpgradeScope,
  ClusterCluster,
  CloudConfigControllerListAvailableComponentVersionsClusterType,
  CloudconfigClusterComponentVersion,
  CloudconfigClusterType as PremiumClusterType,
  ListClustersParams,
  PremiumupgradeExecutionMode as PremiumUpgradeExecutionMode,
  PremiumupgradeScopeMode as PremiumUpgradeScopeMode
} from 'apiClient/services/devops/interface'

import {
  isPremiumUpgradeTarget,
  PREMIUM_UPGRADE_SERVICE_PLANS,
  premiumUpgradeTargetTypeLabel
} from './targetSupport'
import {
  applyGroupName,
  componentName,
  executionModeName,
  renderUpgradeStatus
} from './utils'

import './index.less'

const { Text, Title } = Typography
const CLUSTER_PAGE_SIZE = 20
const CLUSTER_LOOKUP_PAGE_SIZE = 100

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

type DiscoveryMode = 'cluster_ids' | 'tenant_filter'

type ResolvedComponentPolicy = PremiumUpgradeComponentPolicy &
  Required<
    Pick<
      PremiumUpgradeComponentPolicy,
      'apply_group' | 'atomic_group' | 'cluster_type' | 'component' | 'priority'
    >
  >

type PolicyGroup = {
  key: string
  applyGroup: string
  clusterType: PremiumClusterType
  priority: number
  atomic: boolean
  policies: ResolvedComponentPolicy[]
}

type PreviewExecutionRow = {
  key: string
  target: PremiumUpgradePreviewTarget
  step: PremiumUpgradePreviewStep
}

type DefinitionSectionHeadingProps = {
  title: string
  description: string
  optional?: boolean
}

const DefinitionSectionHeading = ({
  title,
  description,
  optional = false
}: DefinitionSectionHeadingProps) => (
  <div className="premium-upgrade-plan__definition-section-heading">
    <span className="premium-upgrade-plan__definition-section-label">
      <Text strong>{title}</Text>
      {optional && <Tag>Optional</Tag>}
    </span>
    <Text type="secondary">{description}</Text>
  </div>
)

const parseIds = (value: string) =>
  Array.from(
    new Set(
      value
        .split(/[\s,]+/)
        .map((item) => item.trim())
        .filter(Boolean)
    )
  )

const getErrorMessage = (error: unknown) => {
  const candidate = error as {
    message?: string
    response?: { data?: { errmsg?: string; message?: string } }
  }
  return (
    candidate.response?.data?.errmsg ||
    candidate.response?.data?.message ||
    candidate.message ||
    'The request could not be completed'
  )
}

const createIdempotencyKey = () =>
  `premium-upgrade-${Date.now()}-${Math.random().toString(36).slice(2)}`

const isResolvedComponentPolicy = (
  policy: PremiumUpgradeComponentPolicy
): policy is ResolvedComponentPolicy =>
  Boolean(policy.apply_group && policy.cluster_type && policy.component) &&
  policy.priority !== undefined &&
  policy.atomic_group !== undefined

const getPolicyKey = (policy: ResolvedComponentPolicy) =>
  `${policy.cluster_type}/${policy.component}`

const targetLabel = (target: PremiumUpgradePreviewTarget) =>
  `${target.cluster_type} / ${target.cluster_id}`

const PremiumUpgradePlanCreate = () => {
  const history = useHistory()
  const { profile } = useProfileContext()
  const [currentStep, setCurrentStep] = useState(0)
  const [discoveryMode, setDiscoveryMode] =
    useState<DiscoveryMode>('cluster_ids')
  const [clusterIdsInput, setClusterIdsInput] = useState('')
  const [tenantIdsInput, setTenantIdsInput] = useState('')
  const [projectIdsInput, setProjectIdsInput] = useState('')
  const [clusterSearchStarted, setClusterSearchStarted] = useState(false)
  const [candidateClusters, setCandidateClusters] = useState<ClusterCluster[]>(
    []
  )
  const [candidateTotal, setCandidateTotal] = useState(0)
  const [candidatePage, setCandidatePage] = useState(1)
  const [candidatePageSize, setCandidatePageSize] = useState(CLUSTER_PAGE_SIZE)
  const [requestedExactIds, setRequestedExactIds] = useState<string[]>([])
  const [selectedClusters, setSelectedClusters] = useState<
    Record<string, ClusterCluster>
  >({})
  const [clusterLoading, setClusterLoading] = useState(false)
  const [targetConflictLoading, setTargetConflictLoading] = useState(false)
  const [targetConflicts, setTargetConflicts] = useState<
    PremiumUpgradeTargetConflict[]
  >([])
  const [planName, setPlanName] = useState('')
  const [reason, setReason] = useState('')
  const [desiredVersion, setDesiredVersion] = useState('')
  const [executionMode, setExecutionMode] =
    useState<PremiumUpgradeExecutionMode>('orchestrated')
  const [scopeMode, setScopeMode] =
    useState<PremiumUpgradeScopeMode>('selected')
  const [selectedPolicyKeys, setSelectedPolicyKeys] = useState<string[]>([])
  const [componentImageTags, setComponentImageTags] = useState<
    Record<string, string>
  >({})
  const [preview, setPreview] = useState<PremiumUpgradePreview>()
  const [previewLoading, setPreviewLoading] = useState(false)
  const [submitLoading, setSubmitLoading] = useState(false)
  const [requestError, setRequestError] = useState('')
  const [idempotencyKey, setIdempotencyKey] = useState('')

  const policiesQuery = useQuery(
    ['premiumUpgradeComponentPolicies'],
    async () => {
      const response = await listPremiumUpgradeComponentPolicies()
      return response.data.items || []
    },
    { staleTime: 10 * 60 * 1000 }
  )

  const policyGroups = useMemo<PolicyGroup[]>(() => {
    const grouped = new Map<string, PolicyGroup>()
    ;(policiesQuery.data || []).forEach((policy) => {
      if (!isResolvedComponentPolicy(policy)) return
      const key = `${policy.cluster_type}/${policy.apply_group}`
      const current = grouped.get(key) || {
        key,
        applyGroup: policy.apply_group,
        clusterType: policy.cluster_type,
        priority: policy.priority,
        atomic: policy.atomic_group,
        policies: []
      }
      current.policies.push(policy)
      grouped.set(key, current)
    })
    return Array.from(grouped.values())
      .map((group) => ({
        ...group,
        policies: [...group.policies].sort((left, right) =>
          left.component.localeCompare(right.component)
        )
      }))
      .sort(
        (left, right) =>
          left.priority - right.priority || left.key.localeCompare(right.key)
      )
  }, [policiesQuery.data])

  const selectedClusterList = useMemo(
    () =>
      Object.values(selectedClusters).sort((left, right) =>
        (left.id || '').localeCompare(right.id || '', undefined, {
          numeric: true
        })
      ),
    [selectedClusters]
  )

  useEffect(() => {
    setTargetConflicts([])
  }, [selectedClusters])

  const conflictingClusterIDs = useMemo(
    () =>
      new Set(
        targetConflicts
          .map((conflict) => conflict.cluster_id)
          .filter((clusterID): clusterID is string => !!clusterID)
      ),
    [targetConflicts]
  )

  const selectedClusterTypes = useMemo(
    () =>
      new Set<PremiumClusterType>(
        selectedClusterList.map((cluster) =>
          cluster.deploy_type === 'DEPLOY_TYPE_RESOURCE_POOL'
            ? 'SHARED_POOL'
            : 'LOGICAL_CLUSTER'
        )
      ),
    [selectedClusterList]
  )

  const selectedClusterTypeList = useMemo(
    () => Array.from(selectedClusterTypes).sort(),
    [selectedClusterTypes]
  )

  const applicablePolicyGroups = useMemo(
    () =>
      policyGroups.filter((group) =>
        selectedClusterTypes.has(group.clusterType)
      ),
    [policyGroups, selectedClusterTypes]
  )

  const versionComponentTargets = useMemo(
    () =>
      Array.from(
        new Map(
          applicablePolicyGroups.flatMap((group) =>
            group.policies.map(
              (policy) =>
                [
                  `${policy.cluster_type}/${policy.component}`,
                  {
                    clusterType: policy.cluster_type,
                    component: policy.component
                  }
                ] as const
            )
          )
        ).values()
      ).sort((left, right) =>
        `${left.clusterType}/${left.component}`.localeCompare(
          `${right.clusterType}/${right.component}`
        )
      ),
    [applicablePolicyGroups]
  )

  const availableTargetVersionsQuery = useQuery(
    [
      'premiumUpgradeAvailableTargetVersions',
      selectedClusterTypeList,
      versionComponentTargets.map(
        ({ clusterType, component }) => `${clusterType}/${component}`
      )
    ],
    async () => {
      if (!versionComponentTargets.length) {
        throw new Error('No component policies are available for these targets')
      }

      const [clusterVersionResults, componentVersionResults] =
        await Promise.all([
          Promise.allSettled(
            selectedClusterTypeList.map((clusterType) =>
              cloudConfigControllerListAvailableClusterVersions(clusterType)
            )
          ),
          Promise.allSettled(
            versionComponentTargets.map(({ clusterType, component }) =>
              cloudConfigControllerListAvailableComponentVersions(component, {
                cluster_type:
                  clusterType as CloudConfigControllerListAvailableComponentVersionsClusterType
              })
            )
          )
        ])
      const versions = new Map<
        string,
        { version: string; isDefault: boolean }
      >()

      clusterVersionResults.forEach((result) => {
        if (result.status !== 'fulfilled') return
        const items = result.value.data.items || []
        items.forEach((item) => {
          if (!item.version) return
          const current = versions.get(item.version)
          versions.set(item.version, {
            version: item.version,
            isDefault: Boolean(current?.isDefault || item.is_default)
          })
        })
      })
      componentVersionResults.forEach((result) => {
        if (result.status !== 'fulfilled') return
        const responseVersions = new Set([
          ...(result.value.data.versions || []),
          ...(result.value.data.detail || [])
            .map((item) => item.version)
            .filter((version): version is string => !!version)
        ])
        responseVersions.forEach((version) => {
          if (versions.has(version)) return
          versions.set(version, { version, isDefault: false })
        })
      })

      if (!versions.size) {
        const failedResult = [
          ...clusterVersionResults,
          ...componentVersionResults
        ].find((result) => result.status === 'rejected')
        if (failedResult?.status === 'rejected') throw failedResult.reason
      }

      return Array.from(versions.values()).sort(
        (left, right) =>
          Number(right.isDefault) - Number(left.isDefault) ||
          right.version.localeCompare(left.version, undefined, {
            numeric: true,
            sensitivity: 'base'
          })
      )
    },
    {
      enabled:
        currentStep === 1 &&
        selectedClusterTypeList.length > 0 &&
        !policiesQuery.isLoading,
      staleTime: 10 * 60 * 1000
    }
  )

  const availableTargetVersions = availableTargetVersionsQuery.data
  const targetVersionOptions = (availableTargetVersions || []).map(
    ({ version, isDefault }) => ({
      label: isDefault ? `${version} (Default)` : version,
      value: version
    })
  )

  useEffect(() => {
    if (
      !desiredVersion ||
      !availableTargetVersions ||
      availableTargetVersionsQuery.isFetching
    ) {
      return
    }
    if (
      !availableTargetVersions.some(({ version }) => version === desiredVersion)
    ) {
      setDesiredVersion('')
      setComponentImageTags({})
    }
  }, [
    availableTargetVersions,
    availableTargetVersionsQuery.isFetching,
    desiredVersion
  ])

  const policyGroupSections = useMemo(
    () =>
      [
        {
          clusterType: 'SHARED_POOL' as PremiumClusterType,
          title: 'Resource Pool Components',
          description: 'Components upgraded on each selected resource pool.'
        },
        {
          clusterType: 'LOGICAL_CLUSTER' as PremiumClusterType,
          title: 'Logical Cluster Components',
          description:
            'Components upgraded independently on each selected logical cluster.'
        }
      ]
        .filter((section) => selectedClusterTypes.has(section.clusterType))
        .map((section) => ({
          ...section,
          groups: applicablePolicyGroups.filter(
            (group) => group.clusterType === section.clusterType
          )
        }))
        .filter((section) => section.groups.length > 0),
    [applicablePolicyGroups, selectedClusterTypes]
  )

  const selectedPolicies = useMemo(
    () =>
      applicablePolicyGroups
        .flatMap((group) => group.policies)
        .filter((policy) => selectedPolicyKeys.includes(getPolicyKey(policy))),
    [applicablePolicyGroups, selectedPolicyKeys]
  )

  const selectedApplyGroupCount = useMemo(
    () =>
      applicablePolicyGroups.filter((group) =>
        group.policies.some((policy) =>
          selectedPolicyKeys.includes(getPolicyKey(policy))
        )
      ).length,
    [applicablePolicyGroups, selectedPolicyKeys]
  )

  useEffect(() => {
    const applicablePolicyKeys = new Set(
      applicablePolicyGroups.flatMap((group) =>
        group.policies.map(getPolicyKey)
      )
    )
    setSelectedPolicyKeys((current) =>
      current.filter((key) => applicablePolicyKeys.has(key))
    )
  }, [applicablePolicyGroups])

  const imageTagPolicies = useMemo(() => {
    const policies =
      scopeMode === 'selected'
        ? selectedPolicies
        : applicablePolicyGroups.flatMap((group) => group.policies)
    return Array.from(
      new Map(
        policies.map((policy) => [
          `${policy.cluster_type}/${policy.component}`,
          policy
        ])
      ).values()
    ).sort(
      (left, right) =>
        left.priority - right.priority ||
        `${left.cluster_type}/${left.component}`.localeCompare(
          `${right.cluster_type}/${right.component}`
        )
    )
  }, [applicablePolicyGroups, scopeMode, selectedPolicies])

  const defaultImageTagsQuery = useQuery(
    [
      'premiumUpgradeDefaultImageTags',
      desiredVersion,
      imageTagPolicies.map(
        (policy) => `${policy.cluster_type}/${policy.component}`
      )
    ],
    async () => {
      const entries = await Promise.all(
        imageTagPolicies.map(async (policy) => {
          const response =
            await cloudConfigControllerListAvailableComponentVersions(
              policy.component,
              {
                cluster_type:
                  policy.cluster_type as CloudConfigControllerListAvailableComponentVersionsClusterType
              }
            )
          const availableVersion = (response.data.detail || []).find(
            (version) => version.version === desiredVersion
          )
          return [
            `${policy.cluster_type}/${policy.component}`,
            getAvailableVersionImageTag(availableVersion)
          ] as const
        })
      )
      const defaultImageTags: Record<string, string> = {}
      entries.forEach(([key, imageTag]) => {
        if (imageTag) defaultImageTags[key] = imageTag
      })
      return defaultImageTags
    },
    {
      enabled: Boolean(
        currentStep === 1 && desiredVersion && imageTagPolicies.length
      ),
      staleTime: 10 * 60 * 1000
    }
  )

  const defaultImageTags = defaultImageTagsQuery.data || {}
  const defaultImageTagsLoading = Boolean(
    desiredVersion && defaultImageTagsQuery.isFetching
  )

  const buildClusterQuery = (): ListClustersParams | undefined => {
    const clusterIds = parseIds(clusterIdsInput)
    const tenantIds = parseIds(tenantIdsInput)
    const projectIds = parseIds(projectIdsInput)
    const idsToValidate =
      discoveryMode === 'cluster_ids'
        ? clusterIds
        : [...tenantIds, ...projectIds]
    const invalidIds = idsToValidate.filter((id) => !/^\d+$/.test(id))
    if (invalidIds.length) {
      message.error(`IDs must be numeric: ${invalidIds.join(', ')}`)
      return undefined
    }
    if (discoveryMode === 'cluster_ids' && !clusterIds.length) {
      message.warning('Enter at least one cluster ID')
      return undefined
    }
    if (discoveryMode === 'tenant_filter' && !tenantIds.length) {
      message.warning('Enter at least one tenant ID')
      return undefined
    }
    setRequestedExactIds(discoveryMode === 'cluster_ids' ? clusterIds : [])
    return {
      deploy_type:
        discoveryMode === 'tenant_filter' ? 'DEPLOY_TYPE_NEXTGEN' : undefined,
      cluster_id: discoveryMode === 'cluster_ids' ? clusterIds : undefined,
      tenant_id: discoveryMode === 'tenant_filter' ? tenantIds : undefined,
      project_id:
        discoveryMode === 'tenant_filter' && projectIds.length
          ? projectIds
          : undefined
    }
  }

  const loadAllClusterPages = async (query: ListClustersParams) => {
    const items: ClusterCluster[] = []
    let page = 1
    let fetched = 0
    let total = 0
    do {
      const response = await listClusters({
        ...query,
        page,
        per_page: CLUSTER_LOOKUP_PAGE_SIZE
      })
      const batch = response.data.items || []
      total = response.data.total || batch.length
      fetched += batch.length
      items.push(...batch)
      if (!batch.length) break
      page += 1
    } while (fetched < total)
    return items
  }

  const loadClustersByTenant = async (query: ListClustersParams) => {
    setClusterLoading(true)
    setRequestError('')
    try {
      const logicalClustersPromise = Promise.all(
        PREMIUM_UPGRADE_SERVICE_PLANS.map((servicePlan) =>
          loadAllClusterPages({ ...query, service_plan: servicePlan })
        )
      ).then((results) => results.flat())
      const resourcePoolsPromise = query.project_id?.length
        ? Promise.resolve<ClusterCluster[]>([])
        : Promise.all(
            (query.tenant_id || []).flatMap((tenantId) =>
              PREMIUM_UPGRADE_SERVICE_PLANS.map((servicePlan) =>
                listClusters({
                  is_resource_pool: true,
                  service_plan: servicePlan,
                  tenant_id: [tenantId]
                })
              )
            )
          ).then((responses) =>
            responses.flatMap((response) => response.data.items || [])
          )
      const [logicalClusters, resourcePools] = await Promise.all([
        logicalClustersPromise,
        resourcePoolsPromise
      ])
      const items = Array.from(
        new Map(
          [...logicalClusters, ...resourcePools]
            .filter(isPremiumUpgradeTarget)
            .map((cluster) => [cluster.id, cluster])
        ).values()
      )
      setCandidateClusters(items)
      setCandidateTotal(items.length)
      setCandidatePage(1)
    } catch (error) {
      setCandidateClusters([])
      setCandidateTotal(0)
      setRequestError(getErrorMessage(error))
    } finally {
      setClusterLoading(false)
    }
  }

  const loadClustersByID = async (clusterIds: string[]) => {
    setClusterLoading(true)
    setRequestError('')
    try {
      const results = await Promise.allSettled(
        clusterIds.map((clusterId) => getCluster(clusterId))
      )
      const items = results
        .filter(
          (
            result
          ): result is PromiseFulfilledResult<
            Awaited<ReturnType<typeof getCluster>>
          > => result.status === 'fulfilled'
        )
        .map((result) => result.value.data as ClusterCluster)
        .filter(isPremiumUpgradeTarget)
      setCandidateClusters(items)
      setCandidateTotal(items.length)
      setCandidatePage(1)
      if (!items.length) {
        const failed = results.find((result) => result.status === 'rejected')
        if (failed?.status === 'rejected') {
          setRequestError(getErrorMessage(failed.reason))
        }
      }
    } finally {
      setClusterLoading(false)
    }
  }

  const searchClusters = async () => {
    const query = buildClusterQuery()
    if (!query) return
    setClusterSearchStarted(true)
    if (discoveryMode === 'cluster_ids') {
      await loadClustersByID(query.cluster_id || [])
      return
    }
    await loadClustersByTenant(query)
  }

  const changeDiscoveryMode = (mode: DiscoveryMode) => {
    if (mode === discoveryMode) return
    setDiscoveryMode(mode)
    setCandidateClusters([])
    setCandidateTotal(0)
    setCandidatePage(1)
    setRequestedExactIds([])
    setClusterSearchStarted(false)
    setRequestError('')
  }

  const selectAllMatchingClusters = () => {
    if (candidateTotal === 0) return
    setSelectedClusters((current) => ({
      ...current,
      ...Object.fromEntries(
        candidateClusters
          .filter((cluster) => Boolean(cluster.id))
          .map((cluster) => [cluster.id!, cluster])
      )
    }))
  }

  const requestedIdsNotFound = useMemo(() => {
    if (!requestedExactIds.length || candidatePage !== 1) return []
    const found = new Set(candidateClusters.map((cluster) => cluster.id))
    return requestedExactIds.filter((id) => !found.has(id))
  }, [candidateClusters, candidatePage, requestedExactIds])

  const clusterColumns = useMemo<ColumnsType<ClusterCluster>>(
    () => [
      {
        title: 'Cluster',
        dataIndex: 'id',
        width: 240,
        render: (id: string, cluster) => (
          <Space direction="vertical" size={0}>
            <Text strong>{cluster.name || 'Unnamed cluster'}</Text>
            <Text code>{id}</Text>
          </Space>
        )
      },
      {
        title: 'Tenant ID',
        dataIndex: 'tenant_id',
        width: 200,
        render: (id?: string) =>
          id ? (
            <Text code style={{ whiteSpace: 'nowrap' }}>
              {id}
            </Text>
          ) : (
            '-'
          )
      },
      {
        title: 'Project ID',
        dataIndex: 'project_id',
        width: 200,
        render: (id?: string) =>
          id ? (
            <Text code style={{ whiteSpace: 'nowrap' }}>
              {id}
            </Text>
          ) : (
            '-'
          )
      },
      {
        title: 'Target Type',
        key: 'target_type',
        width: 150,
        render: (_, cluster) => (
          <Tag
            color={
              cluster.deploy_type === 'DEPLOY_TYPE_RESOURCE_POOL'
                ? 'purple'
                : 'blue'
            }
          >
            {premiumUpgradeTargetTypeLabel(cluster)}
          </Tag>
        )
      },
      {
        title: 'Location',
        key: 'location',
        width: 230,
        render: (_, cluster) => (
          <Space size={4} wrap>
            {cluster.provider && <Tag>{cluster.provider}</Tag>}
            <Text>{cluster.region || cluster.logical_region || '-'}</Text>
          </Space>
        )
      },
      {
        title: 'Version',
        dataIndex: 'version',
        width: 130,
        render: (version?: string) => version || '-'
      },
      {
        title: 'Status',
        dataIndex: 'status',
        width: 130,
        render: (status?: string) => <Tag>{status || '-'}</Tag>
      }
    ],
    []
  )

  const selectedClusterColumns = useMemo<ColumnsType<ClusterCluster>>(
    () => [
      {
        title: 'Cluster ID',
        dataIndex: 'id',
        width: 210,
        render: (id?: string) => (id ? <Text code>{id}</Text> : '-')
      },
      {
        title: 'Cluster Name',
        dataIndex: 'name',
        width: 220,
        render: (name?: string) => name || 'Unnamed cluster'
      },
      {
        title: 'Tenant ID',
        dataIndex: 'tenant_id',
        width: 170,
        render: (id?: string) => (id ? <Text code>{id}</Text> : '-')
      },
      {
        title: 'Target Type',
        key: 'target_type',
        width: 150,
        render: (_, cluster) => (
          <Tag
            color={
              cluster.deploy_type === 'DEPLOY_TYPE_RESOURCE_POOL'
                ? 'purple'
                : 'blue'
            }
          >
            {premiumUpgradeTargetTypeLabel(cluster)}
          </Tag>
        )
      },
      {
        title: 'Version',
        dataIndex: 'version',
        width: 130,
        render: (version?: string) => version || '-'
      },
      {
        title: 'Status',
        dataIndex: 'status',
        width: 130,
        render: (status?: string) => <Tag>{status || '-'}</Tag>
      },
      {
        title: '',
        key: 'actions',
        width: 90,
        align: 'right',
        render: (_, cluster) => (
          <Button
            danger
            type="link"
            onClick={() => {
              if (!cluster.id) return
              setSelectedClusters((current) => {
                const next = { ...current }
                delete next[cluster.id!]
                return next
              })
            }}
          >
            Remove
          </Button>
        )
      }
    ],
    []
  )

  const buildScope = (): PremiumUpgradeScope => ({
    mode: scopeMode,
    components:
      scopeMode === 'selected'
        ? selectedPolicies.map<PremiumUpgradeComponentSelector>((policy) => ({
            cluster_type: policy.cluster_type,
            component: policy.component
          }))
        : undefined
  })

  const buildComponentImageTags = (): PremiumUpgradeComponentImageTag[] =>
    imageTagPolicies.flatMap((policy) => {
      const key = `${policy.cluster_type}/${policy.component}`
      const imageTag = componentImageTags[key]?.trim()
      const defaultImageTag = defaultImageTags[key]?.trim()
      return imageTag && imageTag !== defaultImageTag
        ? [
            {
              cluster_type: policy.cluster_type,
              component: policy.component,
              image_tag: imageTag
            }
          ]
        : []
    })

  const generatePreview = async () => {
    if (!planName.trim() || !reason.trim() || !desiredVersion.trim()) {
      message.warning('Plan name, reason, and target version are required')
      return
    }
    if (scopeMode === 'selected' && !selectedPolicies.length) {
      message.warning('Select at least one Apply Group')
      return
    }
    setPreviewLoading(true)
    setRequestError('')
    try {
      const response = await previewPremiumUpgradePlan({
        cluster_ids: selectedClusterList.map((cluster) => cluster.id!),
        desired_version: desiredVersion.trim(),
        execution_mode: executionMode,
        component_image_tags: buildComponentImageTags(),
        scope: buildScope()
      })
      if (!response.data.preview) {
        throw new Error('The backend returned an empty preview')
      }
      setPreview(response.data.preview)
      setIdempotencyKey(createIdempotencyKey())
      setCurrentStep(2)
    } catch (error) {
      setPreview(undefined)
      setRequestError(getErrorMessage(error))
    } finally {
      setPreviewLoading(false)
    }
  }

  const submitPlan = async () => {
    if (!preview || !idempotencyKey) return
    const {
      cluster_ids,
      component_image_tags,
      desired_version,
      execution_mode,
      manifest_hash,
      scope
    } = preview
    if (!cluster_ids?.length || !desired_version || !manifest_hash || !scope) {
      setRequestError('The backend returned an incomplete preview')
      return
    }
    setSubmitLoading(true)
    setRequestError('')
    try {
      const response = await createPremiumUpgradePlan(
        {
          cluster_ids,
          component_image_tags,
          desired_version,
          execution_mode,
          scope,
          name: planName.trim(),
          reason: reason.trim(),
          expected_manifest_hash: manifest_hash
        },
        { headers: { 'Idempotency-Key': idempotencyKey } }
      )
      if (!response.data.plan) {
        throw new Error('The backend returned an empty plan')
      }
      message.success('Premium upgrade plan submitted for approval')
      history.push(`/premiumUpgradePlans/${response.data.plan.id}`)
    } catch (error) {
      setRequestError(getErrorMessage(error))
    } finally {
      setSubmitLoading(false)
    }
  }

  const executionRows = useMemo<PreviewExecutionRow[]>(
    () =>
      (preview?.targets || [])
        .flatMap((target) =>
          (target.steps || []).map((step) => ({
            key: `${target.cluster_type}/${target.cluster_id}/${step.apply_group}`,
            target,
            step
          }))
        )
        .sort(
          (left, right) =>
            (left.step.sequence || 0) - (right.step.sequence || 0) ||
            left.key.localeCompare(right.key)
        ),
    [preview]
  )

  const executionColumns = useMemo<ColumnsType<PreviewExecutionRow>>(
    () => [
      {
        title: 'Sequence',
        dataIndex: ['step', 'sequence'],
        width: 95,
        render: (sequence: number) => <Tag color="blue">{sequence}</Tag>
      },
      {
        title: 'Actual Target',
        dataIndex: 'target',
        width: 250,
        render: (target: PremiumUpgradePreviewTarget) => (
          <Space direction="vertical" size={0}>
            <Text strong>{targetLabel(target)}</Text>
            <Text type="secondary">
              Source: {(target.source_cluster_ids || []).join(', ') || '-'}
            </Text>
          </Space>
        )
      },
      {
        title: 'ConfigurationChange',
        dataIndex: ['step', 'apply_group'],
        width: 220,
        render: (applyGroup: string, row) => (
          <Space direction="vertical" size={0}>
            <Text strong>{applyGroupName(applyGroup)}</Text>
            <Text type="secondary">
              {(row.step.components || []).length > 1
                ? 'Atomic group · one Change'
                : 'Single component · separate Change'}
            </Text>
          </Space>
        )
      },
      {
        title: 'Components and Versions',
        dataIndex: ['step', 'components'],
        width: 420,
        render: (components: PremiumUpgradePreviewStep['components']) => (
          <Space direction="vertical" size={2}>
            {(components || []).map((component) => (
              <span key={component.component}>
                <Tag>{componentName(component.component || 'unknown')}</Tag>
                <Text type="secondary">{component.from_version}</Text>
                <Text> → </Text>
                <Text strong>{component.to_version}</Text>
                {component.noop && <Tag color="default">No change</Tag>}
                {(component.from_image_tag || component.to_image_tag) && (
                  <>
                    <br />
                    <Text type="secondary">Image: </Text>
                    <Text code>{component.from_image_tag || '-'}</Text>
                    <Text type="secondary"> → </Text>
                    <Text code>{component.to_image_tag || '-'}</Text>
                    {component.image_tag_source === 'requested' && (
                      <Tag color="cyan">Override</Tag>
                    )}
                  </>
                )}
              </span>
            ))}
          </Space>
        )
      }
    ],
    []
  )

  const renderClusterStep = () => (
    <div className="premium-upgrade-plan__create-step">
      <div className="premium-upgrade-plan__step-heading">
        <span>1</span>
        <div>
          <Title level={4}>Select Premium or Essential V2 targets</Title>
          <Text type="secondary">
            Find clusters by exact cluster IDs or filter by tenant IDs. Search
            results are never submitted automatically—you choose the final list.
          </Text>
        </div>
      </div>

      <section className="premium-upgrade-plan__cluster-discovery">
        <div className="premium-upgrade-plan__cluster-discovery-heading">
          <DefinitionSectionHeading
            title="Find clusters"
            description="Start with known cluster IDs, or switch to tenant and project filters for broader discovery."
          />
          <Radio.Group
            className="premium-upgrade-plan__discovery-modes"
            buttonStyle="solid"
            disabled={clusterLoading}
            value={discoveryMode}
            onChange={(event) =>
              changeDiscoveryMode(event.target.value as DiscoveryMode)
            }
          >
            <Radio.Button value="cluster_ids">
              <SearchOutlined />
              Exact cluster IDs
            </Radio.Button>
            <Radio.Button value="tenant_filter">
              <FilterOutlined />
              Tenant &amp; project filters
            </Radio.Button>
          </Radio.Group>
        </div>

        <div className="premium-upgrade-plan__cluster-query">
          {discoveryMode === 'cluster_ids' ? (
            <div className="premium-upgrade-plan__input-block">
              <span className="premium-upgrade-plan__input-label">
                <Text strong>Cluster IDs</Text>
                <Text type="secondary">
                  Paste one or many numeric IDs, separated by commas, spaces, or
                  new lines.
                </Text>
              </span>
              <Input.TextArea
                value={clusterIdsInput}
                autoSize={{ minRows: 2, maxRows: 5 }}
                placeholder="Example: 101234567890, 101234567891"
                onChange={(event) => setClusterIdsInput(event.target.value)}
              />
            </div>
          ) : (
            <Row gutter={16}>
              <Col xs={24} md={12}>
                <div className="premium-upgrade-plan__input-block">
                  <span className="premium-upgrade-plan__input-label">
                    <Text strong>Tenant IDs</Text>
                    <Text type="secondary">Required</Text>
                  </span>
                  <Input.TextArea
                    value={tenantIdsInput}
                    autoSize={{ minRows: 2, maxRows: 5 }}
                    placeholder="Enter numeric tenant IDs"
                    onChange={(event) => setTenantIdsInput(event.target.value)}
                  />
                </div>
              </Col>
              <Col xs={24} md={12}>
                <div className="premium-upgrade-plan__input-block">
                  <span className="premium-upgrade-plan__input-label">
                    <Text strong>Project IDs</Text>
                    <Text type="secondary">Optional</Text>
                  </span>
                  <Input.TextArea
                    value={projectIdsInput}
                    autoSize={{ minRows: 2, maxRows: 5 }}
                    placeholder="Narrow the tenant results by project ID."
                    onChange={(event) => setProjectIdsInput(event.target.value)}
                  />
                </div>
              </Col>
            </Row>
          )}
          <div className="premium-upgrade-plan__query-actions">
            <Text type="secondary">
              {discoveryMode === 'cluster_ids'
                ? 'Matches Premium and Essential V2 logical clusters and Resource Pools.'
                : projectIdsInput.trim()
                ? 'Project filtering returns Premium and Essential V2 logical clusters only.'
                : 'Matches Premium and Essential V2 logical clusters and Resource Pools.'}
            </Text>
            <Button
              type="primary"
              size="large"
              icon={<SearchOutlined />}
              loading={clusterLoading}
              onClick={searchClusters}
            >
              Find upgrade targets
            </Button>
          </div>
        </div>
      </section>

      {requestedIdsNotFound.length > 0 && (
        <Alert
          type="warning"
          showIcon
          message="Some requested cluster IDs were not returned"
          description={requestedIdsNotFound.join(', ')}
        />
      )}

      {clusterSearchStarted && (
        <section className="premium-upgrade-plan__cluster-results">
          <div className="premium-upgrade-plan__result-heading">
            <span>
              <span className="premium-upgrade-plan__result-title">
                <Text strong>Search results</Text>
                <Tag color={candidateTotal ? 'blue' : undefined}>
                  {candidateTotal}
                </Tag>
              </span>
              <Text type="secondary">
                {selectedClusterList.length} selected across all searches
              </Text>
            </span>
            <Space>
              <Button
                disabled={!candidateTotal}
                onClick={selectAllMatchingClusters}
              >
                Select all {candidateTotal || ''} matches
              </Button>
              <Button
                disabled={!selectedClusterList.length}
                onClick={() => setSelectedClusters({})}
              >
                Clear selection
              </Button>
            </Space>
          </div>

          <Table<ClusterCluster>
            rowKey={(cluster) => cluster.id || ''}
            size="small"
            loading={clusterLoading}
            columns={clusterColumns}
            dataSource={candidateClusters}
            rowSelection={{
              preserveSelectedRowKeys: true,
              selectedRowKeys: selectedClusterList.map(
                (cluster) => cluster.id!
              ),
              onSelect: (cluster, selected) => {
                if (!cluster.id) return
                setSelectedClusters((current) => {
                  const next = { ...current }
                  if (selected) next[cluster.id!] = cluster
                  else delete next[cluster.id!]
                  return next
                })
              },
              onSelectAll: (selected, selectedRows, changedRows) => {
                setSelectedClusters((current) => {
                  const next = { ...current }
                  if (selected) {
                    selectedRows.forEach((cluster) => {
                      if (cluster.id) next[cluster.id] = cluster
                    })
                  } else {
                    changedRows.forEach((cluster) => {
                      if (cluster.id) delete next[cluster.id]
                    })
                  }
                  return next
                })
              }
            }}
            pagination={{
              current: candidatePage,
              pageSize: candidatePageSize,
              total: candidateTotal,
              showSizeChanger: true,
              onChange: (page, pageSize) => {
                setCandidatePage(page)
                setCandidatePageSize(pageSize)
              }
            }}
            scroll={{ x: 1250 }}
          />
        </section>
      )}

      {selectedClusterList.length > 0 && (
        <div
          className={`premium-upgrade-plan__selected-targets ${
            targetConflicts.length ? 'has-conflicts' : ''
          }`}
        >
          <div className="premium-upgrade-plan__selected-targets-header">
            <span className="premium-upgrade-plan__selected-targets-summary">
              {targetConflicts.length ? (
                <ExclamationCircleOutlined />
              ) : (
                <CheckCircleOutlined />
              )}
              <span>
                <Text strong>
                  Selected Clusters ({selectedClusterList.length})
                </Text>
                <Text type="secondary">
                  {targetConflicts.length
                    ? 'Resolve the active-plan conflicts before continuing.'
                    : 'Every cluster listed below will be frozen into the approval manifest.'}
                </Text>
              </span>
            </span>
          </div>
          {targetConflicts.length > 0 && (
            <Alert
              className="premium-upgrade-plan__target-conflicts"
              type="error"
              showIcon
              message="Some selected clusters are already in active plans"
              description={
                <div className="premium-upgrade-plan__target-conflict-content">
                  <Text>
                    Remove these clusters before configuring a new upgrade plan.
                  </Text>
                  <ul>
                    {targetConflicts.map((conflict) => {
                      const clusterID = conflict.cluster_id || 'Unknown'
                      const cluster = selectedClusters[clusterID]
                      return (
                        <li
                          key={`${conflict.plan_id}/${conflict.cluster_type}/${clusterID}`}
                        >
                          <span>
                            <Text strong>{cluster?.name || clusterID}</Text>{' '}
                            <Text code>{clusterID}</Text>
                          </span>
                          <span>
                            Active plan:{' '}
                            {conflict.plan_id ? (
                              <Link
                                to={`/premiumUpgradePlans/${conflict.plan_id}`}
                                target="_blank"
                                rel="noopener noreferrer"
                              >
                                {conflict.plan_name ||
                                  `Plan #${conflict.plan_id}`}
                              </Link>
                            ) : (
                              conflict.plan_name || 'Unknown plan'
                            )}{' '}
                            {renderUpgradeStatus(conflict.plan_status)}
                          </span>
                        </li>
                      )
                    })}
                  </ul>
                  <Button
                    danger
                    onClick={() =>
                      setSelectedClusters((current) =>
                        Object.fromEntries(
                          Object.entries(current).filter(
                            ([clusterID]) =>
                              !conflictingClusterIDs.has(clusterID)
                          )
                        )
                      )
                    }
                  >
                    Remove conflicting clusters
                  </Button>
                </div>
              }
            />
          )}
          <Table<ClusterCluster>
            className="premium-upgrade-plan__selected-targets-table"
            rowKey={(cluster) => cluster.id || ''}
            size="small"
            columns={selectedClusterColumns}
            dataSource={selectedClusterList}
            rowClassName={(cluster) =>
              cluster.id && conflictingClusterIDs.has(cluster.id)
                ? 'premium-upgrade-plan__conflicting-target-row'
                : ''
            }
            pagination={false}
            scroll={{ x: 860, y: 320 }}
          />
        </div>
      )}
    </div>
  )

  const renderDefinitionStep = () => (
    <div className="premium-upgrade-plan__create-step">
      <div className="premium-upgrade-plan__step-heading">
        <span>2</span>
        <div>
          <Title level={4}>Configure the upgrade</Title>
          <Text type="secondary">
            Complete the required sections in order: describe the plan, choose
            what to upgrade, then decide how each ConfigurationChange starts.
          </Text>
        </div>
      </div>

      <section className="premium-upgrade-plan__definition-section">
        <DefinitionSectionHeading
          title="Plan Details"
          description="Name this rollout and record the version and reason approvers will review."
        />
        <Form layout="vertical" requiredMark>
          <Row gutter={18}>
            <Col xs={24} md={12}>
              <Form.Item label="Plan Name" required>
                <Input
                  size="large"
                  value={planName}
                  placeholder="Example: Premium 26.3 production rollout"
                  onChange={(event) => setPlanName(event.target.value)}
                />
              </Form.Item>
            </Col>
            <Col xs={24} md={12}>
              <Form.Item
                label="Target Version"
                required
                extra={
                  availableTargetVersionsQuery.isError ? (
                    <Text type="danger">
                      Unable to load available versions from Config Center.
                    </Text>
                  ) : (
                    'Versions available to any selected target or component are shown. Preview validates the final selection.'
                  )
                }
              >
                <Select
                  showSearch
                  allowClear
                  size="large"
                  optionFilterProp="label"
                  value={desiredVersion || undefined}
                  loading={
                    availableTargetVersionsQuery.isLoading ||
                    availableTargetVersionsQuery.isFetching
                  }
                  options={targetVersionOptions}
                  placeholder="Select a Config Center version"
                  notFoundContent={
                    availableTargetVersionsQuery.isLoading ? (
                      <Spin size="small" />
                    ) : availableTargetVersionsQuery.isError ? (
                      'Unable to load versions'
                    ) : (
                      'No available versions'
                    )
                  }
                  onChange={(value) => {
                    setDesiredVersion(value || '')
                    setComponentImageTags({})
                  }}
                />
              </Form.Item>
            </Col>
          </Row>
          <Form.Item
            label="Reason"
            required
            extra="This reason is included in the parent approval."
          >
            <Input.TextArea
              value={reason}
              rows={3}
              placeholder="Explain why these clusters and components should be upgraded together."
              onChange={(event) => setReason(event.target.value)}
            />
          </Form.Item>
        </Form>
        <div className="premium-upgrade-plan__applicant">
          <Text type="secondary">Applicant</Text>
          <Text strong>{profile.userEmail || 'Current signed-in user'}</Text>
        </div>
      </section>

      <section className="premium-upgrade-plan__definition-section">
        <div className="premium-upgrade-plan__definition-section-heading-row">
          <DefinitionSectionHeading
            title="Upgrade Scope"
            description="Choose all supported components or select individual components organized by Apply Group."
          />
          <div className="premium-upgrade-plan__scope-summary">
            <Text type="secondary">
              <Text strong>{selectedClusterList.length}</Text> clusters
            </Text>
            <Text type="secondary">
              <Text strong>
                {scopeMode === 'selected'
                  ? selectedApplyGroupCount
                  : applicablePolicyGroups.length}
              </Text>{' '}
              Apply Groups
            </Text>
            <Text type="secondary">
              <Text strong>
                {scopeMode === 'selected'
                  ? selectedPolicies.length
                  : applicablePolicyGroups.reduce(
                      (total, group) => total + group.policies.length,
                      0
                    )}
              </Text>{' '}
              components
            </Text>
          </div>
        </div>

        <div className="premium-upgrade-plan__scope-options">
          <button
            type="button"
            className={scopeMode === 'selected' ? 'active' : ''}
            onClick={() => setScopeMode('selected')}
          >
            <span className="premium-upgrade-plan__scope-indicator" />
            <span>
              <Text strong>Selected Components</Text>
              <Text type="secondary">
                Choose components individually or select a complete Apply Group.
              </Text>
              <Tag color="blue">Partial scope</Tag>
            </span>
          </button>
          <button
            type="button"
            className={scopeMode === 'all_present' ? 'active' : ''}
            onClick={() => setScopeMode('all_present')}
          >
            <span className="premium-upgrade-plan__scope-indicator" />
            <span>
              <Text strong>All Present Components</Text>
              <Text type="secondary">
                Resolve every supported component separately for each actual
                target.
              </Text>
              <Tag color="purple">Full alignment</Tag>
            </span>
          </button>
        </div>

        {scopeMode === 'selected' ? (
          <div className="premium-upgrade-plan__apply-groups">
            <div className="premium-upgrade-plan__apply-groups-heading">
              <Text strong>Components by Apply Group</Text>
              <Space>
                <Button
                  type="link"
                  onClick={() =>
                    setSelectedPolicyKeys(
                      applicablePolicyGroups.flatMap((group) =>
                        group.policies.map(getPolicyKey)
                      )
                    )
                  }
                >
                  Select all
                </Button>
                <Button type="link" onClick={() => setSelectedPolicyKeys([])}>
                  Clear
                </Button>
              </Space>
            </div>
            {policiesQuery.isLoading ? (
              <Spin />
            ) : policiesQuery.isError ? (
              <Alert
                type="error"
                showIcon
                message="Unable to load component policies"
                description={getErrorMessage(policiesQuery.error)}
              />
            ) : policyGroups.length === 0 ? (
              <Alert
                type="warning"
                showIcon
                message="No component policies returned"
                description="The Premium Upgrade service may still be disabled in this environment."
              />
            ) : (
              <div className="premium-upgrade-plan__policy-sections">
                {policyGroupSections.map((section) => (
                  <section
                    key={section.clusterType}
                    className="premium-upgrade-plan__policy-section"
                  >
                    <div className="premium-upgrade-plan__policy-section-heading">
                      <span>
                        <Text strong>{section.title}</Text>
                        <Text type="secondary">{section.description}</Text>
                      </span>
                      <Tag>
                        {section.groups.length}{' '}
                        {section.groups.length === 1
                          ? 'Apply Group'
                          : 'Apply Groups'}
                      </Tag>
                    </div>
                    <div className="premium-upgrade-plan__policy-grid">
                      {section.groups.map((group) => {
                        const groupPolicyKeys = group.policies.map(getPolicyKey)
                        const selectedGroupPolicyKeys = groupPolicyKeys.filter(
                          (key) => selectedPolicyKeys.includes(key)
                        )
                        const checked =
                          selectedGroupPolicyKeys.length ===
                          groupPolicyKeys.length
                        const indeterminate =
                          selectedGroupPolicyKeys.length > 0 && !checked
                        const toggleGroup = () =>
                          setSelectedPolicyKeys((current) => {
                            const next = new Set(current)
                            groupPolicyKeys.forEach((key) =>
                              checked ? next.delete(key) : next.add(key)
                            )
                            return Array.from(next)
                          })
                        return (
                          <div
                            key={group.key}
                            className={`premium-upgrade-plan__policy-card ${
                              selectedGroupPolicyKeys.length ? 'selected' : ''
                            }`}
                          >
                            <span>
                              <span className="premium-upgrade-plan__policy-title">
                                <Checkbox
                                  checked={checked}
                                  indeterminate={indeterminate}
                                  onChange={toggleGroup}
                                >
                                  <Text strong>
                                    {applyGroupName(group.applyGroup)}
                                  </Text>
                                </Checkbox>
                                <Tag>
                                  {selectedGroupPolicyKeys.length}/
                                  {groupPolicyKeys.length}
                                </Tag>
                              </span>
                              <div className="premium-upgrade-plan__policy-components">
                                {group.policies.map((policy) => (
                                  <Checkbox
                                    key={policy.component}
                                    checked={selectedPolicyKeys.includes(
                                      getPolicyKey(policy)
                                    )}
                                    onChange={(event) =>
                                      setSelectedPolicyKeys((current) =>
                                        event.target.checked
                                          ? Array.from(
                                              new Set([
                                                ...current,
                                                getPolicyKey(policy)
                                              ])
                                            )
                                          : current.filter(
                                              (key) =>
                                                key !== getPolicyKey(policy)
                                            )
                                      )
                                    }
                                  >
                                    {componentName(policy.component)}
                                  </Checkbox>
                                ))}
                              </div>
                              <Text type="secondary">
                                {group.atomic
                                  ? 'Atomic group · Preview may restore required companion components'
                                  : 'Each selected component is resolved independently'}
                              </Text>
                            </span>
                          </div>
                        )
                      })}
                    </div>
                  </section>
                ))}
              </div>
            )}
          </div>
        ) : (
          <Alert
            type="info"
            showIcon
            message="The server will resolve all supported components"
            description="Components absent from a target are skipped for that target. The preview remains authoritative and shows every resulting ConfigurationChange before submission."
          />
        )}
      </section>

      <section className="premium-upgrade-plan__definition-section">
        <DefinitionSectionHeading
          title="Execution Mode"
          description="Choose how the approved managed ConfigurationChanges are started."
        />
        <div className="premium-upgrade-plan__scope-options premium-upgrade-plan__execution-options">
          <button
            type="button"
            className={executionMode === 'orchestrated' ? 'active' : ''}
            onClick={() => setExecutionMode('orchestrated')}
          >
            <span className="premium-upgrade-plan__scope-indicator" />
            <span>
              <span className="premium-upgrade-plan__option-title">
                <Text strong>Run all in sequence</Text>
                <Tag color="blue">Recommended</Tag>
              </span>
              <Text type="secondary">
                After approval, start the plan once. The server applies every
                ConfigurationChange in the fixed sequence.
              </Text>
            </span>
          </button>
          <button
            type="button"
            className={executionMode === 'step_by_step' ? 'active' : ''}
            onClick={() => setExecutionMode('step_by_step')}
          >
            <span className="premium-upgrade-plan__scope-indicator" />
            <span>
              <span className="premium-upgrade-plan__option-title">
                <Text strong>Apply step by step</Text>
                <Tag>Manual control</Tag>
              </span>
              <Text type="secondary">
                Each ready ConfigurationChange waits for an explicit Apply
                action in the plan detail page.
              </Text>
            </span>
          </button>
        </div>
      </section>

      {imageTagPolicies.length > 0 && (
        <Collapse ghost className="premium-upgrade-plan__advanced-options">
          <Collapse.Panel
            key="component-image-tags"
            header={
              <DefinitionSectionHeading
                title="Image Tag Overrides"
                description="Override the Config Center image tag only when this rollout needs a specific build."
                optional
              />
            }
          >
            {!desiredVersion.trim() ? (
              <Alert
                type="warning"
                showIcon
                message="Enter a Target Version to load default image tags"
              />
            ) : defaultImageTagsLoading ? (
              <Alert
                type="info"
                showIcon
                message={`Loading Config Center defaults for ${desiredVersion.trim()}`}
              />
            ) : defaultImageTagsQuery.isError ? (
              <Alert
                type="warning"
                showIcon
                message="Unable to load default image tags"
                description="You can still enter an explicit image tag. The preview will show the resolved value before submission."
              />
            ) : (
              <Alert
                type="info"
                showIcon
                message={`Showing Config Center defaults for ${desiredVersion.trim()}`}
                description="Change a field only when this rollout needs a different build. Unchanged values are not submitted as overrides."
              />
            )}
            <div className="premium-upgrade-plan__image-tag-grid">
              {imageTagPolicies.map((policy) => {
                const key = `${policy.cluster_type}/${policy.component}`
                const defaultImageTag = defaultImageTags[key]
                const overrideImageTag = componentImageTags[key]?.trim()
                const hasOverride = Boolean(
                  overrideImageTag && overrideImageTag !== defaultImageTag
                )
                return (
                  <div key={key}>
                    <Space size={6} wrap>
                      <Text strong>{componentName(policy.component)}</Text>
                      <Tag>
                        {policy.cluster_type === 'SHARED_POOL'
                          ? 'Resource Pool'
                          : 'Logical Cluster'}
                      </Tag>
                      <Tag
                        color={
                          hasOverride
                            ? 'gold'
                            : defaultImageTagsLoading || defaultImageTag
                            ? undefined
                            : 'orange'
                        }
                      >
                        {hasOverride
                          ? 'Override'
                          : defaultImageTagsLoading
                          ? 'Loading'
                          : defaultImageTag
                          ? 'Default'
                          : 'No default'}
                      </Tag>
                    </Space>
                    <Input
                      allowClear={hasOverride}
                      value={componentImageTags[key] ?? defaultImageTag ?? ''}
                      placeholder={
                        defaultImageTagsLoading
                          ? 'Loading default image tag...'
                          : `Enter an image tag for ${desiredVersion.trim()}`
                      }
                      onChange={(event) => {
                        const nextValue = event.target.value
                        setComponentImageTags((current) => {
                          const next = { ...current }
                          if (
                            !nextValue.trim() ||
                            nextValue.trim() === defaultImageTag?.trim()
                          ) {
                            delete next[key]
                          } else {
                            next[key] = nextValue
                          }
                          return next
                        })
                      }}
                    />
                    <Text
                      type="secondary"
                      className="premium-upgrade-plan__image-tag-help"
                    >
                      {defaultImageTagsLoading
                        ? 'Loading Config Center default'
                        : hasOverride && defaultImageTag
                        ? `Default: ${defaultImageTag}`
                        : defaultImageTag
                        ? `Config Center default for ${desiredVersion.trim()}`
                        : `No default found for ${desiredVersion.trim()}`}
                    </Text>
                  </div>
                )
              })}
            </div>
          </Collapse.Panel>
        </Collapse>
      )}
    </div>
  )

  const renderReviewStep = () => (
    <div className="premium-upgrade-plan__create-step">
      <div className="premium-upgrade-plan__step-heading">
        <span>3</span>
        <div>
          <Title level={4}>Review the server-resolved plan</Title>
          <Text type="secondary">
            This immutable manifest is what approvers authorize. Execution uses
            the same targets, Apply Groups, versions, and sequence.
          </Text>
        </div>
      </div>

      {preview && (
        <>
          <Alert
            type="success"
            showIcon
            message="One Upgrade Plan creates one parent approval"
            description={`Rows below are separate managed ConfigurationChanges and may share a sequence without being merged. Execution mode: ${executionModeName(
              preview.execution_mode
            )}.`}
          />

          {(preview.warnings || []).map((warning) => (
            <Alert key={warning} type="warning" showIcon message={warning} />
          ))}

          <Row gutter={16} className="premium-upgrade-plan__preview-stats">
            <Col span={6}>
              <Card size="small">
                <Statistic
                  title="Selected Clusters"
                  value={preview.cluster_ids?.length || 0}
                />
              </Card>
            </Col>
            <Col span={6}>
              <Card size="small">
                <Statistic
                  title="Actual Targets"
                  value={preview.targets?.length || 0}
                />
              </Card>
            </Col>
            <Col span={6}>
              <Card size="small">
                <Statistic
                  title="ConfigurationChanges"
                  value={executionRows.length}
                />
              </Card>
            </Col>
            <Col span={6}>
              <Card size="small">
                <Statistic
                  title="Approval Routes"
                  value={preview.approval_routes?.length || 0}
                />
              </Card>
            </Col>
          </Row>

          <Descriptions bordered size="small" column={3}>
            <Descriptions.Item label="Plan">{planName}</Descriptions.Item>
            <Descriptions.Item label="Target Version">
              <Text strong>{preview.desired_version}</Text>
            </Descriptions.Item>
            <Descriptions.Item label="Scope">
              {preview.scope?.mode === 'all_present'
                ? 'All present components'
                : 'Selected Apply Groups'}
            </Descriptions.Item>
            <Descriptions.Item label="Execution Mode">
              <Tag
                color={
                  preview.execution_mode === 'step_by_step' ? 'cyan' : 'blue'
                }
              >
                {executionModeName(preview.execution_mode)}
              </Tag>
            </Descriptions.Item>
            <Descriptions.Item label="Applicant">
              {profile.userEmail || 'Current signed-in user'}
            </Descriptions.Item>
            <Descriptions.Item label="Order Revision">
              {preview.component_order_revision}
            </Descriptions.Item>
            <Descriptions.Item label="Manifest Hash" span={3}>
              <Text code ellipsis={{ tooltip: preview.manifest_hash }}>
                {preview.manifest_hash?.slice(0, 16) || '-'}
              </Text>
            </Descriptions.Item>
            <Descriptions.Item label="Reason" span={3}>
              {reason}
            </Descriptions.Item>
          </Descriptions>

          <Card
            size="small"
            title="Resolved Execution Manifest"
            className="premium-upgrade-plan__preview-manifest"
            extra={<Tag color="blue">One row = one ConfigurationChange</Tag>}
          >
            <Table<PreviewExecutionRow>
              rowKey="key"
              size="small"
              columns={executionColumns}
              dataSource={executionRows}
              pagination={false}
              scroll={{ x: 1000 }}
            />
          </Card>

          <Card
            size="small"
            title="Aggregated Approval Routes"
            className="premium-upgrade-plan__approval-routes"
          >
            <div className="premium-upgrade-plan__approval-flow">
              <div>
                <SafetyCertificateOutlined />
                <span>
                  <Text type="secondary">Authorization boundary</Text>
                  <Text strong>One parent approval</Text>
                </span>
              </div>
              <span>→</span>
              <div>
                <ApartmentOutlined />
                <span>
                  <Text type="secondary">Execution boundary</Text>
                  <Text strong>{executionRows.length} managed Changes</Text>
                </span>
              </div>
            </div>
            <div className="premium-upgrade-plan__route-grid">
              {(preview.approval_routes || []).map((route) => (
                <div key={`${route.group}/${route.name}`}>
                  <Text strong>{route.name || route.group}</Text>
                  <Text type="secondary">Group: {route.group}</Text>
                  <Space size={[4, 4]} wrap>
                    {(route.components || []).map((component) => (
                      <Tag key={component}>{component}</Tag>
                    ))}
                  </Space>
                  <Text type="secondary">
                    {[
                      route.approver?.email,
                      ...(route.approver?.emails || []),
                      route.approver?.user_group,
                      ...(route.approver?.user_groups || [])
                    ]
                      .filter(Boolean)
                      .join(', ') || 'Resolved by Approval Service'}
                  </Text>
                </div>
              ))}
            </div>
          </Card>
        </>
      )}
    </div>
  )

  const nextFromClusterStep = async () => {
    if (!selectedClusterList.length) {
      message.warning('Select at least one Premium or Essential V2 target')
      return
    }
    setTargetConflictLoading(true)
    setRequestError('')
    try {
      const response = await checkPremiumUpgradeTargetConflicts({
        cluster_ids: selectedClusterList.map((cluster) => cluster.id!)
      })
      const conflicts = response.data.conflicts || []
      setTargetConflicts(conflicts)
      if (conflicts.length) return
      setCurrentStep(1)
    } catch (error) {
      setRequestError(getErrorMessage(error))
    } finally {
      setTargetConflictLoading(false)
    }
  }

  const definitionReady = Boolean(
    planName.trim() &&
      desiredVersion.trim() &&
      reason.trim() &&
      (scopeMode === 'all_present' || selectedPolicies.length)
  )

  return (
    <div className="premium-upgrade-plan-page premium-upgrade-plan__create-page">
      <PageHeader
        title="Create Premium Upgrade Plan"
        subTitle="Multiple clusters and components · one parent approval"
        onBack={() => history.push('/premiumUpgradePlans')}
      />

      <Card className="premium-upgrade-plan__wizard">
        <Steps
          current={currentStep}
          items={[
            { title: 'Select Clusters' },
            { title: 'Define Upgrade' },
            { title: 'Review & Submit' }
          ]}
        />

        {requestError && (
          <Alert
            className="premium-upgrade-plan__request-error"
            type="error"
            showIcon
            message="Request failed"
            description={requestError}
            closable
            onClose={() => setRequestError('')}
          />
        )}

        {currentStep === 0 && renderClusterStep()}
        {currentStep === 1 && renderDefinitionStep()}
        {currentStep === 2 && renderReviewStep()}

        <div className="premium-upgrade-plan__wizard-footer">
          <Button onClick={() => history.push('/premiumUpgradePlans')}>
            Cancel
          </Button>
          <Space>
            {currentStep > 0 && (
              <Button
                onClick={() => {
                  setRequestError('')
                  setCurrentStep((step) => step - 1)
                }}
              >
                Back
              </Button>
            )}
            {currentStep === 0 && (
              <Button
                type="primary"
                loading={targetConflictLoading}
                onClick={nextFromClusterStep}
              >
                Check &amp; Continue
              </Button>
            )}
            {currentStep === 1 && (
              <Space>
                {!definitionReady && (
                  <Text type="secondary">Complete all required sections</Text>
                )}
                <Button
                  type="primary"
                  disabled={!definitionReady}
                  loading={previewLoading}
                  onClick={generatePreview}
                >
                  Preview This Plan
                </Button>
              </Space>
            )}
            {currentStep === 2 && (
              <Button
                type="primary"
                loading={submitLoading}
                data-mp-event="premium-upgrade-plan-submit-approval"
                onClick={submitPlan}
              >
                Submit One Approval
              </Button>
            )}
          </Space>
        </div>
      </Card>
    </div>
  )
}

export default PremiumUpgradePlanCreate
