import {
  CheckCircleOutlined,
  CopyOutlined,
  DeleteOutlined,
  LinkOutlined,
  PlusOutlined,
  ReloadOutlined,
  SaveOutlined,
  SearchOutlined,
  SendOutlined,
} from '@ant-design/icons'
import {
  Alert,
  Button,
  Card,
  Empty,
  Input,
  InputNumber,
  Popconfirm,
  Space,
  Spin,
  Switch,
  Table,
  Tabs,
  Tag,
  Typography,
} from 'antd'
import type { TableColumnsType } from 'antd'
import { useEffect, useState } from 'react'
import { useSearchParams } from 'react-router'

import PageHeader from '@/components/admin/PageHeader'
import { showError, showSuccess } from '@/lib/feedback'
import {
  backAdminCloudtentaclesVn,
  deleteAdminCloudtentaclesSource,
  exchangeAdminKuaishouIndustryAuthorizationCode,
  fetchAdminCloudtentaclesAsset,
  fetchAdminCloudtentaclesSkuList,
  fetchAdminCloudtentaclesSourceConfig,
  fetchAdminCloudtentaclesVnList,
  fetchAdminAffiliateDashConfig,
  fetchAdminAffiliateDashProducts,
  fetchAdminAffiliateDashWallet,
  fetchAdminKuaishouFeifeiConfig,
  fetchAdminKuaishouIndustrySourceConfig,
  fetchAdminNotificationConfig,
  fetchAdminScheduledJobsConfig,
  matchAdminAffiliateDashSku,
  matchAdminKuaishouFeifeiProduct,
  refreshAdminKuaishouIndustryAccessToken,
  runAdminScheduledJob,
  saveAdminCloudtentaclesSourceConfig,
  saveAdminAffiliateDashConfig,
  saveAdminKuaishouFeifeiConfig,
  saveAdminKuaishouIndustrySourceConfig,
  saveAdminNotificationConfig,
  saveAdminScheduledJobsConfig,
  sendAdminCloudtentaclesSmsCode,
  syncAdminKuaishouFeifeiProducts,
  testAdminCloudtentaclesLogin,
  testAdminNotification,
  validateAdminCloudtentaclesSession,
} from '@/services/admin'
import type {
  AdminCloudtentaclesMonitorAccount,
  AdminCloudtentaclesSourceConfigResponse,
  AdminCloudtentaclesSourceItem,
  AdminCloudtentaclesVnListItem,
  AdminAffiliateDashConfig,
  AdminAffiliateDashConfigResponse,
  AdminAffiliateDashMatchResult,
  AdminAffiliateDashProductItem,
  AdminAffiliateDashWallet,
  AdminKuaishouIndustryConfigResponse,
  AdminKuaishouIndustryShopConfig,
  AdminKuaishouIndustrySourceConfig,
  AdminKuaishouFeifeiConfig,
  AdminKuaishouFeifeiConfigResponse,
  AdminKuaishouFeifeiMatchResult,
  AdminKuaishouFeifeiProductRule,
  AdminNotificationConfig,
  AdminNotificationTestResult,
  AdminScheduledJobAccountRuntime,
  AdminScheduledJobCloudtentaclesAccount,
  AdminScheduledJobItem,
  AdminScheduledJobRuntimeState,
  AdminScheduledJobsConfig,
} from '@/types/admin'
import { hasAdminRole } from '@/utils/admin-auth'
import { buildAdminLocalTablePagination } from '@/utils/admin-pagination'
import { formatAdminDateTime } from '@/utils/admin-time'

type PlatformTab =
  | 'notifications'
  | 'kuaishouIndustry'
  | 'kuaishouFeifei'
  | 'cloudtentacles'
  | 'affiliateDash'

type NotificationConfigResponse = {
  filePath: string
  source: AdminNotificationConfig
}

type ScheduledJobsState = {
  filePath: string
  source: AdminScheduledJobsConfig
  runtime: AdminScheduledJobRuntimeState[]
  cloudtentaclesAccounts: AdminCloudtentaclesMonitorAccount[]
}

const platformTabs: PlatformTab[] = [
  'notifications',
  'kuaishouIndustry',
  'kuaishouFeifei',
  'cloudtentacles',
  'affiliateDash',
]

export default function AdminPlatformShopsPage() {
  const [searchParams, setSearchParams] = useSearchParams()
  const activeTab = normalizePlatformTab(searchParams.get('tab'))
  const [loading, setLoading] = useState(true)
  const [errorMessage, setErrorMessage] = useState('')
  const [notificationConfig, setNotificationConfig] = useState<NotificationConfigResponse | null>(
    null,
  )
  const [scheduledJobs, setScheduledJobs] = useState<ScheduledJobsState | null>(null)
  const [industryConfig, setIndustryConfig] = useState<AdminKuaishouIndustryConfigResponse | null>(
    null,
  )
  const [feifeiConfig, setFeifeiConfig] = useState<AdminKuaishouFeifeiConfigResponse | null>(null)
  const [cloudtentaclesConfig, setCloudtentaclesConfig] =
    useState<AdminCloudtentaclesSourceConfigResponse | null>(null)
  const [_affiliateDashConfig, setAffiliateDashConfig] =
    useState<AdminAffiliateDashConfigResponse | null>(null)

  useEffect(() => {
    void loadConfigs()
  }, [])

  async function loadConfigs() {
    if (!hasAdminRole('admin')) {
      setLoading(false)
      return
    }

    setLoading(true)
    setErrorMessage('')

    try {
      const [
        notificationResponse,
        scheduledJobsResponse,
        industryResponse,
        feifeiResponse,
        cloudtentaclesResponse,
        affiliateDashResponse,
      ] = await Promise.all([
        fetchAdminNotificationConfig(),
        fetchAdminScheduledJobsConfig(),
        fetchAdminKuaishouIndustrySourceConfig(),
        fetchAdminKuaishouFeifeiConfig(),
        fetchAdminCloudtentaclesSourceConfig(),
        fetchAdminAffiliateDashConfig(),
      ])

      setNotificationConfig(notificationResponse.data)
      setScheduledJobs(scheduledJobsResponse.data)
      setIndustryConfig(industryResponse.data)
      setFeifeiConfig(feifeiResponse.data)
      setCloudtentaclesConfig(cloudtentaclesResponse.data)
      setAffiliateDashConfig(affiliateDashResponse.data)
    } catch (error) {
      setErrorMessage(error instanceof Error ? error.message : '读取平台配置失败')
    } finally {
      setLoading(false)
    }
  }

  function updateActiveTab(key: string) {
    setSearchParams({ tab: normalizePlatformTab(key) })
  }

  return (
    <section className="page-stack">
      <PageHeader
        title="平台配置"
        description="按平台分别管理来源接入、凭据与店铺能力。"
        extra={
          <Button icon={<ReloadOutlined />} loading={loading} onClick={loadConfigs}>
            刷新全部
          </Button>
        }
      />

      {!hasAdminRole('admin') ? (
        <Card>
          <Empty description="仅管理员可以维护平台配置" />
        </Card>
      ) : (
        <>
          {errorMessage ? <Alert type="error" showIcon message={errorMessage} /> : null}
          {loading ? (
            <Card>
              <Spin />
            </Card>
          ) : (
            <>
              <Tabs
                activeKey={activeTab}
                onChange={updateActiveTab}
                className="platform-tabs"
                items={[
                  {
                    key: 'notifications',
                    label: '内部通知',
                    children:
                      notificationConfig && scheduledJobs ? (
                        <NotificationPanel
                          notificationConfig={notificationConfig}
                          scheduledJobs={scheduledJobs}
                          onNotificationChange={setNotificationConfig}
                          onScheduledJobsChange={setScheduledJobs}
                        />
                      ) : (
                        <Empty description="通知配置未加载" />
                      ),
                  },
                  {
                    key: 'kuaishouIndustry',
                    label: '行业电子凭证',
                    children: industryConfig ? (
                      <KuaishouIndustryPanel config={industryConfig} onChange={setIndustryConfig} />
                    ) : (
                      <Empty description="快手行业电子凭证配置未加载" />
                    ),
                  },
                  {
                    key: 'kuaishouFeifei',
                    label: 'kuaishou-feifei',
                    children: feifeiConfig ? (
                      <KuaishouFeifeiPlatformPanel
                        config={feifeiConfig}
                        onChange={setFeifeiConfig}
                      />
                    ) : (
                      <Empty description="kuaishou-feifei 配置未加载" />
                    ),
                  },
                  {
                    key: 'affiliateDash',
                    label: 'affiliate-dash',
                    children: <AffiliateDashPlatformPanel />,
                  },
                  {
                    key: 'cloudtentacles',
                    label: 'kuaishou-lewan',
                    children: cloudtentaclesConfig ? (
                      <CloudtentaclesPlatformPanel
                        config={cloudtentaclesConfig}
                        onChange={setCloudtentaclesConfig}
                      />
                    ) : (
                      <Empty description="kuaishou-lewan 配置未加载" />
                    ),
                  },
                ]}
              />
            </>
          )}
        </>
      )}
    </section>
  )
}

