import {
  CheckCircleOutlined,
  ClockCircleOutlined,
  ExclamationCircleOutlined,
  LinkOutlined,
  LoadingOutlined,
  ReloadOutlined,
  SendOutlined,
  SwapOutlined,
} from '@ant-design/icons'
import {
  Alert,
  Button,
  Card,
  Collapse,
  Empty,
  Image,
  Input,
  Space,
  Spin,
  Tooltip,
  Typography,
} from 'antd'
import QRCode from 'qrcode'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useParams } from 'react-router'

import {
  TASK_STATUS,
  hasKuaishouCloudRedeemResultStatus,
  isClaimInactiveTaskStatus,
  isKuaishouCloudCompletedStatus,
  isKuaishouCloudRoleConfirmedStatus,
  normalizeTaskStatus,
} from '@/domain/task-status'
import { isFeedbackDismissed, showConfirm, showError, showSuccess } from '@/lib/feedback'
import {
  confirmKuaishouCloudClaimRole,
  fetchClaimDetail,
  rebindKuaishouCloudClaimRole,
  redeemKuaishouCloudClaim,
  verifyKuaishouCloudClaimTicket,
} from '@/services/claim'
import type {
  ClaimDetailData,
  ClaimKuaishouCloudFlowInfo,
  ClaimKuaishouFeifeiFlowInfo,
  ClaimOrderInfo,
  ClaimProductInfo,
} from '@/types/claim'
import { formatAdminDateTime } from '@/utils/admin-time'

const BINDING_PREPARE_POLL_MS = 5_000
const ROLE_FAST_POLL_MS = 10_000
const ROLE_SLOW_POLL_MS = 30_000
const ROLE_IDLE_POLL_MS = 60_000
const ROLE_FAST_WINDOW_MS = 3 * 60_000
const ROLE_IDLE_WINDOW_MS = 10 * 60_000

type ResultVariant = 'success' | 'warning' | 'info'

type ClaimSnapshot = ReturnType<typeof createClaimSnapshot>

export default function ClaimPage() {
  const { token: routeToken } = useParams<{ token: string }>()
  const token = String(routeToken || '').trim()

  const [loading, setLoading] = useState(true)
  const [submitting, setSubmitting] = useState(false)
  const [refreshingRole, setRefreshingRole] = useState(false)
  const [confirmingRole, setConfirmingRole] = useState(false)
  const [rebindingRole, setRebindingRole] = useState(false)
  const [redeeming, setRedeeming] = useState(false)
  const [errorMessage, setErrorMessage] = useState('')
  const [detail, setDetail] = useState<ClaimDetailData | null>(null)
  const [ticketCode, setTicketCode] = useState('')
  const [qrCodeDataUrl, setQrCodeDataUrl] = useState('')

  const pollTimerRef = useRef<number>(0)
  const rolePollBaselineAtRef = useRef(0)
  const rolePollBaselineKeyRef = useRef('')

  const snapshot = useMemo(
    () => createClaimSnapshot(detail, { rebindingRole }),
    [detail, rebindingRole],
  )

  const stopPolling = useCallback(() => {
    if (pollTimerRef.current) {
      window.clearTimeout(pollTimerRef.current)
      pollTimerRef.current = 0
    }
  }, [])

  const generateQRCode = useCallback(async (url: string) => {
    if (!url) {
      setQrCodeDataUrl('')
      return
    }

    try {
      const dataUrl = await QRCode.toDataURL(url, {
        width: 280,
        margin: 2,
        color: {
          dark: '#0f172a',
          light: '#ffffff',
        },
      })
      setQrCodeDataUrl(dataUrl)
    } catch (error) {
      setQrCodeDataUrl('')
      console.error('生成二维码失败:', error)
    }
  }, [])

  const applyDetail = useCallback(
    async (nextDetail: ClaimDetailData) => {
      setDetail(nextDetail)
      setTicketCode((current) => current || nextDetail.kuaishouCloudFulfillment?.ticket.code || '')
      await generateQRCode(
        String(nextDetail.kuaishouCloudFulfillment?.binding.bindUrl || '').trim(),
      )
    },
    [generateQRCode],
  )

  const loadDetail = useCallback(
    async (options: { silent?: boolean } = {}) => {
      if (!token) {
        setLoading(false)
        setErrorMessage('领取链接无效，请检查链接是否完整')
        stopPolling()
        return null
      }

      if (!options.silent) {
        setLoading(true)
      }
      setErrorMessage('')

      try {
        const response = await fetchClaimDetail(token)
        await applyDetail(response.data)
        return response.data
      } catch (error) {
        setErrorMessage(error instanceof Error ? error.message : '读取领取信息失败')
        stopPolling()
        return null
      } finally {
        if (!options.silent) {
          setLoading(false)
        }
      }
    },
    [applyDetail, stopPolling, token],
  )

  const resolveRolePollDelay = useCallback((nextSnapshot: ClaimSnapshot) => {
    const nextRoleKey = [nextSnapshot.roleName, nextSnapshot.roleId].join('::')
    const now = Date.now()

    if (nextRoleKey !== rolePollBaselineKeyRef.current) {
      rolePollBaselineKeyRef.current = nextRoleKey
      rolePollBaselineAtRef.current = now
    }

    if (!rolePollBaselineAtRef.current) {
      rolePollBaselineAtRef.current = now
    }

    const elapsedMs = now - rolePollBaselineAtRef.current
    if (elapsedMs < ROLE_FAST_WINDOW_MS) {
      return ROLE_FAST_POLL_MS
    }

    if (elapsedMs < ROLE_IDLE_WINDOW_MS) {
      return ROLE_SLOW_POLL_MS
    }

    return ROLE_IDLE_POLL_MS
  }, [])

  const resolveNextPollDelay = useCallback(
    (nextSnapshot: ClaimSnapshot) => {
      if (
        !nextSnapshot.flow ||
        nextSnapshot.hasRedeemResult ||
        isClaimInactiveTaskStatus(nextSnapshot.task?.status)
      ) {
        return 0
      }

      if (nextSnapshot.currentStep === 2) {
        if (!nextSnapshot.isBindingPrepared) {
          return BINDING_PREPARE_POLL_MS
        }

        return resolveRolePollDelay(nextSnapshot)
      }

      return 0
    },
    [resolveRolePollDelay],
  )

  useEffect(() => {
    rolePollBaselineAtRef.current = 0
    rolePollBaselineKeyRef.current = ''
    setDetail(null)
    setTicketCode('')
    setQrCodeDataUrl('')
    void loadDetail()

    return () => {
      stopPolling()
    }
  }, [loadDetail, stopPolling])

  useEffect(() => {
    stopPolling()
    const delay = resolveNextPollDelay(snapshot)
    if (!delay) {
      return undefined
    }

    pollTimerRef.current = window.setTimeout(() => {
      pollTimerRef.current = 0
      void loadDetail({ silent: true })
    }, delay)

    return () => {
      stopPolling()
    }
  }, [loadDetail, resolveNextPollDelay, snapshot, stopPolling])

  async function submitTicket() {
    const normalizedTicketCode = ticketCode.trim()
    if (!normalizedTicketCode) {
      showError('请输入核销码')
      return
    }

    setSubmitting(true)
    try {
      const response = await verifyKuaishouCloudClaimTicket(token, {
        ticketCode: normalizedTicketCode,
      })
      await applyDetail(response.data)
      showSuccess('核销已完成，系统已开始准备绑定资源')
    } catch (error) {
      showError(error instanceof Error ? error.message : '核销码验证失败')
    } finally {
      setSubmitting(false)
    }
  }

  async function refreshRole() {
    setRefreshingRole(true)
    try {
      const nextDetail = await loadDetail({ silent: true })
      const nextSnapshot = createClaimSnapshot(nextDetail, { rebindingRole })

      if (nextSnapshot.isCustomerRoleReady) {
        showSuccess('角色信息已刷新')
      } else if (nextSnapshot.isDefaultRole) {
        showError('当前仍是虚拟机默认角色，请重新绑定自己的角色信息')
      } else if (nextDetail) {
        showError(
          nextSnapshot.flow?.role.errorMessage ||
            nextSnapshot.flow?.role.defaultErrorMessage ||
            '暂时还没有识别到角色信息，请完成绑定后稍等片刻再试',
        )
      }
    } finally {
      setRefreshingRole(false)
    }
  }

  async function confirmRole() {
    setConfirmingRole(true)
    try {
      const response = await confirmKuaishouCloudClaimRole(token)
      await applyDetail(response.data)
      showSuccess('角色已确认，进入下一步')
    } catch (error) {
      showError(error instanceof Error ? error.message : '确认角色失败')
    } finally {
      setConfirmingRole(false)
    }
  }

  async function confirmRedeem() {
    try {
      await showConfirm('兑换后不可取消，也不可退货。请确认角色和商品信息完全正确。', '确认兑换', {
        okText: '确认兑换',
        okButtonProps: { danger: true },
      })
    } catch (error) {
      if (!isFeedbackDismissed(error)) {
        showError('确认弹窗已关闭')
      }
      return
    }

    setRedeeming(true)
    try {
      const response = await redeemKuaishouCloudClaim(token)
      await applyDetail(response.data)
      showSuccess('兑换请求已提交')
    } catch (error) {
      showError(error instanceof Error ? error.message : '兑换失败')
    } finally {
      setRedeeming(false)
    }
  }

  async function rebindRole() {
    try {
      await showConfirm(
        '换绑后当前绑定链接会失效，系统会退还当前虚拟号并生成新的绑定二维码。核销码和领取商品不会改变，但需要重新扫码绑定角色。',
        '确认换绑角色',
        {
          okText: '确认换绑',
          okButtonProps: { danger: true },
        },
      )
    } catch (error) {
      if (!isFeedbackDismissed(error)) {
        showError('确认弹窗已关闭')
      }
      return
    }

    setRebindingRole(true)
    try {
      const response = await rebindKuaishouCloudClaimRole(token)
      await applyDetail(response.data)
      showSuccess('新的绑定二维码已生成，请重新绑定角色')
    } catch (error) {
      showError(error instanceof Error ? error.message : '换绑角色失败')
    } finally {
      setRebindingRole(false)
    }
  }

  function openBindUrl(useCurrentPage = false) {
    const bindUrl = String(snapshot.flow?.binding.bindUrl || '').trim()
    if (!bindUrl) {
      showError('绑定链接还没准备好，请稍后刷新')
      return
    }

    if (useCurrentPage) {
      window.location.assign(bindUrl)
      return
    }

    window.open(bindUrl, '_blank', 'noopener,noreferrer')
  }

  function openFeifeiUrl(useCurrentPage = false) {
    const url = String(
      snapshot.feifei?.h5.rechargeUrl || snapshot.feifei?.h5.entryUrl || '',
    ).trim()

    if (!url) {
      showError('领取链接还没准备好，请稍后刷新')
      return
    }

    if (useCurrentPage) {
      window.location.assign(url)
      return
    }

    window.open(url, '_blank', 'noopener,noreferrer')
  }

  return (
    <main className="claim-page">
      <ClaimHeaderCard
        order={snapshot.order}
        product={snapshot.product}
        currentStep={snapshot.currentStep}
        progressText={snapshot.progressText}
      />

      {loading ? (
        <Card className="claim-content-card claim-state-card">
          <Spin indicator={<LoadingOutlined spin />} size="large" />
          <p>正在读取当前领取进度...</p>
        </Card>
      ) : errorMessage ? (
        <Card className="claim-content-card claim-state-card">
          <ExclamationCircleOutlined className="claim-large-icon claim-icon-error" />
          <p>{errorMessage}</p>
        </Card>
      ) : snapshot.isFeifeiFlow && snapshot.feifei ? (
        <FeifeiClaimPanel
          feifei={snapshot.feifei}
          order={snapshot.order}
          product={snapshot.product}
          taskLastError={snapshot.task?.lastError}
          onOpen={() => openFeifeiUrl(false)}
        />
      ) : detail && snapshot.flow ? (
        <KuaishouCloudClaimSteps
          snapshot={snapshot}
          ticketCode={ticketCode}
          qrCodeDataUrl={qrCodeDataUrl}
          submitting={submitting}
          refreshingRole={refreshingRole}
          confirmingRole={confirmingRole}
          rebindingRole={rebindingRole}
          redeeming={redeeming}
          onTicketCodeChange={setTicketCode}
          onSubmitTicket={submitTicket}
          onOpenBindUrl={openBindUrl}
          onRefreshRole={refreshRole}
          onRebindRole={rebindRole}
          onConfirmRole={confirmRole}
          onConfirmRedeem={confirmRedeem}
        />
      ) : (
        <Card className="claim-content-card claim-state-card">
          <Empty description="流程数据不完整，请联系客服处理" />
        </Card>
      )}
    </main>
  )
}