function NotificationPanel({
  notificationConfig,
  scheduledJobs,
  onNotificationChange,
  onScheduledJobsChange,
}: {
  notificationConfig: NotificationConfigResponse
  scheduledJobs: ScheduledJobsState
  onNotificationChange: (data: NotificationConfigResponse) => void
  onScheduledJobsChange: (data: ScheduledJobsState) => void
}) {
  const [savingNotification, setSavingNotification] = useState(false)
  const [testingNotification, setTestingNotification] = useState(false)
  const [savingJobs, setSavingJobs] = useState(false)
  const [runningJobId, setRunningJobId] = useState('')
  const [testResult, setTestResult] = useState<AdminNotificationTestResult | null>(null)
  const source = notificationConfig.source
  const monitorAccounts = scheduledJobs.cloudtentaclesAccounts || []
  const jobsWithMergedAccounts = scheduledJobs.source.jobs.map((job) => {
    if (job.type !== 'cloudtentacles_health') {
      return job
    }
    return {
      ...job,
      config: {
        ...job.config,
        accounts: mergeScheduledJobAccounts(
          job.config?.accounts || [],
          monitorAccounts,
          Number(job.config?.assetThreshold ?? 500),
        ),
      },
    }
  })

  async function saveNotification() {
    setSavingNotification(true)
    try {
      const response = await saveAdminNotificationConfig(source)
      onNotificationChange(response.data)
      showSuccess('通知配置已保存')
    } catch (error) {
      showError(error instanceof Error ? error.message : '保存通知配置失败')
    } finally {
      setSavingNotification(false)
    }
  }

  async function testNotification() {
    setTestingNotification(true)
    try {
      const response = await testAdminNotification({
        title: '订单系统通知测试',
        body: '这是一条来自 React 后台的测试通知',
      })
      setTestResult(response.data)
      showSuccess(`通知测试完成，成功 ${response.data.successCount} 条`)
    } catch (error) {
      showError(error instanceof Error ? error.message : '发送测试通知失败')
    } finally {
      setTestingNotification(false)
    }
  }

  async function saveJobs() {
    setSavingJobs(true)
    try {
      const payload: AdminScheduledJobsConfig = {
        ...scheduledJobs.source,
        jobs: jobsWithMergedAccounts.map((job) => {
          if (job.type !== 'cloudtentacles_health') {
            return { ...job }
          }
          return {
            ...job,
            config: {
              assetThreshold: Number(job.config?.assetThreshold ?? 500),
              accounts: (job.config?.accounts || []).map((account) => ({
                sourceKey: account.sourceKey,
                label: account.label,
                enabled: account.enabled !== false,
                assetThreshold: Number(account.assetThreshold ?? job.config?.assetThreshold ?? 500),
              })),
            },
          }
        }),
      }
      const response = await saveAdminScheduledJobsConfig(payload)
      onScheduledJobsChange(response.data)
      showSuccess('定时任务配置已保存')
    } catch (error) {
      showError(error instanceof Error ? error.message : '保存定时任务配置失败')
    } finally {
      setSavingJobs(false)
    }
  }

  async function runJob(jobId: string) {
    setRunningJobId(jobId)
    try {
      const response = await runAdminScheduledJob(jobId)
      onScheduledJobsChange({
        ...scheduledJobs,
        runtime: response.data.runtime,
      })
      showSuccess('定时任务已触发')
    } catch (error) {
      showError(error instanceof Error ? error.message : '触发定时任务失败')
    } finally {
      setRunningJobId('')
    }
  }

  function updateNotification(nextSource: AdminNotificationConfig) {
    onNotificationChange({ ...notificationConfig, source: nextSource })
  }

  function updateJobs(nextSource: AdminScheduledJobsConfig) {
    onScheduledJobsChange({ ...scheduledJobs, source: nextSource })
  }

  function updateJob(index: number, nextJob: AdminScheduledJobItem) {
    updateJobs({
      ...scheduledJobs.source,
      jobs: jobsWithMergedAccounts.map((item, jobIndex) => (jobIndex === index ? nextJob : item)),
    })
  }

  return (
    <section className="platform-panel-stack">
      <Card
        title="通知渠道"
        extra={
          <Space wrap>
            <Button
              icon={<SendOutlined />}
              loading={testingNotification}
              onClick={testNotification}
            >
              发送测试
            </Button>
            <Button
              type="primary"
              icon={<SaveOutlined />}
              loading={savingNotification}
              onClick={saveNotification}
            >
              保存通知
            </Button>
          </Space>
        }
      >
        <div className="platform-form-grid">
          <FieldSwitch
            label="通知总开关"
            checked={source.enabled}
            onChange={(enabled) => updateNotification({ ...source, enabled })}
          />
          <FieldSwitch
            label="Bark"
            checked={source.channels.bark.enabled}
            onChange={(enabled) =>
              updateNotification({
                ...source,
                channels: {
                  ...source.channels,
                  bark: { ...source.channels.bark, enabled },
                },
              })
            }
          />
          <FieldSwitch
            label="WPush"
            checked={source.channels.wpush.enabled}
            onChange={(enabled) =>
              updateNotification({
                ...source,
                channels: {
                  ...source.channels,
                  wpush: { ...source.channels.wpush, enabled },
                },
              })
            }
          />
        </div>

        <div className="platform-section-gap">
          <Typography.Text type="secondary">Bark 服务地址</Typography.Text>
          <Input
            value={source.channels.bark.serverUrl}
            placeholder="https://api.day.app"
            onChange={(event) =>
              updateNotification({
                ...source,
                channels: {
                  ...source.channels,
                  bark: { ...source.channels.bark, serverUrl: event.target.value },
                },
              })
            }
          />
        </div>

        <RecipientList
          title="Bark 接收人"
          secretField="deviceKey"
          items={source.channels.bark.recipients}
          onChange={(recipients) =>
            updateNotification({
              ...source,
              channels: {
                ...source.channels,
                bark: { ...source.channels.bark, recipients },
              },
            })
          }
        />

        <RecipientList
          title="WPush 接收人"
          secretField="apiKey"
          items={source.channels.wpush.recipients}
          onChange={(recipients) =>
            updateNotification({
              ...source,
              channels: {
                ...source.channels,
                wpush: { ...source.channels.wpush, recipients },
              },
            })
          }
        />

        {testResult ? (
          <Alert
            className="platform-section-gap"
            type={testResult.failedCount > 0 ? 'warning' : 'success'}
            showIcon
            message={`测试结果：成功 ${testResult.successCount}，失败 ${testResult.failedCount}，跳过 ${testResult.skippedCount}`}
          />
        ) : null}
      </Card>

      <Card
        title="监控任务"
        extra={
          <Button type="primary" icon={<SaveOutlined />} loading={savingJobs} onClick={saveJobs}>
            保存任务
          </Button>
        }
      >
        <Typography.Paragraph type="secondary" style={{ marginBottom: 12 }}>
          按 kuaishou-lewan 账号分别检查登录态和余额；新增账号会自动纳入监控，可单独设置阈值。
        </Typography.Paragraph>
        <FieldSwitch
          label="任务总开关"
          checked={scheduledJobs.source.enabled}
          onChange={(enabled) => updateJobs({ ...scheduledJobs.source, enabled })}
        />

        <Space direction="vertical" className="full-width platform-section-gap" size={12}>
          {jobsWithMergedAccounts.map((job, index) => (
            <ScheduledJobCard
              key={job.id}
              job={job}
              runtime={scheduledJobs.runtime.find((item) => item.id === job.id) || null}
              monitorAccounts={monitorAccounts}
              running={runningJobId === job.id}
              onRun={() => runJob(job.id)}
              onChange={(nextJob) => updateJob(index, nextJob)}
            />
          ))}
        </Space>
      </Card>
    </section>
  )
}

function RecipientList<T extends { id: string; name: string; enabled: boolean }>({
  title,
  secretField,
  items,
  onChange,
}: {
  title: string
  secretField: 'deviceKey' | 'apiKey'
  items: Array<
    T & Partial<Record<'deviceKey' | 'apiKey' | 'deviceKeyMasked' | 'apiKeyMasked', string>>
  >
  onChange: (
    items: Array<
      T & Partial<Record<'deviceKey' | 'apiKey' | 'deviceKeyMasked' | 'apiKeyMasked', string>>
    >,
  ) => void
}) {
  function addRecipient() {
    onChange([
      ...items,
      {
        id: `recipient_${Date.now()}`,
        name: '',
        enabled: true,
        [secretField]: '',
      } as T & Partial<Record<'deviceKey' | 'apiKey', string>>,
    ])
  }

  function updateRecipient(
    index: number,
    patch: Partial<T & Record<'deviceKey' | 'apiKey', string>>,
  ) {
    onChange(items.map((item, itemIndex) => (itemIndex === index ? { ...item, ...patch } : item)))
  }

  return (
    <Card
      size="small"
      title={title}
      className="platform-section-gap"
      extra={
        <Button icon={<PlusOutlined />} onClick={addRecipient}>
          新增
        </Button>
      }
    >
      {items.length === 0 ? (
        <Empty description="暂无接收人" />
      ) : (
        <Space direction="vertical" className="full-width" size={8}>
          {items.map((item, index) => (
            <div key={item.id || index} className="platform-inline-row">
              <Switch
                checked={item.enabled !== false}
                onChange={(enabled) =>
                  updateRecipient(index, { enabled } as Partial<
                    T & Record<'deviceKey' | 'apiKey', string>
                  >)
                }
              />
              <Input
                value={item.name}
                placeholder="名称"
                onChange={(event) =>
                  updateRecipient(index, { name: event.target.value } as Partial<
                    T & Record<'deviceKey' | 'apiKey', string>
                  >)
                }
              />
              <Input.Password
                value={String(item[secretField] || '')}
                placeholder={String(
                  item[`${secretField}Masked` as keyof typeof item] || secretField,
                )}
                onChange={(event) =>
                  updateRecipient(index, {
                    [secretField]: event.target.value,
                  } as Partial<T & Record<'deviceKey' | 'apiKey', string>>)
                }
              />
              <Button
                danger
                icon={<DeleteOutlined />}
                onClick={() => onChange(items.filter((_, itemIndex) => itemIndex !== index))}
              />
            </div>
          ))}
        </Space>
      )}
    </Card>
  )
}