function createClaimSnapshot(
  detail: ClaimDetailData | null,
  options: { rebindingRole?: boolean } = {},
) {
  const flow = detail?.kuaishouCloudFulfillment || null
  const feifei = detail?.kuaishouFeifei || null
  const order = detail?.order || null
  const orderItem = detail?.orderItem || null
  const product = detail?.product || null
  const task = detail?.task || null

  const roleName = flow?.role.name || flow?.binding.roleName || ''
  const roleId = flow?.role.rid || flow?.binding.roleId || ''
  const isFeifeiFlow = detail?.flowType === 'kuaishou_feifei'
  const isTicketVerified = flow?.ticket.status === 'verified'
  const isBindUrlExpired = isDateExpired(flow?.binding.bindExpiresAt || '')
  const isBindingPrepared =
    flow?.binding.prepareStatus === 'ready' &&
    Boolean(String(flow?.binding.bindUrl || '').trim()) &&
    !isBindUrlExpired
  const isBindingPreparing = flow?.binding.prepareStatus === 'pending'
  const canEnterBindingStep = isTicketVerified
  const isRoleReady = Boolean(roleName || roleId)
  const hasDefaultRoleSnapshot = Boolean(flow?.role.defaultName || flow?.role.defaultRid)
  const isDefaultRole = flow?.role.isDefaultRole === true
  const isCustomerRoleReady = isRoleReady && hasDefaultRoleSnapshot && !isDefaultRole
  const confirmRoleDisabledReason = resolveConfirmRoleDisabledReason({
    flow,
    isBindingPrepared,
    hasDefaultRoleSnapshot,
    isRoleReady,
    isDefaultRole,
    rebindingRole: Boolean(options.rebindingRole),
  })
  const isRoleConfirmed = isKuaishouCloudRoleConfirmedStatus(task?.status)
  const isDispatched = String(flow?.dispatch.status || '').trim() === 'success'
  const normalizedStatus = normalizeTaskStatus(task?.status)
  const isRedeemFailed =
    normalizedStatus === TASK_STATUS.MANUAL_REVIEW ||
    normalizedStatus === TASK_STATUS.FAILED ||
    String(flow?.dispatch.status || '').trim() === 'failed'
  const isCompleted = isKuaishouCloudCompletedStatus(task?.status)
  const hasRedeemResult = isDispatched || hasKuaishouCloudRedeemResultStatus(task?.status)
  const canSubmitTicket = !isClaimInactiveTaskStatus(task?.status)

  const currentStep = resolveCurrentStep({
    isFeifeiFlow,
    isCompleted,
    hasRedeemResult,
    isRoleConfirmed,
    canEnterBindingStep,
  })
  const progressText = resolveProgressText({
    feifei,
    isFeifeiFlow,
    hasRedeemResult,
    isRoleConfirmed,
    isTicketVerified,
    isBindingPrepared,
  })
  const resultTitle = resolveResultTitle({ isRedeemFailed, isCompleted, isDispatched })
  const resultDescription = resolveResultDescription({ detail, flow, task, isRedeemFailed, isCompleted })
  const resultVariant: ResultVariant = isRedeemFailed
    ? 'warning'
    : isCompleted || isDispatched
      ? 'success'
      : 'info'

  return {
    flow,
    feifei,
    order,
    orderItem,
    product,
    task,
    roleName,
    roleId,
    isFeifeiFlow,
    isTicketVerified,
    isBindUrlExpired,
    isBindingPrepared,
    isBindingPreparing,
    canEnterBindingStep,
    isRoleReady,
    hasDefaultRoleSnapshot,
    isDefaultRole,
    isCustomerRoleReady,
    confirmRoleDisabledReason,
    isRoleConfirmed,
    isDispatched,
    isRedeemFailed,
    isCompleted,
    hasRedeemResult,
    canSubmitTicket,
    currentStep,
    progressText,
    resultTitle,
    resultDescription,
    resultVariant,
  }
}

function resolveConfirmRoleDisabledReason(options: {
  flow: ClaimKuaishouCloudFlowInfo | null
  isBindingPrepared: boolean
  hasDefaultRoleSnapshot: boolean
  isRoleReady: boolean
  isDefaultRole: boolean
  rebindingRole: boolean
}) {
  if (!options.isBindingPrepared) {
    return '绑定二维码还在准备中，请稍后自动刷新'
  }
  if (!options.hasDefaultRoleSnapshot) {
    return options.flow?.role.defaultErrorMessage || '系统还未获取到虚拟机默认角色信息，请稍后刷新'
  }
  if (!options.isRoleReady) {
    return '请先扫码绑定自己的角色，并刷新角色信息'
  }
  if (options.isDefaultRole) {
    return '当前仍是虚拟机默认角色，请重新绑定自己的角色信息'
  }
  if (options.rebindingRole) {
    return '正在换绑角色，请稍候'
  }
  return ''
}

function resolveCurrentStep(options: {
  isFeifeiFlow: boolean
  isCompleted: boolean
  hasRedeemResult: boolean
  isRoleConfirmed: boolean
  canEnterBindingStep: boolean
}) {
  if (options.isFeifeiFlow) {
    return options.isCompleted ? 4 : 2
  }
  if (options.hasRedeemResult) {
    return 4
  }
  if (options.isRoleConfirmed) {
    return 3
  }
  if (options.canEnterBindingStep) {
    return 2
  }
  return 1
}