function ScheduledJobCard({
  job,
  runtime,
  monitorAccounts,
  running,
  onRun,
  onChange,
}: {
  job: AdminScheduledJobItem
  runtime: AdminScheduledJobRuntimeState | null
  monitorAccounts: AdminCloudtentaclesMonitorAccount[]
  running: boolean
  onRun: () => void
  onChange: (job: AdminScheduledJobItem) => void
}) {
  if (job.type === 'work_order_timeout' || job.id === 'work-order-timeout') {
    return (
      <Card
        size="small"
        title={
          <Space wrap>
            <Switch
              checked={job.enabled !== false}
              onChange={(enabled) => onChange({ ...job, enabled })}
            />
            <span>{formatScheduledJobTitle(job)}</span>
            <Tag color={job.enabled ? 'green' : 'default'}>{job.enabled ? '已启用' : '已停用'}</Tag>
          </Space>
        }
        extra={
          <Button icon={<ReloadOutlined />} loading={running} onClick={onRun}>
            立即扫描
          </Button>
        }
      >
        <Typography.Paragraph type="secondary" style={{ marginBottom: 12 }}>
          扫描代练中且已过截止时间的工单，按工单超时策略自动处置（退回大厅 / 取消退押金 /
          取消扣押金）。
        </Typography.Paragraph>
        <div className="platform-form-grid">
          <NumberField
            label="执行间隔（秒）"
            value={job.intervalSeconds}
            min={1}
            onChange={(intervalSeconds) => onChange({ ...job, intervalSeconds })}
          />
          <NumberField
            label="每次扫描数量"
            value={Number(job.config?.scanLimit ?? 50)}
            min={1}
            onChange={(scanLimit) => onChange({ ...job, config: { ...job.config, scanLimit } })}
          />
        </div>
        {runtime ? (
          <Alert
            className="platform-section-gap"
            type={
              runtime.lastStatus === 'ok' || runtime.lastStatus === 'success'
                ? 'success'
                : runtime.lastStatus
                  ? 'warning'
                  : 'info'
            }
            showIcon
            message={runtime.lastMessage || '尚未运行'}
            description={[
              `扫描 ${runtime.lastCheckedCount ?? 0}`,
              `处置 ${runtime.lastAsset ?? 0}`,
              `跳过 ${runtime.lastFailedCount ?? 0}`,
              `上次：${formatAdminDateTime(runtime.lastFinishedAt || runtime.lastRunAt)}`,
              `下次：${formatAdminDateTime(runtime.nextRunAt)}`,
            ].join(' · ')}
          />
        ) : null}
      </Card>
    )
  }

  if (job.type === 'deposit_unfreeze' || job.id === 'deposit-unfreeze') {
    // 押金到期解冻配置已迁至 接单平台 → 杂项配置，这里不再渲染。
    return null
  }
  const accounts = job.config?.accounts || []
  const defaultThreshold = Number(job.config?.assetThreshold ?? 500)

  function updateAccount(
    sourceKey: string,
    patch: Partial<AdminScheduledJobCloudtentaclesAccount>,
  ) {
    onChange({
      ...job,
      config: {
        ...job.config,
        accounts: accounts.map((account) =>
          account.sourceKey === sourceKey ? { ...account, ...patch } : account,
        ),
      },
    })
  }

  function updateDefaultThreshold(assetThreshold: number) {
    onChange({
      ...job,
      config: {
        ...job.config,
        assetThreshold,
        accounts: accounts.map((account) => ({
          ...account,
          // 仅同步仍等于旧默认阈值的账号，避免覆盖用户单独设置
          assetThreshold:
            Number(account.assetThreshold) === defaultThreshold
              ? assetThreshold
              : account.assetThreshold,
        })),
      },
    })
  }

  const accountColumns: TableColumnsType<AdminScheduledJobCloudtentaclesAccount> = [
    {
      title: '账号',
      minWidth: 220,
      render: (_, account) => {
        const source = monitorAccounts.find((item) => item.sourceKey === account.sourceKey)
        return (
          <Space direction="vertical" size={0}>
            <Typography.Text strong>{account.label || account.sourceKey}</Typography.Text>
            <Typography.Text type="secondary">{account.sourceKey}</Typography.Text>
            <Typography.Text type="secondary">
              {formatMonitorAccountMeta(account, source)}
            </Typography.Text>
          </Space>
        )
      },
    },
    {
      title: '监控',
      width: 90,
      align: 'center',
      render: (_, account) => (
        <Switch
          size="small"
          checked={account.enabled !== false}
          onChange={(enabled) => updateAccount(account.sourceKey, { enabled })}
        />
      ),
    },
    {
      title: '余额阈值',
      width: 140,
      render: (_, account) => (
        <InputNumber
          min={0}
          value={account.assetThreshold}
          style={{ width: '100%' }}
          onChange={(value) =>
            updateAccount(account.sourceKey, {
              assetThreshold: Number(value ?? defaultThreshold),
            })
          }
        />
      ),
    },
    {
      title: '最近结果',
      minWidth: 240,
      render: (_, account) => {
        const accountRuntime = getAccountRuntime(runtime, account.sourceKey)
        if (!accountRuntime) {
          return <Typography.Text type="secondary">尚未检查</Typography.Text>
        }

        return (
          <Space direction="vertical" size={2}>
            <Space wrap size={6}>
              <Tag color={resolveHealthStatusColor(accountRuntime.status)}>
                {formatHealthStatus(accountRuntime.status)}
              </Tag>
              <Typography.Text type="secondary">
                余额 {accountRuntime.asset ?? '-'} / 阈值{' '}
                {accountRuntime.threshold ?? account.assetThreshold}
              </Typography.Text>
            </Space>
            <Typography.Text type="secondary">
              {accountRuntime.message || formatHealthStatus(accountRuntime.status)}
            </Typography.Text>
            <Typography.Text type="secondary">
              {formatAdminDateTime(accountRuntime.checkedAt)}
            </Typography.Text>
          </Space>
        )
      },
    },
  ]

  return (
    <Card
      size="small"
      title={
        <Space wrap>
          <Switch
            checked={job.enabled !== false}
            onChange={(enabled) => onChange({ ...job, enabled })}
          />
          <span>{formatScheduledJobTitle(job)}</span>
          <Tag color={job.enabled ? 'green' : 'default'}>{job.enabled ? '已启用' : '已停用'}</Tag>
        </Space>
      }
      extra={
        <Button icon={<ReloadOutlined />} loading={running} onClick={onRun}>
          立即检查
        </Button>
      }
    >
      <div className="platform-form-grid">
        <NumberField
          label="执行间隔（秒）"
          value={job.intervalSeconds}
          min={1}
          onChange={(intervalSeconds) => onChange({ ...job, intervalSeconds })}
        />
        <NumberField
          label="冷却时间（秒）"
          value={job.cooldownSeconds}
          min={0}
          onChange={(cooldownSeconds) => onChange({ ...job, cooldownSeconds })}
        />
        <NumberField
          label="默认余额阈值"
          value={defaultThreshold}
          min={0}
          onChange={(assetThreshold) => updateDefaultThreshold(Number(assetThreshold ?? 500))}
        />
      </div>

      {runtime ? (
        <Alert
          className="platform-section-gap"
          type={
            runtime.lastStatus === 'ok' || runtime.lastStatus === 'success'
              ? 'success'
              : runtime.lastStatus
                ? 'warning'
                : 'info'
          }
          showIcon
          message={runtime.lastMessage || '尚未运行'}
          description={[
            `已检查 ${runtime.lastCheckedCount ?? 0} / ${accounts.length}`,
            `预警 ${runtime.lastLowAssetCount ?? 0}`,
            `异常 ${runtime.lastFailedCount ?? 0}`,
            `上次：${formatAdminDateTime(runtime.lastFinishedAt || runtime.lastRunAt)}`,
            `下次：${formatAdminDateTime(runtime.nextRunAt)}`,
          ].join(' · ')}
        />
      ) : null}

      <Table<AdminScheduledJobCloudtentaclesAccount>
        className="platform-section-gap"
        rowKey="sourceKey"
        size="small"
        pagination={false}
        columns={accountColumns}
        dataSource={accounts}
        locale={{ emptyText: '暂无 kuaishou-lewan 账号，请先在平台配置登录。' }}
      />
    </Card>
  )
}

function mergeScheduledJobAccounts(
  configuredAccounts: AdminScheduledJobCloudtentaclesAccount[],
  monitorAccounts: AdminCloudtentaclesMonitorAccount[],
  defaultAssetThreshold: number,
) {
  const configuredMap = new Map(
    configuredAccounts
      .map((item) => [String(item.sourceKey || '').trim(), item] as const)
      .filter(([sourceKey]) => Boolean(sourceKey)),
  )
  const merged = monitorAccounts
    .filter((item) => String(item.sourceKey || '').trim())
    .map((item) => {
      const sourceKey = String(item.sourceKey || '').trim()
      const configured = configuredMap.get(sourceKey)
      return {
        sourceKey,
        label: String(configured?.label || item.label || sourceKey).trim(),
        enabled: configured ? configured.enabled !== false : item.enabled !== false,
        assetThreshold: Number(configured?.assetThreshold ?? defaultAssetThreshold),
      }
    })
  const mergedKeys = new Set(merged.map((item) => item.sourceKey))

  for (const configured of configuredAccounts) {
    const sourceKey = String(configured.sourceKey || '').trim()
    if (!sourceKey || mergedKeys.has(sourceKey)) {
      continue
    }
    merged.push({
      sourceKey,
      label: String(configured.label || sourceKey).trim(),
      enabled: configured.enabled !== false,
      assetThreshold: Number(configured.assetThreshold ?? defaultAssetThreshold),
    })
  }

  return merged
}

function formatScheduledJobTitle(job: AdminScheduledJobItem) {
  if (job.type === 'cloudtentacles_health' || job.id === 'cloudtentacles-health') {
    return 'kuaishou-lewan 健康检查'
  }
  if (job.type === 'work_order_timeout' || job.id === 'work-order-timeout') {
    return '接单工单超时扫描'
  }
  return job.id || job.type || '定时任务'
}

function formatMonitorAccountMeta(
  account: AdminScheduledJobCloudtentaclesAccount,
  source: AdminCloudtentaclesMonitorAccount | undefined,
) {
  if (!source) {
    return '账号配置不存在'
  }

  return (
    [
      source.username ? `登录名 ${source.username}` : '',
      source.phoneMasked ? `手机 ${source.phoneMasked}` : '',
      source.hasToken ? 'Token 已保存' : '未登录',
    ]
      .filter(Boolean)
      .join(' · ') || account.sourceKey
  )
}

function getAccountRuntime(
  runtime: AdminScheduledJobRuntimeState | null,
  sourceKey: string,
): AdminScheduledJobAccountRuntime | null {
  if (!runtime?.lastAccounts?.length) {
    return null
  }
  return runtime.lastAccounts.find((item) => item.sourceKey === sourceKey) || null
}

function formatHealthStatus(status: string) {
  const normalized = String(status || '').trim()
  if (normalized === 'ok' || normalized === 'success') return '正常'
  if (normalized === 'asset_low') return '余额预警'
  if (normalized === 'auth_missing') return '未登录'
  if (normalized === 'source_missing') return '账号缺失'
  if (normalized === 'disabled') return '已停用'
  if (normalized === 'skipped') return '已跳过'
  if (normalized === 'running') return '执行中'
  if (normalized === 'failed') return '检查失败'
  if (normalized === 'pending') return '待执行'
  return normalized || '待执行'
}

function resolveHealthStatusColor(status: string) {
  const normalized = String(status || '').trim()
  if (normalized === 'ok' || normalized === 'success') return 'green'
  if (normalized === 'asset_low' || normalized === 'running') return 'orange'
  if (['auth_missing', 'source_missing', 'failed'].includes(normalized)) return 'red'
  return 'default'
}