function resolveProgressText(options: {
  feifei: ClaimKuaishouFeifeiFlowInfo | null
  isFeifeiFlow: boolean
  hasRedeemResult: boolean
  isRoleConfirmed: boolean
  isTicketVerified: boolean
  isBindingPrepared: boolean
}) {
  if (options.isFeifeiFlow) {
    return options.feifei?.rechargeStatusLabel || '请打开领取链接'
  }
  if (options.hasRedeemResult) {
    return '兑换结果已生成'
  }
  if (options.isRoleConfirmed) {
    return '角色已确认，等待兑换'
  }
  if (options.isTicketVerified) {
    return options.isBindingPrepared ? '请完成扫码绑定' : '绑定链接刷新中，请稍候'
  }
  return '等待提交并核销'
}

function resolveResultTitle(options: {
  isRedeemFailed: boolean
  isCompleted: boolean
  isDispatched: boolean
}) {
  if (options.isRedeemFailed) {
    return '兑换遇到问题'
  }
  if (options.isCompleted) {
    return '兑换成功'
  }
  if (options.isDispatched) {
    return '兑换请求已提交'
  }
  return '结果已记录'
}

function resolveResultDescription(options: {
  detail: ClaimDetailData | null
  flow: ClaimKuaishouCloudFlowInfo | null
  task: ClaimDetailData['task'] | null
  isRedeemFailed: boolean
  isCompleted: boolean
}) {
  if (options.isRedeemFailed) {
    const message = String(
      options.task?.lastError ||
        options.flow?.dispatch.errorMessage ||
        options.detail?.result?.resultMessage ||
        '',
    ).trim()
    return message || '当前兑换流程需要客服处理，后续结果请以后台任务界面为准。'
  }

  if (options.isCompleted) {
    return '当前兑换流程已经完成。发货、退号和核销结果会保存在后台任务界面。'
  }

  return '你的兑换请求已经提交。发货、退号和核销结果会由系统保存在后台任务界面，无需在此页面等待。'
}

function isDateExpired(value: string | null) {
  const normalized = String(value || '').trim()
  if (!normalized) {
    return false
  }

  const expiresTime = Date.parse(normalized)
  return Number.isFinite(expiresTime) && expiresTime <= Date.now()
}

function ClaimHeaderCard({
  order,
  product,
  currentStep,
  progressText,
}: {
  order: ClaimOrderInfo | null
  product: ClaimProductInfo | null
  currentStep: number
  progressText: string
}) {
  return (
    <Card className="claim-header-card">
      <div className="claim-header-copy">
        <span className="eyebrow">快手 Cloud 客户领取</span>
        <h1>{product?.title || '商品领取'}</h1>
        <p>订单号：{order?.platformOrderId || '-'}</p>
        <ClaimProductItems product={product} compact />
      </div>

      <div className="claim-step-indicator">
        <div className="claim-step-dots" aria-label={`当前第 ${currentStep} 步`}>
          {[1, 2, 3, 4].map((step) => (
            <span key={step} className={currentStep >= step ? 'claim-step-dot active' : 'claim-step-dot'}>
              {step}
            </span>
          ))}
        </div>
        <span className="claim-step-progress-text">{progressText}</span>
      </div>
    </Card>
  )
}

function KuaishouCloudClaimSteps({
  snapshot,
  ticketCode,
  qrCodeDataUrl,
  submitting,
  refreshingRole,
  confirmingRole,
  rebindingRole,
  redeeming,
  onTicketCodeChange,
  onSubmitTicket,
  onOpenBindUrl,
  onRefreshRole,
  onRebindRole,
  onConfirmRole,
  onConfirmRedeem,
}: {
  snapshot: ClaimSnapshot
  ticketCode: string
  qrCodeDataUrl: string
  submitting: boolean
  refreshingRole: boolean
  confirmingRole: boolean
  rebindingRole: boolean
  redeeming: boolean
  onTicketCodeChange: (value: string) => void
  onSubmitTicket: () => void
  onOpenBindUrl: (useCurrentPage?: boolean) => void
  onRefreshRole: () => void
  onRebindRole: () => void
  onConfirmRole: () => void
  onConfirmRedeem: () => void
}) {
  if (!snapshot.flow) {
    return null
  }

  if (snapshot.currentStep === 1) {
    return (
      <ClaimTicketStep
        ticketCode={ticketCode}
        canSubmitTicket={snapshot.canSubmitTicket && !submitting}
        submitting={submitting}
        isBindingPreparing={snapshot.isBindingPreparing}
        flow={snapshot.flow}
        onTicketCodeChange={onTicketCodeChange}
        onSubmitTicket={onSubmitTicket}
      />
    )
  }

  if (snapshot.currentStep === 2) {
    return (
      <ClaimBindingStep
        snapshot={snapshot}
        qrCodeDataUrl={qrCodeDataUrl}
        refreshingRole={refreshingRole}
        confirmingRole={confirmingRole}
        rebindingRole={rebindingRole}
        onOpenBindUrl={onOpenBindUrl}
        onRefreshRole={onRefreshRole}
        onRebindRole={onRebindRole}
        onConfirmRole={onConfirmRole}
      />
    )
  }

  if (snapshot.currentStep === 3) {
    return (
      <ClaimConfirmStep
        roleName={snapshot.roleName}
        roleId={snapshot.roleId}
        product={snapshot.product}
        redeeming={redeeming}
        rebindingRole={rebindingRole}
        onConfirmRedeem={onConfirmRedeem}
        onRebindRole={onRebindRole}
      />
    )
  }

  return <ClaimResultStep snapshot={snapshot} />
}

function ClaimTicketStep({
  ticketCode,
  canSubmitTicket,
  submitting,
  isBindingPreparing,
  flow,
  onTicketCodeChange,
  onSubmitTicket,
}: {
  ticketCode: string
  canSubmitTicket: boolean
  submitting: boolean
  isBindingPreparing: boolean
  flow: ClaimKuaishouCloudFlowInfo
  onTicketCodeChange: (value: string) => void
  onSubmitTicket: () => void
}) {
  return (
    <Card className="claim-content-card">
      <Typography.Title level={2}>第 1 步：提交核销码</Typography.Title>
      <p className="claim-muted">
        请您先从快手小店复制核销码。提交后会立即核销，核销成功后系统会自动准备绑定资源。
      </p>

      <Input
        size="large"
        value={ticketCode}
        placeholder="粘贴快手核销码"
        disabled={!canSubmitTicket}
        onChange={(event) => onTicketCodeChange(event.target.value)}
        onPressEnter={onSubmitTicket}
      />

      <Button
        type="primary"
        size="large"
        icon={<SendOutlined />}
        loading={submitting}
        disabled={!canSubmitTicket}
        onClick={onSubmitTicket}
      >
        提交核销码并继续
      </Button>

      <Alert
        type="info"
        showIcon
        icon={<ClockCircleOutlined />}
        message={
          isBindingPreparing
            ? '核销完成后会自动准备绑定资源。'
            : '核销完成后会自动进入扫码绑定步骤。'
        }
      />

      {flow.guideImages.length > 0 ? (
        <Collapse
          defaultActiveKey={['guide']}
          items={[
            {
              key: 'guide',
              label: '查看核销码图文指引',
              children: (
                <Image.PreviewGroup>
                  <div className="claim-guide-grid">
                    {flow.guideImages.map((imageUrl, index) => (
                      <figure key={imageUrl} className="claim-guide-figure">
                        <Image src={imageUrl} alt={`步骤 ${index + 1}`} />
                        <figcaption>步骤 {index + 1}</figcaption>
                      </figure>
                    ))}
                  </div>
                </Image.PreviewGroup>
              ),
            },
          ]}
        />
      ) : null}
    </Card>
  )
}