function KuaishouIndustryPanel({
  config,
  onChange,
}: {
  config: AdminKuaishouIndustryConfigResponse
  onChange: (config: AdminKuaishouIndustryConfigResponse) => void
}) {
  const [searchParams] = useSearchParams()
  const [saving, setSaving] = useState(false)
  const [actionLoading, setActionLoading] = useState('')
  const [authorizationCodes, setAuthorizationCodes] = useState<Record<string, string>>({})
  const source = config.source
  const shops = source.shops || []
  const enabledShopCount = shops.filter((shop) => shop.enabled !== false).length
  const validTokenCount = shops.filter((shop) => shop.accessTokenStatus === 'valid').length
  const problemTokenCount = shops.filter((shop) =>
    ['expired', 'expiring', 'missing'].includes(shop.accessTokenStatus),
  ).length

  useEffect(() => {
    const code = String(searchParams.get('code') || '').trim()
    if (!code || shops.length === 0) {
      return
    }

    const state = String(searchParams.get('state') || '').trim()
    const matchedIndex = state
      ? shops.findIndex((shop) =>
          [shop.authState, shop.sellerId, shop.shopId].some((value) => value === state),
        )
      : -1
    const enabledIndex = shops.findIndex((shop) => shop.enabled !== false)
    const targetIndex = matchedIndex >= 0 ? matchedIndex : Math.max(enabledIndex, 0)
    const actionKey = buildIndustryShopActionKey(targetIndex)

    setAuthorizationCodes((current) => {
      if (current[actionKey] || Object.values(current).some((value) => value.trim() === code)) {
        return current
      }

      return {
        ...current,
        [actionKey]: code,
      }
    })
  }, [searchParams, shops])

  async function saveConfig() {
    setSaving(true)
    try {
      const response = await saveAdminKuaishouIndustrySourceConfig(source)
      onChange(response.data)
      showSuccess('快手行业电子凭证配置已保存')
    } catch (error) {
      showError(error instanceof Error ? error.message : '保存快手行业电子凭证配置失败')
    } finally {
      setSaving(false)
    }
  }

  async function refreshToken(shop: AdminKuaishouIndustryShopConfig, index: number) {
    const sellerId = shop.sellerId.trim()
    if (!sellerId) {
      showError('请先填写 sellerId')
      return
    }

    setActionLoading(`refresh-${index}`)
    try {
      const saved = await saveAdminKuaishouIndustrySourceConfig(source)
      onChange(saved.data)
      const response = await refreshAdminKuaishouIndustryAccessToken({ sellerId })
      onChange(response.data)
      showSuccess(`${resolveIndustryShopDisplayName(shop)} 的 accessToken 已刷新`)
    } catch (error) {
      showError(error instanceof Error ? error.message : '刷新 accessToken 失败')
    } finally {
      setActionLoading('')
    }
  }

  async function exchangeAuthorizationCode(shop: AdminKuaishouIndustryShopConfig, index: number) {
    const sellerId = shop.sellerId.trim()
    if (!sellerId) {
      showError('请先填写 sellerId')
      return
    }

    const actionKey = buildIndustryShopActionKey(index)
    const code = String(authorizationCodes[actionKey] || '').trim()
    if (!code) {
      showError('请输入授权 code')
      return
    }

    setActionLoading(`exchange-${index}`)
    try {
      const saved = await saveAdminKuaishouIndustrySourceConfig(source)
      onChange(saved.data)
      const response = await exchangeAdminKuaishouIndustryAuthorizationCode({
        code,
        sellerId,
        shopName: shop.shopName,
        customShopName: shop.customShopName,
      })
      onChange(response.data)
      setAuthorizationCodes((current) => ({ ...current, [actionKey]: '' }))
      showSuccess(`${resolveIndustryShopDisplayName(shop)} 的授权 token 已保存`)
    } catch (error) {
      showError(error instanceof Error ? error.message : '授权 code 换 token 失败')
    } finally {
      setActionLoading('')
    }
  }

  async function copyAuthorizationUrl(shop: AdminKuaishouIndustryShopConfig) {
    const authorizationUrl = buildIndustryAuthorizationUrl(source, shop)
    if (!authorizationUrl) {
      showError('授权链接未生成')
      return
    }

    try {
      await navigator.clipboard.writeText(authorizationUrl)
      showSuccess('授权链接已复制')
    } catch (error) {
      showError(error instanceof Error ? error.message : '复制授权链接失败')
    }
  }

  function openAuthorizationUrl(shop: AdminKuaishouIndustryShopConfig) {
    const authorizationUrl = buildIndustryAuthorizationUrl(source, shop)
    if (!authorizationUrl) {
      showError('授权链接未生成')
      return
    }

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

  function updateSource(patch: Partial<AdminKuaishouIndustrySourceConfig>) {
    onChange({
      ...config,
      source: {
        ...source,
        ...patch,
      },
    })
  }

  function updateShop(index: number, patch: Partial<AdminKuaishouIndustryShopConfig>) {
    updateSource({
      shops: shops.map((shop, shopIndex) => (shopIndex === index ? { ...shop, ...patch } : shop)),
    })
  }

  function updateShopSellerId(index: number, sellerId: string) {
    const shop = shops[index]
    if (!shop) {
      return
    }

    updateShop(index, {
      sellerId,
      shopId: !shop.shopId || shop.shopId === shop.sellerId ? sellerId : shop.shopId,
      authState: !shop.authState || shop.authState === shop.sellerId ? sellerId : shop.authState,
    })
  }

  function addShop() {
    updateSource({
      shops: [...shops, createEmptyIndustryShopConfig()],
    })
  }

  function deleteShop(index: number) {
    updateSource({
      shops: shops.filter((_, shopIndex) => shopIndex !== index),
    })
  }

  function renderSecretInput(
    label: string,
    field: 'appSecret' | 'signSecret' | 'messageSecret',
    masked: string,
  ) {
    return (
      <div>
        <Typography.Text type="secondary">{label}</Typography.Text>
        <Input.Password
          value={source[field]}
          placeholder={masked || '未配置'}
          onChange={(event) => updateSource({ [field]: event.target.value })}
        />
      </div>
    )
  }

  const shopColumns: TableColumnsType<AdminKuaishouIndustryShopConfig> = [
    {
      title: '启用',
      width: 76,
      fixed: 'left',
      render: (_, row, index) => (
        <Switch
          checked={row.enabled !== false}
          onChange={(enabled) => updateShop(index, { enabled })}
        />
      ),
    },
    {
      title: '店铺',
      minWidth: 260,
      render: (_, row, index) => (
        <div className="cell-stack">
          <Input
            value={row.sellerId}
            placeholder="sellerId"
            onChange={(event) => updateShopSellerId(index, event.target.value)}
          />
          <Input
            value={row.shopName}
            placeholder="官方店铺名称"
            onChange={(event) => updateShop(index, { shopName: event.target.value })}
          />
          <Input
            value={row.customShopName}
            placeholder="自定义店铺名"
            onChange={(event) => updateShop(index, { customShopName: event.target.value })}
          />
        </div>
      ),
    },
    {
      title: 'Token 状态',
      width: 150,
      render: (_, row) => {
        const status = resolveIndustryAccessTokenStatus(row)
        return (
          <div className="status-stack">
            <Tag color={status.color}>{status.label}</Tag>
            {row.accessTokenMasked ? (
              <Typography.Text type="secondary">{row.accessTokenMasked}</Typography.Text>
            ) : null}
          </div>
        )
      },
    },
    {
      title: '过期时间',
      minWidth: 220,
      render: (_, row) => (
        <div className="cell-stack">
          <span>
            {row.accessTokenExpiresAt ? formatAdminDateTime(row.accessTokenExpiresAt) : '-'}
          </span>
          <span className="muted">
            {formatIndustryTokenCountdown(row.accessTokenExpiresInSeconds)}
          </span>
        </div>
      ),
    },
    {
      title: '授权操作',
      width: 420,
      render: (_, row, index) => {
        const actionKey = buildIndustryShopActionKey(index)
        return (
          <Space direction="vertical" className="full-width" size={8}>
            <Space.Compact className="full-width">
              <Input
                value={authorizationCodes[actionKey] || ''}
                placeholder="授权 code"
                onChange={(event) =>
                  setAuthorizationCodes((current) => ({
                    ...current,
                    [actionKey]: event.target.value,
                  }))
                }
                onPressEnter={() => exchangeAuthorizationCode(row, index)}
              />
              <Button
                type="primary"
                loading={actionLoading === `exchange-${index}`}
                disabled={saving || Boolean(actionLoading && actionLoading !== `exchange-${index}`)}
                onClick={() => exchangeAuthorizationCode(row, index)}
              >
                换取
              </Button>
            </Space.Compact>
            <Space wrap>
              <Button
                size="small"
                icon={<CopyOutlined />}
                onClick={() => copyAuthorizationUrl(row)}
              >
                复制授权
              </Button>
              <Button
                size="small"
                icon={<LinkOutlined />}
                onClick={() => openAuthorizationUrl(row)}
              >
                打开授权
              </Button>
              <Button
                size="small"
                icon={<ReloadOutlined />}
                loading={actionLoading === `refresh-${index}`}
                disabled={saving || Boolean(actionLoading && actionLoading !== `refresh-${index}`)}
                onClick={() => refreshToken(row, index)}
              >
                刷新
              </Button>
              <Button
                danger
                size="small"
                icon={<DeleteOutlined />}
                disabled={saving || Boolean(actionLoading)}
                onClick={() => deleteShop(index)}
              >
                删除
              </Button>
            </Space>
          </Space>
        )
      },
    },
  ]

  return (
    <section className="platform-panel-stack">
      {source.lastRefreshError ? (
        <Alert
          type="warning"
          showIcon
          message="最近一次刷新失败"
          description={source.lastRefreshError}
        />
      ) : null}

      <div className="metric-grid four">
        <MetricCard
          label="接入状态"
          value={source.enabled ? '已启用' : '停用'}
          detail={source.enabled ? '配置启用' : '配置停用'}
        />
        <MetricCard
          label="店铺授权"
          value={`${enabledShopCount}/${shops.length}`}
          detail="启用店铺 / 全部店铺"
        />
        <MetricCard
          label="有效 token"
          value={String(validTokenCount)}
          detail={problemTokenCount > 0 ? `${problemTokenCount} 个需处理` : '全部正常'}
        />
        <MetricCard
          label="授权范围"
          value={source.scopes || '-'}
          detail={source.redirectUri || '-'}
        />
      </div>

      <Card
        title="应用参数"
        extra={
          <Button
            type="primary"
            icon={<SaveOutlined />}
            loading={saving}
            disabled={Boolean(actionLoading)}
            onClick={saveConfig}
          >
            保存配置
          </Button>
        }
      >
        <div className="platform-form-grid">
          <FieldSwitch
            label="启用配置"
            checked={source.enabled}
            onChange={(enabled) => updateSource({ enabled })}
          />
          <LabeledInput
            label="开放平台 API"
            value={source.baseUrl}
            onChange={(baseUrl) => updateSource({ baseUrl })}
          />
          <LabeledInput
            label="授权页地址"
            value={source.authBaseUrl}
            onChange={(authBaseUrl) => updateSource({ authBaseUrl })}
          />
          <LabeledInput
            label="appKey"
            value={source.appKey}
            onChange={(appKey) => updateSource({ appKey })}
          />
          <LabeledInput
            label="回调地址"
            value={source.redirectUri}
            onChange={(redirectUri) => updateSource({ redirectUri })}
          />
          <LabeledInput
            label="scope"
            value={source.scopes}
            onChange={(scopes) => updateSource({ scopes })}
          />
          <LabeledInput
            label="全局 state 兜底"
            value={source.authState}
            onChange={(authState) => updateSource({ authState })}
          />
          <LabeledInput
            label="版本"
            value={source.version}
            onChange={(version) => updateSource({ version })}
          />
          {renderSecretInput('appSecret', 'appSecret', source.appSecretMasked)}
          {renderSecretInput('signSecret', 'signSecret', source.signSecretMasked)}
          {renderSecretInput('messageSecret', 'messageSecret', source.messageSecretMasked)}
        </div>
      </Card>

      <Card
        title="店铺授权"
        extra={
          <Space wrap>
            <Button icon={<PlusOutlined />} onClick={addShop}>
              新增店铺
            </Button>
            <Button
              type="primary"
              icon={<SaveOutlined />}
              loading={saving}
              disabled={Boolean(actionLoading)}
              onClick={saveConfig}
            >
              保存店铺
            </Button>
          </Space>
        }
      >
        {shops.length === 0 ? (
          <Empty description="暂无店铺授权" />
        ) : (
          <Table<AdminKuaishouIndustryShopConfig>
            rowKey={(row, index) => buildIndustryShopRowKey(row, index || 0)}
            columns={shopColumns}
            dataSource={shops}
            pagination={false}
            scroll={{ x: 1120 }}
            expandable={{
              expandedRowRender: (row, index) => {
                const authorizationUrl = buildIndustryAuthorizationUrl(source, row)
                return (
                  <div className="platform-form-grid">
                    <LabeledInput
                      label="shopId"
                      value={row.shopId}
                      onChange={(shopId) => updateShop(index, { shopId })}
                    />
                    <LabeledInput
                      label="官方店铺名称"
                      value={row.shopName}
                      onChange={(shopName) => updateShop(index, { shopName })}
                    />
                    <LabeledInput
                      label="自定义店铺名"
                      value={row.customShopName}
                      onChange={(customShopName) => updateShop(index, { customShopName })}
                    />
                    <LabeledInput
                      label="state"
                      value={row.authState}
                      onChange={(authState) => updateShop(index, { authState })}
                    />
                    <div>
                      <Typography.Text type="secondary">授权链接</Typography.Text>
                      <Input readOnly value={authorizationUrl} />
                    </div>
                    <LabeledInput
                      label="openId"
                      value={row.openId}
                      onChange={(openId) => updateShop(index, { openId })}
                    />
                    <LabeledInput
                      label="已授权 scope"
                      value={row.grantedScopes}
                      onChange={(grantedScopes) => updateShop(index, { grantedScopes })}
                    />
                    <div>
                      <Typography.Text type="secondary">accessToken</Typography.Text>
                      <Input.Password
                        value={row.accessToken}
                        placeholder={row.accessTokenMasked || '留空保持原值'}
                        onChange={(event) => updateShop(index, { accessToken: event.target.value })}
                      />
                    </div>
                    <div>
                      <Typography.Text type="secondary">refreshToken</Typography.Text>
                      <Input.Password
                        value={row.refreshToken}
                        placeholder={row.refreshTokenMasked || '留空保持原值'}
                        onChange={(event) =>
                          updateShop(index, { refreshToken: event.target.value })
                        }
                      />
                    </div>
                    <LabeledInput
                      label="accessToken 过期时间"
                      value={row.accessTokenExpiresAt}
                      onChange={(accessTokenExpiresAt) =>
                        updateShop(index, { accessTokenExpiresAt })
                      }
                    />
                    <LabeledInput
                      label="refreshToken 过期时间"
                      value={row.refreshTokenExpiresAt}
                      onChange={(refreshTokenExpiresAt) =>
                        updateShop(index, { refreshTokenExpiresAt })
                      }
                    />
                    <div>
                      <Typography.Text type="secondary">最近错误</Typography.Text>
                      <Input.TextArea
                        autoSize={{ minRows: 1, maxRows: 4 }}
                        value={row.lastRefreshError}
                        onChange={(event) =>
                          updateShop(index, { lastRefreshError: event.target.value })
                        }
                      />
                    </div>
                  </div>
                )
              },
            }}
          />
        )}
      </Card>
    </section>
  )
}

function KuaishouFeifeiPlatformPanel({
  config,
  onChange,
}: {
  config: AdminKuaishouFeifeiConfigResponse
  onChange: (config: AdminKuaishouFeifeiConfigResponse) => void
}) {
  const [saving, setSaving] = useState(false)
  const [syncing, setSyncing] = useState(false)
  const [matching, setMatching] = useState(false)
  const [matchInput, setMatchInput] = useState('')
  const [matchResult, setMatchResult] = useState<AdminKuaishouFeifeiMatchResult | null>(null)
  const source = config.source
  const enabledRules = source.productRules.filter((rule) => rule.enabled !== false)

  async function saveConfig() {
    setSaving(true)
    try {
      const response = await saveAdminKuaishouFeifeiConfig(source)
      onChange(response.data)
      showSuccess('kuaishou-feifei 平台配置已保存')
    } catch (error) {
      showError(error instanceof Error ? error.message : '保存 kuaishou-feifei 配置失败')
    } finally {
      setSaving(false)
    }
  }

  async function syncProducts() {
    setSyncing(true)
    try {
      const response = await syncAdminKuaishouFeifeiProducts({ status: 'on_sale' })
      onChange(response.data)
      showSuccess(
        `商品映射已同步：商品 ${response.data.sync.productCount} 个，规则 ${response.data.sync.ruleCount} 条`,
      )
    } catch (error) {
      showError(error instanceof Error ? error.message : '同步 kuaishou-feifei 商品映射失败')
    } finally {
      setSyncing(false)
    }
  }

  async function previewMatch() {
    if (!matchInput.trim()) {
      showError('请输入 91 商品名')
      return
    }

    setMatching(true)
    try {
      const response = await matchAdminKuaishouFeifeiProduct(matchInput.trim())
      setMatchResult(response.data)
    } catch (error) {
      showError(error instanceof Error ? error.message : '预览映射失败')
    } finally {
      setMatching(false)
    }
  }

  function updateSource(patch: Partial<AdminKuaishouFeifeiConfig>) {
    onChange({ ...config, source: { ...source, ...patch } })
  }

  function updateRule(index: number, patch: Partial<AdminKuaishouFeifeiProductRule>) {
    updateSource({
      productRules: source.productRules.map((rule, ruleIndex) =>
        ruleIndex === index ? { ...rule, ...patch } : rule,
      ),
    })
  }

  return (
    <section className="platform-panel-stack">
      <div className="metric-grid three">
        <MetricCard
          label="运行状态"
          value={config.effective.enabled ? '已启用' : '停用'}
          detail={source.enabled ? '源配置启用' : '源配置停用'}
        />
        <MetricCard
          label="API 凭据"
          value={config.effective.hasAppKey && config.effective.hasAppSecret ? '已配置' : '待补全'}
        />
        <MetricCard
          label="启用规则"
          value={String(enabledRules.length)}
          detail={`共 ${source.productRules.length} 条`}
        />
      </div>

      <Card
        title="API 配置"
        extra={
          <Space wrap>
            <Button icon={<ReloadOutlined />} loading={syncing} onClick={syncProducts}>
              同步商品映射
            </Button>
            <Button type="primary" icon={<SaveOutlined />} loading={saving} onClick={saveConfig}>
              保存配置
            </Button>
          </Space>
        }
      >
        <div className="platform-form-grid">
          <FieldSwitch
            label="启用"
            checked={source.enabled}
            onChange={(enabled) => updateSource({ enabled })}
          />
          <div>
            <Typography.Text type="secondary">接口地址</Typography.Text>
            <Input
              value={source.baseUrl}
              onChange={(event) => updateSource({ baseUrl: event.target.value })}
            />
          </div>
          <div>
            <Typography.Text type="secondary">App Key</Typography.Text>
            <Input
              value={source.appKey}
              onChange={(event) => updateSource({ appKey: event.target.value })}
            />
          </div>
          <div>
            <Typography.Text type="secondary">App Secret</Typography.Text>
            <Input.Password
              value={source.appSecret}
              onChange={(event) => updateSource({ appSecret: event.target.value })}
            />
          </div>
          <NumberField
            label="超时毫秒"
            value={source.timeoutMs}
            min={1000}
            onChange={(timeoutMs) => updateSource({ timeoutMs })}
          />
          <div>
            <Typography.Text type="secondary">通知地址</Typography.Text>
            <Input
              value={source.notifyUrl}
              onChange={(event) => updateSource({ notifyUrl: event.target.value })}
            />
          </div>
        </div>
      </Card>

      <Card title="商品映射">
        <Space.Compact className="full-width">
          <Input
            value={matchInput}
            placeholder="91 商品名"
            onChange={(event) => setMatchInput(event.target.value)}
            onPressEnter={previewMatch}
          />
          <Button icon={<SearchOutlined />} loading={matching} onClick={previewMatch}>
            预览命中
          </Button>
        </Space.Compact>
        {matchResult ? (
          <Alert
            className="platform-section-gap"
            type={matchResult.matched ? 'success' : 'warning'}
            showIcon
            message={matchResult.matched ? '已命中' : '未命中'}
            description={
              matchResult.match
                ? `${matchResult.match.productName} -> ${matchResult.match.productCode}`
                : matchResult.normalizedProductName
            }
          />
        ) : null}

        <Table<AdminKuaishouFeifeiProductRule>
          className="platform-section-gap"
          rowKey={(row) => row.id || row.productCode || row.productName}
          dataSource={source.productRules}
          pagination={buildAdminLocalTablePagination()}
          scroll={{ x: 980 }}
          columns={[
            {
              title: '启用',
              width: 80,
              render: (_, row, index) => (
                <Switch
                  checked={row.enabled !== false}
                  onChange={(enabled) => updateRule(index, { enabled })}
                />
              ),
            },
            {
              title: '商品名',
              minWidth: 260,
              render: (_, row, index) => (
                <Input
                  value={row.productName}
                  onChange={(event) => updateRule(index, { productName: event.target.value })}
                />
              ),
            },
            {
              title: 'product_code',
              minWidth: 160,
              render: (_, row, index) => (
                <Input
                  value={row.productCode}
                  onChange={(event) => updateRule(index, { productCode: event.target.value })}
                />
              ),
            },
            {
              title: '展示名',
              minWidth: 220,
              render: (_, row, index) => (
                <Input
                  value={row.skuName}
                  onChange={(event) => updateRule(index, { skuName: event.target.value })}
                />
              ),
            },
            {
              title: '备注',
              minWidth: 180,
              render: (_, row, index) => (
                <Input
                  value={row.notes}
                  onChange={(event) => updateRule(index, { notes: event.target.value })}
                />
              ),
            },
          ]}
        />
      </Card>
    </section>
  )
}

function CloudtentaclesPlatformPanel({
  config,
  onChange,
}: {
  config: AdminCloudtentaclesSourceConfigResponse
  onChange: (config: AdminCloudtentaclesSourceConfigResponse) => void
}) {
  const [saving, setSaving] = useState(false)
  const [debugLoading, setDebugLoading] = useState('')
  const [selectedSourceKey, setSelectedSourceKey] = useState(config.sources[0]?.key || '')
  const [smsCode, setSmsCode] = useState('')
  const [debugResult, setDebugResult] = useState<unknown>(null)
  const [vnItems, setVnItems] = useState<AdminCloudtentaclesVnListItem[] | null>(null)
  const [vnLoading, setVnLoading] = useState(false)
  const [vnBackingId, setVnBackingId] = useState<number | null>(null)

  useEffect(() => {
    if (!config.sources.some((source) => source.key === selectedSourceKey)) {
      setSelectedSourceKey(config.sources[0]?.key || '')
    }
  }, [config.sources, selectedSourceKey])

  const selectedSource = config.sources.find((source) => source.key === selectedSourceKey) || null
  const selectedSession = selectedSource ? config.sessions[selectedSource.key] : null

  async function saveConfig() {
    setSaving(true)
    try {
      const response = await saveAdminCloudtentaclesSourceConfig({
        enabled: config.enabled,
        sources: config.sources,
      })
      onChange(response.data)
      const nextKey =
        selectedSourceKey && response.data.sources.some((item) => item.key === selectedSourceKey)
          ? selectedSourceKey
          : response.data.sources[0]?.key || ''
      setSelectedSourceKey(nextKey)
      showSuccess('kuaishou-lewan 账号配置已保存')
    } catch (error) {
      showError(error instanceof Error ? error.message : '保存 kuaishou-lewan 账号失败')
    } finally {
      setSaving(false)
    }
  }

  async function deleteSource(sourceKey: string) {
    setDebugLoading(`delete-${sourceKey}`)
    try {
      await deleteAdminCloudtentaclesSource(sourceKey)
      const response = await fetchAdminCloudtentaclesSourceConfig()
      onChange(response.data)
      const nextKey =
        selectedSourceKey === sourceKey ? response.data.sources[0]?.key || '' : selectedSourceKey
      setSelectedSourceKey(
        response.data.sources.some((item) => item.key === nextKey)
          ? nextKey
          : response.data.sources[0]?.key || '',
      )
      showSuccess('kuaishou-lewan 账号已删除')
    } catch (error) {
      showError(error instanceof Error ? error.message : '删除 kuaishou-lewan 账号失败')
    } finally {
      setDebugLoading('')
    }
  }

  async function runDebug(action: string, handler: () => Promise<unknown>) {
    if (!selectedSource) {
      showError('请先选择一个账号')
      return
    }
    setDebugLoading(action)
    setDebugResult(null)
    try {
      const result = await handler()
      setDebugResult(result)
      showSuccess(`账号 ${formatCloudtentaclesSourceLabel(selectedSource)} 调试完成`)
    } catch (error) {
      showError(error instanceof Error ? error.message : 'kuaishou-lewan 调试失败')
    } finally {
      setDebugLoading('')
    }
  }

  function updateConfig(next: Partial<AdminCloudtentaclesSourceConfigResponse>) {
    onChange({ ...config, ...next })
  }

  async function loadVnList() {
    if (!selectedSource) {
      showError('请先选择一个账号')
      return
    }
    setVnLoading(true)
    try {
      const response = await fetchAdminCloudtentaclesVnList({
        sourceKey: selectedSource.key,
        key: '1',
      })
      setVnItems(response.data.items || [])
    } catch (error) {
      showError(error instanceof Error ? error.message : '查询虚拟号列表失败')
    } finally {
      setVnLoading(false)
    }
  }

  async function backVn(item: AdminCloudtentaclesVnListItem) {
    if (!selectedSource) {
      return
    }
    setVnBackingId(item.id)
    try {
      await backAdminCloudtentaclesVn({
        sourceKey: selectedSource.key,
        key: '1',
        id: item.id,
      })
      showSuccess(`号码 ${item.phone} 已退回`)
      await loadVnList()
    } catch (error) {
      showError(error instanceof Error ? error.message : '退回号码失败')
    } finally {
      setVnBackingId(null)
    }
  }

  function updateSource(sourceKey: string, patch: Partial<AdminCloudtentaclesSourceItem>) {
    updateConfig({
      sources: config.sources.map((source) =>
        source.key === sourceKey ? { ...source, ...patch } : source,
      ),
    })
    if (patch.key && patch.key !== sourceKey && selectedSourceKey === sourceKey) {
      setSelectedSourceKey(String(patch.key))
    }
  }

  function addSource() {
    const key = `account_${Date.now().toString(36)}`
    updateConfig({
      sources: [
        ...config.sources,
        {
          key,
          label: '新履约账号',
          enabled: true,
          baseUrl: 'https://123.207.217.176',
          username: '',
          password: '',
          phone: '',
          deviceId: '1',
          deviceType: 2,
        },
      ],
    })
    setSelectedSourceKey(key)
  }

  return (
    <section className="platform-panel-stack">
      <Card
        title="kuaishou-lewan 履约账号"
        extra={
          <Space wrap>
            <Switch
              checked={config.enabled}
              checkedChildren="启用"
              unCheckedChildren="停用"
              onChange={(enabled) => updateConfig({ enabled })}
            />
            <Button icon={<PlusOutlined />} onClick={addSource}>
              新增账号
            </Button>
            <Button type="primary" icon={<SaveOutlined />} loading={saving} onClick={saveConfig}>
              保存账号
            </Button>
          </Space>
        }
      >
        {config.sources.length === 0 ? (
          <Empty description="暂无 kuaishou-lewan 账号" />
        ) : (
          <div className="platform-split-layout">
            <Card title="账号列表" size="small" className="platform-side-card">
              <Space direction="vertical" className="full-width" size={8}>
                {config.sources.map((source) => {
                  const session = config.sessions[source.key]
                  const active = source.key === selectedSourceKey
                  return (
                    <button
                      key={source.key}
                      type="button"
                      className={active ? 'platform-select-card active' : 'platform-select-card'}
                      onClick={() => setSelectedSourceKey(source.key)}
                    >
                      <span>
                        <strong>{formatCloudtentaclesSourceLabel(source)}</strong>
                        <small>{source.username || source.key}</small>
                      </span>
                      <Tag
                        color={
                          session?.hasToken
                            ? 'green'
                            : source.enabled === false
                              ? 'default'
                              : 'orange'
                        }
                      >
                        {session?.hasToken
                          ? '已登录'
                          : source.enabled === false
                            ? '已停用'
                            : '未登录'}
                      </Tag>
                    </button>
                  )
                })}
              </Space>
            </Card>

            <Card
              size="small"
              title={
                selectedSource
                  ? `编辑账号：${formatCloudtentaclesSourceLabel(selectedSource)}`
                  : '编辑账号'
              }
              extra={
                selectedSource ? (
                  <Button
                    danger
                    icon={<DeleteOutlined />}
                    loading={debugLoading === `delete-${selectedSource.key}`}
                    onClick={() => deleteSource(selectedSource.key)}
                  >
                    删除
                  </Button>
                ) : null
              }
            >
              {!selectedSource ? (
                <Empty description="请选择左侧账号" />
              ) : (
                <>
                  <div className="platform-form-grid">
                    <FieldSwitch
                      label="启用账号"
                      checked={selectedSource.enabled !== false}
                      onChange={(enabled) => updateSource(selectedSource.key, { enabled })}
                    />
                    <LabeledInput
                      label="账号标识"
                      value={selectedSource.key}
                      onChange={(value) => updateSource(selectedSource.key, { key: value })}
                    />
                    <LabeledInput
                      label="显示名称"
                      value={selectedSource.label}
                      onChange={(value) => updateSource(selectedSource.key, { label: value })}
                    />
                    <LabeledInput
                      label="接口地址"
                      value={selectedSource.baseUrl}
                      onChange={(value) => updateSource(selectedSource.key, { baseUrl: value })}
                    />
                    <LabeledInput
                      label="用户名"
                      value={selectedSource.username}
                      onChange={(value) => updateSource(selectedSource.key, { username: value })}
                    />
                    <LabeledInput
                      label="密码"
                      password
                      value={selectedSource.password}
                      onChange={(value) => updateSource(selectedSource.key, { password: value })}
                    />
                    <LabeledInput
                      label="手机号"
                      value={selectedSource.phone}
                      onChange={(value) => updateSource(selectedSource.key, { phone: value })}
                    />
                    <LabeledInput
                      label="设备 ID"
                      value={selectedSource.deviceId}
                      onChange={(value) => updateSource(selectedSource.key, { deviceId: value })}
                    />
                    <NumberField
                      label="设备类型"
                      value={selectedSource.deviceType}
                      min={0}
                      onChange={(deviceType) => updateSource(selectedSource.key, { deviceType })}
                    />
                  </div>
                  <Alert
                    className="platform-section-gap"
                    type="info"
                    showIcon
                    message={`当前调试账号：${formatCloudtentaclesSourceLabel(selectedSource)}（${selectedSource.key}）`}
                    description="下方登录 / 查余额 / 查商品都只作用于当前选中账号。"
                  />
                </>
              )}
            </Card>
          </div>
        )}
      </Card>

      <Card title="账号验证与调试">
        {!selectedSource ? (
          <Empty description="请先选择一个账号" />
        ) : (
          <>
            <Alert
              type={selectedSession?.hasToken ? 'success' : 'info'}
              showIcon
              message={
                selectedSession?.hasToken
                  ? `${formatCloudtentaclesSourceLabel(selectedSource)} 已有持久化会话`
                  : `${formatCloudtentaclesSourceLabel(selectedSource)} 还没有可用会话`
              }
              description={`sourceKey：${selectedSource.key} · 登录时间：${formatAdminDateTime(selectedSession?.loggedInAt || '')}`}
            />
            <Space wrap className="platform-section-gap">
              <Input
                value={smsCode}
                placeholder="短信验证码"
                style={{ width: 180 }}
                onChange={(event) => setSmsCode(event.target.value)}
              />
              <Button
                icon={<SendOutlined />}
                loading={debugLoading === 'sms'}
                onClick={() =>
                  runDebug('sms', async () => {
                    const response = await sendAdminCloudtentaclesSmsCode({
                      sourceKey: selectedSource.key,
                      baseUrl: selectedSource.baseUrl,
                      username: selectedSource.username,
                      phone: selectedSource.phone,
                      deviceId: selectedSource.deviceId,
                      deviceType: selectedSource.deviceType,
                    })
                    return response.data
                  })
                }
              >
                发送短信
              </Button>
              <Button
                icon={<CheckCircleOutlined />}
                loading={debugLoading === 'login'}
                onClick={() =>
                  runDebug('login', async () => {
                    const response = await testAdminCloudtentaclesLogin({
                      sourceKey: selectedSource.key,
                      baseUrl: selectedSource.baseUrl,
                      username: selectedSource.username,
                      password: selectedSource.password,
                      phone: selectedSource.phone,
                      code: smsCode,
                      deviceId: selectedSource.deviceId,
                      deviceType: selectedSource.deviceType,
                    })
                    const nextConfig = await fetchAdminCloudtentaclesSourceConfig()
                    onChange({
                      ...nextConfig.data,
                      sources: [
                        ...nextConfig.data.sources,
                        ...config.sources.filter(
                          (source) =>
                            !nextConfig.data.sources.some((item) => item.key === source.key),
                        ),
                      ],
                    })
                    return response.data
                  })
                }
              >
                登录验证
              </Button>
              <Button
                loading={debugLoading === 'validate'}
                onClick={() =>
                  runDebug('validate', async () => {
                    const response = await validateAdminCloudtentaclesSession({
                      sourceKey: selectedSource.key,
                      baseUrl: selectedSource.baseUrl,
                      token: selectedSession?.token,
                      deviceId: selectedSource.deviceId,
                      deviceType: selectedSource.deviceType,
                    })
                    return response.data
                  })
                }
              >
                校验会话
              </Button>
              <Button
                loading={debugLoading === 'asset'}
                onClick={() =>
                  runDebug('asset', async () => {
                    const response = await fetchAdminCloudtentaclesAsset({
                      sourceKey: selectedSource.key,
                      baseUrl: selectedSource.baseUrl,
                      token: selectedSession?.token,
                      deviceId: selectedSource.deviceId,
                      deviceType: selectedSource.deviceType,
                    })
                    return response.data
                  })
                }
              >
                查余额
              </Button>
              <Button
                loading={debugLoading === 'sku'}
                onClick={() =>
                  runDebug('sku', async () => {
                    const response = await fetchAdminCloudtentaclesSkuList({
                      sourceKey: selectedSource.key,
                      baseUrl: selectedSource.baseUrl,
                      token: selectedSession?.token,
                      deviceId: selectedSource.deviceId,
                      deviceType: selectedSource.deviceType,
                    })
                    return response.data
                  })
                }
              >
                查商品
              </Button>
              <Button
                icon={<SearchOutlined />}
                loading={debugLoading === 'vnList' || vnLoading}
                onClick={() => void loadVnList()}
              >
                查看号码列表
              </Button>
            </Space>
            {debugResult ? (
              <pre className="json-preview">{JSON.stringify(debugResult, null, 2)}</pre>
            ) : null}
            {vnItems !== null ? (
              <Table<AdminCloudtentaclesVnListItem>
                className="platform-section-gap"
                size="small"
                rowKey="id"
                loading={vnLoading}
                dataSource={vnItems}
                pagination={{ pageSize: 10, showSizeChanger: false }}
                columns={[
                  {
                    title: '手机号',
                    dataIndex: 'phone',
                    render: (phone: string) => phone || '-',
                  },
                  {
                    title: '绑定角色',
                    dataIndex: 'bindInfo',
                    render: (bindInfo: AdminCloudtentaclesVnListItem['bindInfo']) => {
                      const role = resolveVnBindRole(bindInfo)
                      return role ? (
                        <span>
                          <Tag color="blue">{role.name || '-'}</Tag>
                          <Typography.Text type="secondary" style={{ fontSize: 12 }}>
                            {role.rid || ''}
                          </Typography.Text>
                        </span>
                      ) : (
                        <Typography.Text type="secondary">未绑定</Typography.Text>
                      )
                    },
                  },
                  {
                    title: '回收倒计时',
                    dataIndex: 'countTime',
                    render: (countTime: number) => (
                      <Typography.Text
                        type={
                          Number(countTime) < 300
                            ? 'danger'
                            : Number(countTime) < 900
                              ? 'warning'
                              : undefined
                        }
                      >
                        {formatCountdownSeconds(Number(countTime))}
                      </Typography.Text>
                    ),
                  },
                  {
                    title: '状态',
                    dataIndex: 'status',
                    render: (status: number) =>
                      Number(status) === 0 ? <Tag>空闲</Tag> : <Tag color="orange">占用中</Tag>,
                  },
                  {
                    title: '操作',
                    width: 120,
                    render: (_, item) => (
                      <Popconfirm
                        title={`确认退回号码 ${item.phone}？`}
                        okText="确认退回"
                        cancelText="取消"
                        onConfirm={() => void backVn(item)}
                      >
                        <Button danger size="small" loading={vnBackingId === item.id}>
                          退号
                        </Button>
                      </Popconfirm>
                    ),
                  },
                ]}
              />
            ) : null}
          </>
        )}
      </Card>
    </section>
  )
}

function formatCloudtentaclesSourceLabel(
  source: Pick<AdminCloudtentaclesSourceItem, 'label' | 'username' | 'key'>,
) {
  return String(source.label || source.username || source.key || '').trim() || source.key
}

function resolveVnBindRole(bindInfo: AdminCloudtentaclesVnListItem['bindInfo']) {
  if (typeof bindInfo === 'object' && bindInfo !== null) {
    return {
      name: String((bindInfo as Record<string, unknown>).name || '').trim(),
      rid: String((bindInfo as Record<string, unknown>).rid || '').trim(),
    }
  }
  if (typeof bindInfo === 'string' && bindInfo.trim()) {
    try {
      const parsed = JSON.parse(bindInfo) as Record<string, unknown>
      if (parsed && typeof parsed === 'object') {
        return {
          name: String(parsed.name || '').trim(),
          rid: String(parsed.rid || '').trim(),
        }
      }
    } catch {
      return null
    }
  }
  return null
}

function formatCountdownSeconds(totalSeconds: number) {
  const seconds = Math.max(0, Number(totalSeconds) || 0)
  const minutes = Math.floor(seconds / 60)
  const restSeconds = seconds % 60
  return `${String(minutes).padStart(2, '0')}:${String(restSeconds).padStart(2, '0')}`
}

function resolveIndustryAccessTokenStatus(
  source: Pick<AdminKuaishouIndustrySourceConfig, 'accessTokenStatus'>,
) {
  switch (source.accessTokenStatus) {
    case 'valid':
      return { label: '有效', color: 'green' }
    case 'expiring':
      return { label: '即将过期', color: 'orange' }
    case 'expired':
      return { label: '已过期', color: 'red' }
    case 'unknown':
      return { label: '未知', color: 'blue' }
    case 'missing':
    default:
      return { label: '未配置', color: 'default' }
  }
}

function formatIndustryTokenCountdown(value: number | null) {
  if (value === null || value === undefined) {
    return '未记录过期时间'
  }

  if (value <= 0) {
    return '已过期'
  }

  const days = Math.floor(value / 86_400)
  const hours = Math.floor((value % 86_400) / 3_600)
  const minutes = Math.floor((value % 3_600) / 60)
  if (days > 0) {
    return `${days} 天 ${hours} 小时后过期`
  }
  if (hours > 0) {
    return `${hours} 小时 ${minutes} 分钟后过期`
  }
  return `${minutes} 分钟后过期`
}

function buildIndustryAuthorizationUrl(
  source: AdminKuaishouIndustrySourceConfig,
  shop?: AdminKuaishouIndustryShopConfig,
) {
  if (!source.authBaseUrl || !source.appKey || !source.redirectUri || !source.scopes) {
    return shop?.authorizationUrl || source.authorizationUrl || ''
  }

  try {
    const url = new URL(`${source.authBaseUrl.replace(/\/+$/, '')}/oauth/authorize`)
    url.searchParams.set('app_id', source.appKey)
    url.searchParams.set('redirect_uri', source.redirectUri)
    url.searchParams.set('scope', normalizeIndustryScopeText(source.scopes))
    url.searchParams.set('response_type', 'code')
    const state = String(shop?.authState || source.authState || shop?.sellerId || '').trim()
    if (state) {
      url.searchParams.set('state', state)
    }

    return url.toString()
  } catch {
    return shop?.authorizationUrl || source.authorizationUrl || ''
  }
}

function createEmptyIndustryShopConfig(): AdminKuaishouIndustryShopConfig {
  return {
    enabled: true,
    sellerId: '',
    shopId: '',
    shopName: '',
    customShopName: '',
    authState: '',
    authorizationUrl: '',
    accessToken: '',
    accessTokenMasked: '',
    hasAccessToken: false,
    refreshToken: '',
    refreshTokenMasked: '',
    hasRefreshToken: false,
    accessTokenExpiresAt: '',
    refreshTokenExpiresAt: '',
    accessTokenStatus: 'missing',
    accessTokenExpiresInSeconds: null,
    openId: '',
    grantedScopes: '',
    lastRefreshedAt: '',
    lastRefreshError: '',
  }
}

function buildIndustryShopRowKey(shop: AdminKuaishouIndustryShopConfig, index: number) {
  return shop.sellerId || shop.shopId || `shop-${index}`
}

function buildIndustryShopActionKey(index: number) {
  return `shop-${index}`
}

function resolveIndustryShopDisplayName(shop: AdminKuaishouIndustryShopConfig) {
  return shop.customShopName || shop.shopName || shop.sellerId || '未命名店铺'
}

function normalizeIndustryScopeText(value: string) {
  return value
    .split(/[,\s]+/)
    .map((item) => item.trim())
    .filter(Boolean)
    .join(',')
}

function MetricCard({ label, value, detail }: { label: string; value: string; detail?: string }) {
  return (
    <Card className="metric-card">
      <span>{label}</span>
      <strong>{value}</strong>
      {detail ? <small>{detail}</small> : null}
    </Card>
  )
}

function FieldSwitch({
  label,
  checked,
  onChange,
}: {
  label: string
  checked: boolean
  onChange: (checked: boolean) => void
}) {
  return (
    <div className="field-switch">
      <span>{label}</span>
      <Switch checked={checked} onChange={onChange} />
    </div>
  )
}

function NumberField({
  label,
  value,
  min,
  onChange,
}: {
  label: string
  value: number
  min?: number
  onChange: (value: number) => void
}) {
  return (
    <div>
      <Typography.Text type="secondary">{label}</Typography.Text>
      <InputNumber
        className="full-width"
        value={value}
        min={min}
        onChange={(nextValue) => onChange(Number(nextValue || 0))}
      />
    </div>
  )
}

function LabeledInput({
  label,
  value,
  password,
  onChange,
}: {
  label: string
  value: string
  password?: boolean
  onChange: (value: string) => void
}) {
  const InputComponent = password ? Input.Password : Input
  return (
    <div>
      <Typography.Text type="secondary">{label}</Typography.Text>
      <InputComponent value={value} onChange={(event) => onChange(event.target.value)} />
    </div>
  )
}

function normalizePlatformTab(value: unknown): PlatformTab {
  return typeof value === 'string' && platformTabs.includes(value as PlatformTab)
    ? (value as PlatformTab)
    : 'notifications'
}

type SkuMappingRow = { productNo: string; sku: string }

function AffiliateDashPlatformPanel() {
  const [loading, setLoading] = useState(true)
  const [saving, setSaving] = useState(false)
  const [errorMessage, setErrorMessage] = useState('')
  const [enabled, setEnabled] = useState(true)
  const [preferredMatchEnabled, setPreferredMatchEnabled] = useState(true)
  const [baseUrl, setBaseUrl] = useState('')
  const [appKey, setAppKey] = useState('')
  const [appSecret, setAppSecret] = useState('')
  const [callbackSecret, setCallbackSecret] = useState('')
  const [timeoutMs, setTimeoutMs] = useState(10000)
  const [notifyUrl, setNotifyUrl] = useState('')
  const [timestampToleranceSeconds, setTimestampToleranceSeconds] = useState(300)
  const [skuRows, setSkuRows] = useState<SkuMappingRow[]>([])
  const [matchInput, setMatchInput] = useState('')
  const [matchResult, setMatchResult] = useState<AdminAffiliateDashMatchResult | null>(null)
  const [wallet, setWallet] = useState<AdminAffiliateDashWallet | null>(null)
  const [products, setProducts] = useState<AdminAffiliateDashProductItem[]>([])
  const [productTotal, setProductTotal] = useState(0)
  const [productPage, setProductPage] = useState(1)
  const [productsLoading, setProductsLoading] = useState(false)

  const skuMapping = skuRows.reduce<Record<string, string>>((acc, row) => {
    if (row.productNo.trim() && row.sku.trim()) {
      acc[row.productNo.trim()] = row.sku.trim()
    }
    return acc
  }, {})

  useEffect(() => {
    void loadConfig()
    void loadWallet()
    void loadProducts(1)
  }, [])

  async function loadConfig() {
    setLoading(true)
    setErrorMessage('')

    try {
      const response = await fetchAdminAffiliateDashConfig()
      hydrateConfig(response.data)
    } catch (error) {
      setErrorMessage(error instanceof Error ? error.message : '读取 affiliate-dash 配置失败')
    } finally {
      setLoading(false)
    }
  }

  function hydrateConfig(response: AdminAffiliateDashConfigResponse) {
    const source = response.source
    setEnabled(source.enabled !== false)
    setPreferredMatchEnabled(source.preferredMatchEnabled !== false)
    setBaseUrl(source.baseUrl || '')
    setAppKey(source.appKey || '')
    setAppSecret(source.appSecret || '')
    setCallbackSecret(source.callbackSecret || '')
    setTimeoutMs(Number(source.timeoutMs || 10000))
    setNotifyUrl(source.notifyUrl || '')
    setTimestampToleranceSeconds(Number(source.timestampToleranceSeconds || 300))
    setSkuRows(
      Object.entries(source.skuMapping || {}).map(([productNo, sku]) => ({
        productNo,
        sku: String(sku || ''),
      })),
    )
  }

  async function saveConfig() {
    setSaving(true)
    setErrorMessage('')

    try {
      const payload: AdminAffiliateDashConfig = {
        enabled,
        preferredMatchEnabled,
        baseUrl,
        appKey,
        appSecret,
        callbackSecret,
        timeoutMs,
        notifyUrl,
        timestampToleranceSeconds,
        skuMapping,
      }
      const response = await saveAdminAffiliateDashConfig(payload)
      hydrateConfig(response.data)
      showSuccess('affiliate-dash 配置已保存')
    } catch (error) {
      const message = error instanceof Error ? error.message : '保存 affiliate-dash 配置失败'
      setErrorMessage(message)
      showError(message)
    } finally {
      setSaving(false)
    }
  }

  async function previewMatch() {
    const productNo = matchInput.trim()
    if (!productNo) {
      showError('请输入 91 商品编码（productNo）')
      return
    }

    setMatchResult(null)

    try {
      const response = await matchAdminAffiliateDashSku(productNo)
      setMatchResult(response.data)
    } catch (error) {
      showError(error instanceof Error ? error.message : '预览 affiliate-dash sku 映射失败')
    }
  }

  async function loadWallet() {
    try {
      const response = await fetchAdminAffiliateDashWallet()
      setWallet(response.data)
    } catch (error) {
      showError(error instanceof Error ? error.message : '查询 affiliate-dash 钱包失败')
    }
  }

  async function loadProducts(page: number) {
    setProductsLoading(true)

    try {
      const response = await fetchAdminAffiliateDashProducts({ page, size: 20 })
      setProducts(response.data.list)
      setProductTotal(response.data.total)
      setProductPage(response.data.page)
    } catch (error) {
      showError(error instanceof Error ? error.message : '拉取 affiliate-dash 商品失败')
    } finally {
      setProductsLoading(false)
    }
  }

  function updateSkuRow(index: number, patch: Partial<SkuMappingRow>) {
    setSkuRows((rows) => rows.map((row, i) => (i === index ? { ...row, ...patch } : row)))
  }

  const productColumns: TableColumnsType<AdminAffiliateDashProductItem> = [
    { title: 'sku', dataIndex: 'sku', key: 'sku', width: 180 },
    { title: '名称', dataIndex: 'displayName', key: 'displayName' },
    { title: '单价', dataIndex: 'priceAmount', key: 'priceAmount', width: 90 },
    {
      title: '库存',
      dataIndex: 'stock',
      key: 'stock',
      width: 70,
      render: (v: number) => (v === -1 ? '不限' : v),
    },
    {
      title: '状态',
      dataIndex: 'status',
      key: 'status',
      width: 80,
      render: (v: string) => <Tag color={v === 'active' ? 'green' : 'red'}>{v}</Tag>,
    },
  ]

  return (
    <Card
      title="affiliate-dash 发货平台"
      extra={
        <Space>
          <Button icon={<ReloadOutlined />} onClick={loadWallet}>
            刷新钱包
          </Button>
          <Button icon={<ReloadOutlined />} onClick={() => loadProducts(productPage)}>
            刷新商品
          </Button>
          <Button type="primary" icon={<SaveOutlined />} loading={saving} onClick={saveConfig}>
            保存配置
          </Button>
        </Space>
      }
    >
      {errorMessage ? (
        <Alert type="error" message={errorMessage} showIcon style={{ marginBottom: 16 }} />
      ) : null}

      <Spin spinning={loading}>
        <Space direction="vertical" size="large" style={{ width: '100%' }}>
          <Card size="small" title="对接配置">
            <Space direction="vertical" style={{ width: '100%' }} size="middle">
              <Space>
                <Typography.Text>启用</Typography.Text>
                <Switch checked={enabled} onChange={setEnabled} />
                <Typography.Text type="secondary">映射命中优先</Typography.Text>
                <Switch checked={preferredMatchEnabled} onChange={setPreferredMatchEnabled} />
              </Space>
              <Space wrap>
                <span>BASE_URL</span>
                <Input
                  style={{ width: 320 }}
                  value={baseUrl}
                  onChange={(e) => setBaseUrl(e.target.value)}
                  placeholder="https://skin.khhao.com"
                />
              </Space>
              <Space wrap>
                <span>App Key</span>
                <Input
                  style={{ width: 320 }}
                  value={appKey}
                  onChange={(e) => setAppKey(e.target.value)}
                  placeholder="ak_..."
                />
              </Space>
              <Space wrap>
                <span>App Secret</span>
                <Input.Password
                  style={{ width: 320 }}
                  value={appSecret}
                  onChange={(e) => setAppSecret(e.target.value)}
                  placeholder="sk_..."
                />
              </Space>
              <Space wrap>
                <span>回调 Secret</span>
                <Input.Password
                  style={{ width: 320 }}
                  value={callbackSecret}
                  onChange={(e) => setCallbackSecret(e.target.value)}
                  placeholder="cb_..."
                />
              </Space>
              <Space wrap>
                <span>超时(ms)</span>
                <InputNumber
                  min={1000}
                  value={timeoutMs}
                  onChange={(v) => setTimeoutMs(Number(v || 10000))}
                />
                <span>时间容差(s)</span>
                <InputNumber
                  min={1}
                  value={timestampToleranceSeconds}
                  onChange={(v) => setTimestampToleranceSeconds(Number(v || 300))}
                />
              </Space>
              <Space wrap>
                <span>回调 URL(展示)</span>
                <Typography.Text type="secondary">{notifyUrl || '未配置'}</Typography.Text>
              </Space>
              {wallet ? (
                <Space wrap>
                  <span>钱包余额</span>
                  <Typography.Text strong>
                    {wallet.availableBalance} {wallet.currency}
                  </Typography.Text>
                  <span>(冻结 {wallet.frozenBalance})</span>
                </Space>
              ) : null}
            </Space>
          </Card>

          <Card size="small" title="91 商品编码 → affiliate_dash sku 映射">
            <Space direction="vertical" style={{ width: '100%' }} size="middle">
              {skuRows.map((row, index) => (
                <Space key={`${index}_${row.productNo}`} wrap>
                  <Input
                    style={{ width: 240 }}
                    placeholder="91 商品编码(productNo)"
                    value={row.productNo}
                    onChange={(e) => updateSkuRow(index, { productNo: e.target.value })}
                  />
                  <Input
                    style={{ width: 240 }}
                    placeholder="affiliate_dash sku"
                    value={row.sku}
                    onChange={(e) => updateSkuRow(index, { sku: e.target.value })}
                  />
                  <Button
                    icon={<DeleteOutlined />}
                    onClick={() => setSkuRows((rows) => rows.filter((_, i) => i !== index))}
                  />
                </Space>
              ))}
              <Button
                icon={<PlusOutlined />}
                onClick={() => setSkuRows((rows) => [...rows, { productNo: '', sku: '' }])}
              >
                新增映射
              </Button>
            </Space>
          </Card>

          <Card size="small" title="映射测试与商品目录">
            <Space direction="vertical" style={{ width: '100%' }} size="middle">
              <Space wrap>
                <Input
                  style={{ width: 240 }}
                  placeholder="输入 91 商品编码测试"
                  value={matchInput}
                  onChange={(e) => setMatchInput(e.target.value)}
                />
                <Button icon={<SearchOutlined />} onClick={previewMatch}>
                  测试映射
                </Button>
                {matchResult ? (
                  <Typography.Text type={matchResult.matched ? 'success' : 'warning'}>
                    {matchResult.matched
                      ? `命中 sku: ${matchResult.sku}`
                      : `未命中（productNo=${matchResult.productNo || '-'}）`}
                  </Typography.Text>
                ) : null}
              </Space>
              <Table<AdminAffiliateDashProductItem>
                rowKey="sku"
                size="small"
                loading={productsLoading}
                columns={productColumns}
                dataSource={products}
                pagination={buildAdminLocalTablePagination({
                  current: productPage,
                  pageSize: 20,
                  total: productTotal,
                  onChange: (page) => {
                    void loadProducts(page)
                  },
                })}
              />
            </Space>
          </Card>
        </Space>
      </Spin>
    </Card>
  )
}