function ClaimBindingStep({
  snapshot,
  qrCodeDataUrl,
  refreshingRole,
  confirmingRole,
  rebindingRole,
  onOpenBindUrl,
  onRefreshRole,
  onRebindRole,
  onConfirmRole,
}: {
  snapshot: ClaimSnapshot
  qrCodeDataUrl: string
  refreshingRole: boolean
  confirmingRole: boolean
  rebindingRole: boolean
  onOpenBindUrl: (useCurrentPage?: boolean) => void
  onRefreshRole: () => void
  onRebindRole: () => void
  onConfirmRole: () => void
}) {
  const flow = snapshot.flow
  if (!flow) {
    return null
  }

  return (
    <Card className="claim-content-card claim-binding-card">
      {snapshot.isBindingPrepared && qrCodeDataUrl ? (
        <div className="claim-qr-block">
          <div className="claim-qr-guide-panel">
            <div className="claim-scan-label claim-scan-label-left">Q区用Q扫</div>
            <img src={qrCodeDataUrl} alt="绑定二维码" className="claim-qr-code" />
            <div className="claim-scan-label claim-scan-label-right">V区用V扫</div>
            <p className="claim-save-hint">长按保存相册进行扫码绑定</p>
          </div>

          <Button icon={<LinkOutlined />} onClick={() => onOpenBindUrl(false)}>
            打开绑定链接
          </Button>
        </div>
      ) : (
        <Alert
          type="info"
          showIcon
          icon={<LoadingOutlined spin />}
          message="系统正在准备绑定资源，请稍后自动刷新。"
        />
      )}

      <div className={snapshot.isCustomerRoleReady ? 'claim-role-panel' : 'claim-role-panel pending'}>
        <InfoRow label="当前角色" value={snapshot.roleName || '待识别'} />
        <InfoRow label="角色 ID" value={snapshot.roleId || '-'} />
      </div>

      {flow.role.isDefaultRole ? (
        <Alert type="warning" showIcon message="当前仍是虚拟机默认角色，请重新绑定自己的角色信息。" />
      ) : null}

      <Space size={12} wrap className="claim-action-row">
        <Button size="large" icon={<ReloadOutlined />} loading={refreshingRole} onClick={onRefreshRole}>
          刷新角色信息
        </Button>
        <Button
          size="large"
          icon={<SwapOutlined />}
          loading={rebindingRole}
          disabled={confirmingRole}
          onClick={onRebindRole}
        >
          换绑角色
        </Button>
        <Tooltip title={snapshot.confirmRoleDisabledReason || undefined}>
          <span className="claim-action-tooltip-wrap">
            <Button
              type="primary"
              size="large"
              icon={<CheckCircleOutlined />}
              loading={confirmingRole}
              disabled={Boolean(snapshot.confirmRoleDisabledReason)}
              onClick={onConfirmRole}
            >
              我已完成绑定，下一步
            </Button>
          </span>
        </Tooltip>
      </Space>

      <div className="claim-meta-footer">
        <span>最近刷新：{formatAdminDateTime(flow.role.refreshedAt)}</span>
        <span>链接有效期：{formatAdminDateTime(flow.binding.bindExpiresAt)}</span>
        {flow.binding.bindProbeMessage ? (
          <span>
            链接检测：{flow.binding.bindProbeStatus || '-'} / {flow.binding.bindProbeMessage}
          </span>
        ) : null}
      </div>
    </Card>
  )
}

function ClaimConfirmStep({
  roleName,
  roleId,
  product,
  redeeming,
  rebindingRole,
  onConfirmRedeem,
  onRebindRole,
}: {
  roleName: string
  roleId: string
  product: ClaimProductInfo | null
  redeeming: boolean
  rebindingRole: boolean
  onConfirmRedeem: () => void
  onRebindRole: () => void
}) {
  return (
    <Card className="claim-content-card">
      <Typography.Title level={2}>第 3 步：确认兑换信息</Typography.Title>
      <p className="claim-muted">请再次确认角色和商品信息，确认无误后再继续兑换。</p>

      <div className="claim-info-grid">
        <InfoTile label="角色名称" value={roleName || '-'} />
        <InfoTile label="角色 ID" value={roleId || '-'} />
        <InfoTile label="领取商品" value={product?.title || '-'} />
        <InfoTile label="商品数量" value={String(product?.quantity || 0)} />
      </div>

      <ClaimProductItems product={product} />

      <Alert type="warning" showIcon message="兑换后不可取消，也不可退货。" />

      <Space size={12} wrap className="claim-action-row">
        <Button
          size="large"
          icon={<SwapOutlined />}
          loading={rebindingRole}
          disabled={redeeming}
          onClick={onRebindRole}
        >
          换绑角色
        </Button>
        <Button
          type="primary"
          size="large"
          icon={<CheckCircleOutlined />}
          loading={redeeming}
          disabled={rebindingRole}
          onClick={onConfirmRedeem}
        >
          确认兑换
        </Button>
      </Space>
    </Card>
  )
}

function ClaimResultStep({ snapshot }: { snapshot: ClaimSnapshot }) {
  const flow = snapshot.flow
  if (!flow) {
    return null
  }

  return (
    <Card className="claim-content-card">
      <div className="claim-result-header">
        {snapshot.resultVariant === 'warning' ? (
          <ExclamationCircleOutlined className="claim-large-icon claim-icon-warning" />
        ) : (
          <CheckCircleOutlined className={`claim-large-icon claim-icon-${snapshot.resultVariant}`} />
        )}
        <div>
          <Typography.Title level={2}>{snapshot.resultTitle}</Typography.Title>
          <p className="claim-muted">{snapshot.resultDescription}</p>
        </div>
      </div>

      <div className="claim-info-grid">
        <InfoTile label="结果时间" value={formatAdminDateTime(flow.dispatch.dispatchAt)} />
        <InfoTile label="领取商品" value={snapshot.product?.title || '-'} />
        <InfoTile label="角色名称" value={snapshot.roleName || '-'} />
        <InfoTile label="角色 ID" value={snapshot.roleId || '-'} />
        <InfoTile label="订单号" value={snapshot.order?.platformOrderId || '-'} />
        <InfoTile label="购买数量" value={String(snapshot.product?.quantity || 0)} />
      </div>

      <ClaimProductItems product={snapshot.product} />

      <Alert
        type={snapshot.resultVariant}
        showIcon
        message={
          snapshot.resultVariant === 'warning'
            ? '当前兑换未完成，客服会根据后台任务记录继续处理。'
            : '客户侧操作已经完成，后续履约结果请以后台任务界面为准。'
        }
      />
    </Card>
  )
}

function FeifeiClaimPanel({
  feifei,
  order,
  product,
  taskLastError,
  onOpen,
}: {
  feifei: ClaimKuaishouFeifeiFlowInfo
  order: ClaimOrderInfo | null
  product: ClaimProductInfo | null
  taskLastError?: string
  onOpen: () => void
}) {
  return (
    <Card className="claim-content-card">
      <div className="claim-feifei-main">
        <span className="claim-feifei-label">kuaishou-feifei</span>
        <Typography.Title level={2}>{product?.title || '商品领取'}</Typography.Title>
        <p>{feifei.rechargeStatusLabel || '待领取'}</p>
      </div>

      <div className="claim-info-grid">
        <InfoTile label="订单号" value={order?.platformOrderId || '-'} />
        <InfoTile label="平台单号" value={feifei.orderNo || feifei.platformOrderNo || '-'} />
      </div>

      <Button
        type="primary"
        size="large"
        icon={<LinkOutlined />}
        disabled={!feifei.h5.rechargeUrl && !feifei.h5.entryUrl}
        onClick={onOpen}
      >
        打开领取链接
      </Button>

      {taskLastError ? <Alert type="warning" showIcon message={taskLastError} /> : null}
    </Card>
  )
}

function ClaimProductItems({
  product,
  compact = false,
}: {
  product: ClaimProductInfo | null
  compact?: boolean
}) {
  const items = getProductItems(product)
  const hasMultipleItems = Boolean(product?.isBundle || items.length > 1)

  if (!items.length) {
    return null
  }

  return (
    <div className={compact ? 'claim-product-items compact' : 'claim-product-items'}>
      <div className="claim-product-items-header">
        <span>{hasMultipleItems ? '包含商品' : '商品明细'}</span>
        {hasMultipleItems ? <strong>{items.length} 项</strong> : null}
      </div>

      <div className="claim-product-items-list">
        {items.map((item) => (
          <div key={item.key} className="claim-product-item-row">
            <span>{item.name}</span>
            <strong>x{item.quantity}</strong>
          </div>
        ))}
      </div>
    </div>
  )
}

function getProductItems(product: ClaimProductInfo | null) {
  const items = Array.isArray(product?.items) ? product.items : []
  return items
    .map((item) => ({
      key: item.cloudSkuId || item.name,
      name: String(item.name || '').trim(),
      quantity: Math.max(1, Number(item.quantity || 1) || 1),
    }))
    .filter((item) => item.name)
}

function InfoTile({ label, value }: { label: string; value: string }) {
  return (
    <div className="claim-info-item">
      <span>{label}</span>
      <strong>{value}</strong>
    </div>
  )
}

function InfoRow({ label, value }: { label: string; value: string }) {
  return (
    <div className="claim-role-panel-item">
      <span>{label}</span>
      <strong>{value}</strong>
    </div>
  )
}
