<template>
  <div class="payment-orders">
    <header class="page-heading">
      <div>
        <h2>支付订单</h2>
        <p>核对收款、发货与退款状态，处理需要人工介入的订单。</p>
      </div>
        <el-button :icon="Refresh" :loading="loading" @click="load()">刷新</el-button>
    </header>

    <section class="status-summary" aria-label="当前页订单概览">
      <div v-for="item in pageSummary" :key="item.label" class="summary-item">
        <span>{{ item.label }}</span>
        <strong>{{ item.value }}</strong>
        <small>{{ item.hint }}</small>
      </div>
    </section>

    <div class="kra-search-box filter-band">
      <el-form :model="searchInfo" label-position="top" @submit.prevent="reload">
        <div class="filter-grid">
          <el-form-item label="支付渠道">
            <el-select v-model="searchInfo.provider" clearable filterable placeholder="全部渠道">
              <el-option v-for="item in providerOptions" :key="item.value" :label="item.label" :value="item.value" />
            </el-select>
          </el-form-item>
          <el-form-item label="商户订单号">
            <el-input v-model="searchInfo.tradeNo" clearable placeholder="支持模糊查询" />
          </el-form-item>
          <el-form-item label="业务类型">
            <el-input v-model="searchInfo.businessType" clearable placeholder="精确匹配" />
          </el-form-item>
          <el-form-item label="业务 ID">
            <el-input v-model="searchInfo.businessId" clearable placeholder="支持模糊查询" />
          </el-form-item>
          <el-form-item label="支付状态">
            <el-select v-model="searchInfo.paymentStatus" clearable placeholder="全部状态">
              <el-option v-for="item in paymentStatusOptions" :key="item.value" :label="item.label" :value="item.value" />
            </el-select>
          </el-form-item>
          <el-form-item label="退款状态">
            <el-select v-model="searchInfo.refundStatus" clearable placeholder="全部状态">
              <el-option v-for="item in refundStatusOptions" :key="item.value" :label="item.label" :value="item.value" />
            </el-select>
          </el-form-item>
          <div class="filter-actions">
            <el-button type="primary" :icon="Search" @click="reload">查询</el-button>
            <el-button :icon="Refresh" @click="reset">重置</el-button>
          </div>
        </div>
      </el-form>
    </div>

    <div class="kra-table-box order-table-band">
      <div class="table-heading">
        <div><strong>订单明细</strong><span>共 {{ total }} 笔</span></div>
        <span v-if="issueCount" class="issue-count">{{ issueCount }} 笔需要关注</span>
      </div>

      <el-table v-loading="loading" :data="rows" row-key="ID" stripe :row-class-name="rowClassName">
        <el-table-column label="订单" min-width="250">
          <template #default="scope">
            <div class="order-cell">
              <button type="button" class="order-link" @click="openDetail(scope.row)">{{ scope.row.tradeNo || '-' }}</button>
              <span>{{ scope.row.subject || '未提供商品标题' }}</span>
            </div>
          </template>
        </el-table-column>
        <el-table-column label="业务" min-width="165">
          <template #default="scope">
            <div class="business-cell"><span>{{ scope.row.businessType || '-' }}</span><small>{{ scope.row.businessId || '-' }}</small></div>
          </template>
        </el-table-column>
        <el-table-column label="渠道" width="135">
          <template #default="scope">
            <div class="provider-cell"><span>{{ providerText(scope.row.provider) }}</span><small>{{ scope.row.provider || '-' }}</small></div>
          </template>
        </el-table-column>
        <el-table-column label="金额" width="145" align="right">
          <template #default="scope">
            <div class="amount-cell">
              <strong>{{ formatAmount(scope.row.amount, scope.row.currency) }}</strong>
              <small v-if="scope.row.paidAmount">实付 {{ formatAmount(scope.row.paidAmount, scope.row.currency) }}</small>
            </div>
          </template>
        </el-table-column>
        <el-table-column label="支付" width="115" align="center">
          <template #default="scope"><el-tag :type="paymentStatusType(scope.row.paymentStatus)">{{ paymentStatusText(scope.row.paymentStatus) }}</el-tag></template>
        </el-table-column>
        <el-table-column label="发货" width="115" align="center">
          <template #default="scope"><el-tag :type="fulfillmentStatusType(scope.row.fulfillmentStatus)" effect="plain">{{ fulfillmentStatusText(scope.row.fulfillmentStatus, scope.row) }}</el-tag></template>
        </el-table-column>
        <el-table-column label="退款" width="120" align="center">
          <template #default="scope"><el-tag :type="refundStatusType(scope.row.refundStatus)" effect="plain">{{ refundStatusText(scope.row.refundStatus) }}</el-tag></template>
        </el-table-column>
        <el-table-column label="创建时间" width="170">
          <template #default="scope">{{ formatDateValue(scope.row.createdAt) }}</template>
        </el-table-column>
        <el-table-column label="操作" :fixed="operationFixed" width="310">
          <template #default="scope">
            <div class="row-actions">
              <el-button link type="primary" :icon="View" :disabled="isRowBusy(scope.row)" @click="openDetail(scope.row)">详情</el-button>
              <el-button v-if="canSync(scope.row)" link type="primary" :icon="RefreshRight" :loading="rowAction(scope.row) === 'sync'" :disabled="isRowBusy(scope.row) && rowAction(scope.row) !== 'sync'" @click="syncOrder(scope.row)">同步</el-button>
              <el-button v-if="canRefund(scope.row)" link type="warning" :icon="Money" :disabled="isRowBusy(scope.row)" @click="openRefund(scope.row)">退款</el-button>
              <el-button v-if="canFulfill(scope.row)" link type="success" :icon="Promotion" :loading="rowAction(scope.row) === 'fulfill'" :disabled="isRowBusy(scope.row) && rowAction(scope.row) !== 'fulfill'" @click="retryFulfillment(scope.row)">{{ fulfillmentActionText(scope.row) }}</el-button>
            </div>
          </template>
        </el-table-column>
        <template #empty><el-empty description="没有符合条件的支付订单" /></template>
      </el-table>

      <div class="kra-pagination">
        <el-pagination :current-page="page" :page-size="pageSize" :page-sizes="[10, 30, 50, 100]" :total="total" :layout="paginationLayout" @current-change="changePage" @size-change="changeSize" />
      </div>
    </div>

    <el-drawer v-model="detailVisible" title="支付订单详情" :size="drawerSize" destroy-on-close>
      <div v-loading="detailLoading" class="order-detail">
        <template v-if="detail">
          <div class="detail-toolbar">
            <div class="detail-statuses">
              <el-tag :type="paymentStatusType(detail.paymentStatus)">{{ paymentStatusText(detail.paymentStatus) }}</el-tag>
              <el-tag :type="fulfillmentStatusType(detail.fulfillmentStatus)" effect="plain">{{ fulfillmentStatusText(detail.fulfillmentStatus, detail) }}</el-tag>
              <el-tag :type="refundStatusType(detail.refundStatus)" effect="plain">{{ refundStatusText(detail.refundStatus) }}</el-tag>
            </div>
            <div class="detail-actions">
              <el-button v-if="canSync(detail)" :icon="RefreshRight" :loading="rowAction(detail) === 'sync'" :disabled="isRowBusy(detail) && rowAction(detail) !== 'sync'" @click="syncOrder(detail)">同步状态</el-button>
              <el-button v-if="canFulfill(detail)" type="success" plain :icon="Promotion" :loading="rowAction(detail) === 'fulfill'" :disabled="isRowBusy(detail) && rowAction(detail) !== 'fulfill'" @click="retryFulfillment(detail)">{{ fulfillmentActionText(detail) }}</el-button>
              <el-button v-if="canRefund(detail)" type="warning" plain :icon="Money" :disabled="isRowBusy(detail)" @click="openRefund(detail)">申请退款</el-button>
            </div>
          </div>

          <el-alert v-if="detail.lastError" type="error" :closable="false" show-icon class="detail-error" :title="detail.lastError" />

          <h3 class="section-heading">订单信息</h3>
          <el-descriptions :column="detailColumns" border>
            <el-descriptions-item label="本地记录 ID">{{ detail.ID || '-' }}</el-descriptions-item>
            <el-descriptions-item label="支付方式">{{ paymentModeText(detail.paymentMode) }}</el-descriptions-item>
            <el-descriptions-item label="商户订单号" :span="detailColumns">
              <span class="copy-value"><span>{{ detail.tradeNo || '-' }}</span><el-button v-if="detail.tradeNo" link :icon="CopyDocument" aria-label="复制商户订单号" @click="copyText(detail.tradeNo)" /></span>
            </el-descriptions-item>
            <el-descriptions-item label="第三方交易号" :span="detailColumns">
              <span class="copy-value"><span>{{ detail.providerTradeNo || '-' }}</span><el-button v-if="detail.providerTradeNo" link :icon="CopyDocument" aria-label="复制第三方交易号" @click="copyText(detail.providerTradeNo)" /></span>
            </el-descriptions-item>
            <el-descriptions-item label="支付渠道">{{ providerText(detail.provider) }}（{{ detail.provider || '-' }}）</el-descriptions-item>
            <el-descriptions-item label="平台状态">{{ detail.providerStatus || '-' }}</el-descriptions-item>
            <el-descriptions-item label="业务类型">{{ detail.businessType || '-' }}</el-descriptions-item>
            <el-descriptions-item label="业务 ID">{{ detail.businessId || '-' }}</el-descriptions-item>
            <el-descriptions-item label="商品标题" :span="detailColumns">{{ detail.subject || '-' }}</el-descriptions-item>
          </el-descriptions>

          <h3 class="section-heading">金额明细</h3>
          <div class="amount-grid">
            <div v-for="item in detailAmounts" :key="item.label" class="amount-item"><span>{{ item.label }}</span><strong>{{ item.value }}</strong></div>
          </div>
          <el-alert v-if="!detail.amountBreakdownKnown && detail.paymentStatus === 'paid'" type="info" :closable="false" class="amount-alert" title="支付渠道未返回完整的实付、优惠与结算拆分。" />

          <h3 class="section-heading">处理时间</h3>
          <el-timeline class="order-timeline">
            <el-timeline-item v-for="item in detailTimeline" :key="item.label" :timestamp="item.time" :type="item.type">{{ item.label }}</el-timeline-item>
          </el-timeline>
        </template>
      </div>
    </el-drawer>

        <el-dialog v-model="refundVisible" title="申请退款" width="min(460px, calc(100vw - 32px))" destroy-on-close :close-on-click-modal="!refundSubmitting" :close-on-press-escape="!refundSubmitting">
      <div class="refund-summary">
        <span>商户订单号</span><strong>{{ refundForm.tradeNo || '-' }}</strong>
        <span>可退金额</span><strong>{{ formatAmount(refundForm.maxAmount, refundForm.currency) }}</strong>
      </div>
      <el-form ref="refundFormRef" :model="refundForm" :rules="refundRules" label-position="top" @submit.prevent="submitRefund">
        <el-form-item label="退款金额" prop="amountText">
          <el-input v-model="refundForm.amountText" inputmode="decimal" autocomplete="off" :placeholder="refundAmountPlaceholder">
            <template #prepend>{{ refundForm.currency || 'CNY' }}</template>
          </el-input>
          <span class="form-tip">{{ refundPrecisionText }}</span>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button :disabled="refundSubmitting" @click="refundVisible = false">取消</el-button>
        <el-button type="warning" :icon="Money" :loading="refundSubmitting" @click="submitRefund">确认退款</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { computed, nextTick, onMounted, reactive, ref } from 'vue'
import { useWindowSize } from '@vueuse/core'
import { ElMessage, ElMessageBox } from 'element-plus'
import { CopyDocument, Money, Promotion, Refresh, RefreshRight, Search, View } from '@element-plus/icons-vue'
import { fulfillPaymentOrder, getPaymentOrder, getPaymentOrders, queryPaymentOrder, refundPaymentOrder } from '@/api/payment'
import { formatDate } from '@/utils/format'

defineOptions({ name: 'PaymentOrders' })

const PAYMENT_STATUS_META = {
  initialized: { label: '初始化', type: 'info' }, pending: { label: '待支付', type: 'warning' },
  paid: { label: '已支付', type: 'success' }, failed: { label: '支付失败', type: 'danger' },
  closed: { label: '已关闭', type: 'info' }, partially_refunded: { label: '部分退款', type: 'warning' },
  refunded: { label: '已退款', type: 'info' }
}
const FULFILLMENT_STATUS_META = {
  pending: { label: '待发货', type: 'info' }, processing: { label: '发货中', type: 'warning' },
  succeeded: { label: '已发货', type: 'success' }, failed: { label: '发货失败', type: 'danger' }
}
const REFUND_STATUS_META = {
  none: { label: '未退款', type: 'info' }, processing: { label: '请求处理中', type: 'warning' },
  pending: { label: '渠道处理中', type: 'warning' }, partial: { label: '部分退款', type: 'warning' },
  succeeded: { label: '已退款', type: 'success' }, failed: { label: '退款失败', type: 'danger' }
}
const providerOptions = [
  ['alipay', '支付宝'], ['alipay-v3', '支付宝 V3'], ['wechat-v2', '微信支付 V2'], ['wechat-v3', '微信支付 V3'],
  ['apple-iap', 'Apple IAP'], ['douyin', '抖音支付'], ['qq', 'QQ 钱包'], ['allinpay', '通联支付'],
  ['lakala', '拉卡拉'], ['paypal', 'PayPal'], ['saobei', '扫呗'], ['chinaums', '银联商务'], ['sft', '商福通'],
  ['supper-pay', 'Supper Pay'], ['wechat-game-pay', '微信小游戏支付'], ['douyin-game-pay', '抖音小游戏支付'], ['internal', '内部支付']
].map(([value, label]) => ({ value, label }))
const providerNames = Object.fromEntries(providerOptions.map((item) => [item.value, item.label]))
const paymentStatusOptions = Object.entries(PAYMENT_STATUS_META).map(([value, item]) => ({ value, label: item.label }))
const refundStatusOptions = Object.entries(REFUND_STATUS_META).map(([value, item]) => ({ value, label: item.label }))
const ZERO_DECIMAL_CURRENCIES = new Set(['BIF', 'CLP', 'DJF', 'GNF', 'ISK', 'JPY', 'KMF', 'KRW', 'PYG', 'RWF', 'UGX', 'UYI', 'VND', 'VUV', 'XAF', 'XOF', 'XPF'])
const THREE_DECIMAL_CURRENCIES = new Set(['BHD', 'IQD', 'JOD', 'KWD', 'LYD', 'OMR', 'TND'])
const FOUR_DECIMAL_CURRENCIES = new Set(['CLF', 'UYW'])
const MAX_SAFE_MINOR_AMOUNT = Number.MAX_SAFE_INTEGER

const { width } = useWindowSize()
const page = ref(1)
const pageSize = ref(10)
const total = ref(0)
const rows = ref([])
const loading = ref(false)
const detail = ref(null)
const detailVisible = ref(false)
const detailLoading = ref(false)
const refundVisible = ref(false)
const refundLoading = ref(false)
const refundSubmitting = ref(false)
const refundFormRef = ref()
const rowActions = reactive({})
const searchInfo = reactive({ provider: '', tradeNo: '', businessType: '', businessId: '', paymentStatus: '', refundStatus: '' })
const refundForm = reactive({ provider: '', tradeNo: '', currency: 'CNY', maxAmount: 0, amountText: '' })
let loadRequestID = 0
let detailRequestID = 0

const issueCount = computed(() => rows.value.filter(isIssueOrder).length)
const pageSummary = computed(() => [
  { label: '本页订单', value: rows.value.length, hint: `全部 ${total.value} 笔` },
  { label: '待支付', value: rows.value.filter((row) => ['initialized', 'pending'].includes(row.paymentStatus)).length, hint: '尚未确认收款' },
  { label: '已确认收款', value: rows.value.filter((row) => ['paid', 'partially_refunded', 'refunded'].includes(row.paymentStatus)).length, hint: '含部分或全额退款' },
  { label: '需要关注', value: issueCount.value, hint: '支付、发货或退款异常' }
])
const paginationLayout = computed(() => width.value < 720 ? 'total, prev, pager, next' : 'total, sizes, prev, pager, next, jumper')
const operationFixed = computed(() => width.value >= 1180 ? 'right' : false)
const drawerSize = computed(() => width.value < 720 ? '96%' : '720px')
const detailColumns = computed(() => width.value < 720 ? 1 : 2)
const refundPrecisionText = computed(() => currencyMinorDigits(refundForm.currency) === 0 ? '该币种仅支持整数金额' : `最多支持 ${currencyMinorDigits(refundForm.currency)} 位小数`)
const refundAmountPlaceholder = computed(() => currencyMinorDigits(refundForm.currency) === 0 ? '例如 100' : '例如 100.00')
const detailAmounts = computed(() => {
  if (!detail.value) return []
  const order = detail.value
  const currency = order.currency
  const items = [
    { label: '原始金额', value: formatAmount(order.originalAmount || order.amount, currency) },
    { label: '应付金额', value: formatAmount(order.amount, currency) },
    { label: '已付金额', value: formatAmount(order.paidAmount, currency) },
    { label: '已退款', value: formatAmount(order.refundedAmount, currency) }
  ]
  if (order.refundRequestedAmount) items.push({ label: '退款处理中', value: formatAmount(order.refundRequestedAmount, currency) })
  if (order.amountBreakdownKnown) items.push(
    { label: '付款人实付', value: formatAmount(order.payerPaidAmount, order.payerCurrency || currency) },
    { label: '现金支付', value: formatAmount(order.cashPaidAmount, order.payerCurrency || currency) },
    { label: '积分支付', value: formatAmount(order.pointPaidAmount, order.payerCurrency || currency) },
    { label: '优惠合计', value: formatAmount(order.discountAmount, currency) },
    { label: '渠道优惠', value: formatAmount(order.providerDiscountAmount, currency) },
    { label: '商户优惠', value: formatAmount(order.merchantDiscountAmount, currency) },
    { label: '结算金额', value: formatAmount(order.settlementAmount, currency) }
  )
  return items
})
const detailTimeline = computed(() => {
  if (!detail.value) return []
  return [
    { label: '订单创建', value: detail.value.createdAt, type: 'primary' },
    { label: '支付确认', value: detail.value.paidAt, type: 'success' },
    { label: '业务发货', value: detail.value.fulfilledAt, type: 'success' },
    { label: '退款确认', value: detail.value.refundedAt, type: 'warning' },
    { label: '最后更新', value: detail.value.updatedAt, type: 'info' }
  ].filter((item) => item.value).map((item) => ({ ...item, time: formatDateValue(item.value) }))
})
const refundRules = { amountText: [{ required: true, message: '请输入退款金额', trigger: 'blur' }, { validator: validateRefundAmount, trigger: ['blur', 'change'] }] }

function currencyMinorDigits(currency) {
  const code = String(currency || 'CNY').trim().toUpperCase()
  if (ZERO_DECIMAL_CURRENCIES.has(code)) return 0
  if (THREE_DECIMAL_CURRENCIES.has(code)) return 3
  if (FOUR_DECIMAL_CURRENCIES.has(code)) return 4
  return 2
}
function toIntegerAmount(value) {
  const numeric = Number(value)
  if (!Number.isFinite(numeric)) return 0
  return Math.trunc(numeric)
}
function formatAmount(value, currency = 'CNY') {
  const code = String(currency || 'CNY').trim().toUpperCase()
  const digits = currencyMinorDigits(code)
  const scale = 10 ** digits
  const amount = toIntegerAmount(value)
  const absolute = Math.abs(amount)
  const whole = Math.floor(absolute / scale).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
  const fraction = digits > 0 ? `.${String(absolute % scale).padStart(digits, '0')}` : ''
  return `${amount < 0 ? '-' : ''}${whole}${fraction} ${code}`
}
function formatAmountInput(value, currency) {
  const digits = currencyMinorDigits(currency)
  const scale = 10 ** digits
  const amount = toIntegerAmount(value)
  const whole = Math.floor(amount / scale)
  return digits === 0 ? String(whole) : `${whole}.${String(amount % scale).padStart(digits, '0')}`
}
function parseAmountToMinor(value, currency) {
  const text = String(value || '').trim()
  const digits = currencyMinorDigits(currency)
  const pattern = digits === 0 ? /^\d+$/ : new RegExp(`^\\d+(?:\\.\\d{1,${digits}})?$`)
  if (!pattern.test(text)) throw new Error(digits === 0 ? '请输入整数金额' : `金额最多保留 ${digits} 位小数`)
  const [whole, fraction = ''] = text.split('.')
  const wholeAmount = Number(whole)
  const fractionAmount = Number(fraction.padEnd(digits, '0') || '0')
  const minor = wholeAmount * (10 ** digits) + fractionAmount
  if (!Number.isSafeInteger(minor) || minor > MAX_SAFE_MINOR_AMOUNT) throw new Error('退款金额超出前端可安全处理范围')
  if (minor <= 0) throw new Error('退款金额必须大于 0')
  return minor
}
function validateRefundAmount(_rule, value, callback) {
  try {
    const amount = parseAmountToMinor(value, refundForm.currency)
    if (amount > refundForm.maxAmount) return callback(new Error(`退款金额不能超过 ${formatAmount(refundForm.maxAmount, refundForm.currency)}`))
    callback()
  } catch (error) { callback(error) }
}
function providerText(provider) { return providerNames[provider] || provider || '-' }
function paymentStatusText(status) { return PAYMENT_STATUS_META[status]?.label || status || '-' }
function paymentStatusType(status) { return PAYMENT_STATUS_META[status]?.type || 'info' }
function fulfillmentStatusText(status, order) {
  if (status === 'pending' && order?.paymentStatus !== 'paid') return '未触发'
  return FULFILLMENT_STATUS_META[status]?.label || status || '-'
}
function fulfillmentStatusType(status) { return FULFILLMENT_STATUS_META[status]?.type || 'info' }
function refundStatusText(status) { return REFUND_STATUS_META[status]?.label || status || '-' }
function refundStatusType(status) { return REFUND_STATUS_META[status]?.type || 'info' }
function paymentModeText(mode) { return mode === 'internal' ? '内部支付' : mode === 'external' ? '外部渠道' : mode || '-' }
function formatDateValue(value) { return value ? formatDate(value) || '-' : '-' }
function remainingRefundAmount(order) { return Math.max(0, Number(order?.amount || 0) - Number(order?.refundedAmount || 0)) }
function canRefund(order) { return ['paid', 'partially_refunded'].includes(order?.paymentStatus) && ['none', 'partial', 'failed'].includes(order?.refundStatus) && remainingRefundAmount(order) > 0 }
function canFulfill(order) { return order?.paymentStatus === 'paid' && ['pending', 'processing', 'failed'].includes(order?.fulfillmentStatus) }
function canSync(order) { return ['initialized', 'pending', 'paid', 'partially_refunded', 'refunded', 'failed'].includes(order?.paymentStatus) }
function fulfillmentActionText(order) { return ['processing', 'failed'].includes(order?.fulfillmentStatus) ? '重试发货' : '执行发货' }
function isIssueOrder(order) { return order?.paymentStatus === 'failed' || order?.fulfillmentStatus === 'failed' || order?.refundStatus === 'failed' || Boolean(order?.lastError) }
function rowClassName({ row }) { return isIssueOrder(row) ? 'is-payment-issue' : '' }
function actionKey(order) { return `${order?.provider || ''}\u0000${order?.tradeNo || ''}` }
function rowAction(order) { return rowActions[actionKey(order)] || '' }
function isRowBusy(order) { return Boolean(rowAction(order)) }
function setRowAction(order, action) { const key = actionKey(order); if (action) rowActions[key] = action; else delete rowActions[key] }
function requestFilters() {
  return Object.fromEntries(Object.entries(searchInfo).filter(([, value]) => typeof value === 'string' && value.trim()).map(([key, value]) => [key, value.trim()]))
}

async function load(options = {}) {
  const requestID = ++loadRequestID
  if (!options.silent) loading.value = true
  try {
    const res = await getPaymentOrders({ page: page.value, pageSize: pageSize.value, ...requestFilters() })
    if (requestID !== loadRequestID || res.code !== 0) return
    rows.value = res.data?.list || []
    total.value = Number(res.data?.total || 0)
    page.value = Number(res.data?.page || page.value)
    pageSize.value = Number(res.data?.pageSize || pageSize.value)
  } catch {
    // The request layer already presents transport errors.
  } finally {
    if (requestID === loadRequestID) loading.value = false
  }
}
function reload() { page.value = 1; load() }
function reset() { Object.assign(searchInfo, { provider: '', tradeNo: '', businessType: '', businessId: '', paymentStatus: '', refundStatus: '' }); reload() }
function changePage(value) { page.value = value; load() }
function changeSize(value) { pageSize.value = value; page.value = 1; load() }
async function loadDetail(order) {
  if (!order?.provider || !order?.tradeNo) return
  const requestID = ++detailRequestID
  const provider = order.provider
  const tradeNo = order.tradeNo
  detailLoading.value = true
  try {
    const res = await getPaymentOrder({ provider, tradeNo })
    if (requestID === detailRequestID && detail.value?.provider === provider && detail.value?.tradeNo === tradeNo && res.code === 0 && res.data) detail.value = res.data
  } catch {
    // The request layer already presents transport errors.
  } finally {
    if (requestID === detailRequestID) detailLoading.value = false
  }
}
function openDetail(order) { detail.value = { ...order }; detailVisible.value = true; loadDetail(order) }
async function refreshVisibleDetail(order) {
  if (detailVisible.value && detail.value?.provider === order.provider && detail.value?.tradeNo === order.tradeNo) await loadDetail(order)
}
async function syncOrder(order) {
  if (isRowBusy(order)) return
  setRowAction(order, 'sync')
  try {
    const res = await queryPaymentOrder({ provider: order.provider, tradeNo: order.tradeNo })
    if (res.code !== 0) return
    ElMessage.success(res.data?.orderStatus ? `订单状态已同步：${paymentStatusText(res.data.orderStatus)}` : '订单状态已同步')
    await load({ silent: true })
    await refreshVisibleDetail(order)
  } catch {
    // The request layer already presents transport errors.
  } finally { setRowAction(order, '') }
}
async function retryFulfillment(order) {
  if (isRowBusy(order)) return
  try {
    await ElMessageBox.confirm(`确认${fulfillmentActionText(order)}订单 ${order.tradeNo} 吗？`, fulfillmentActionText(order), { type: 'warning', confirmButtonText: '确认执行' })
  } catch { return }
  setRowAction(order, 'fulfill')
  try {
    const res = await fulfillPaymentOrder({ provider: order.provider, tradeNo: order.tradeNo })
    if (res.code !== 0) return
    ElMessage.success(res.data?.duplicate ? '该订单已完成发货，无需重复处理' : '发货处理已完成')
    await load({ silent: true })
    await refreshVisibleDetail(order)
  } catch {
    // The request layer already presents transport errors.
  } finally { setRowAction(order, '') }
}
function openRefund(order) {
  const maxAmount = remainingRefundAmount(order)
  Object.assign(refundForm, { provider: order.provider, tradeNo: order.tradeNo, currency: order.currency || 'CNY', maxAmount, amountText: formatAmountInput(maxAmount, order.currency || 'CNY') })
  refundVisible.value = true
  nextTick(() => refundFormRef.value?.clearValidate())
}
async function submitRefund() {
  if (refundSubmitting.value) return
  refundSubmitting.value = true
  const valid = await refundFormRef.value?.validate().catch(() => false)
  if (!valid) { refundSubmitting.value = false; return }
  let amount
  try { amount = parseAmountToMinor(refundForm.amountText, refundForm.currency) }
  catch (error) { ElMessage.warning(error.message); refundSubmitting.value = false; return }
  try {
    await ElMessageBox.confirm(`确认退款 ${formatAmount(amount, refundForm.currency)} 吗？提交后将调用支付渠道。`, '确认退款', { type: 'warning', confirmButtonText: '确认退款' })
  } catch { refundSubmitting.value = false; return }
  refundLoading.value = true
  const order = { provider: refundForm.provider, tradeNo: refundForm.tradeNo }
  try {
    const res = await refundPaymentOrder({ provider: refundForm.provider, tradeNo: refundForm.tradeNo, amount })
    if (res.code !== 0) return
    ElMessage.success(res.data?.refundStatus === 'succeeded' ? '退款已完成' : '退款申请已提交，等待渠道确认')
    refundVisible.value = false
    await load({ silent: true })
    await refreshVisibleDetail(order)
  } catch {
    // The request layer already presents transport errors.
  } finally { refundLoading.value = false; refundSubmitting.value = false }
}
async function copyText(value) {
  try { await navigator.clipboard.writeText(String(value)); ElMessage.success('已复制') }
  catch { ElMessage.warning('复制失败，请手动选择文本') }
}

onMounted(load)
</script>

<style scoped>
.payment-orders { min-width: 0; padding-bottom: 24px; }
.page-heading { display: flex; align-items: center; justify-content: space-between; gap: 18px; margin-bottom: 16px; }
.page-heading h2 { margin: 0; color: var(--el-text-color-primary); font-size: 20px; font-weight: 600; letter-spacing: 0; }
.page-heading p { margin: 5px 0 0; color: var(--el-text-color-secondary); font-size: 13px; }
.status-summary { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px; overflow: hidden; margin-bottom: 16px; border: 1px solid var(--el-border-color-lighter); border-radius: 6px; background: var(--el-border-color-lighter); }
.summary-item { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 3px 10px; min-width: 0; padding: 14px 16px; background: var(--el-bg-color); }
.summary-item span { color: var(--el-text-color-regular); font-size: 13px; }
.summary-item strong { grid-row: span 2; color: var(--el-text-color-primary); font-size: 24px; font-weight: 600; line-height: 1; }
.summary-item small { overflow: hidden; color: var(--el-text-color-secondary); font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
.filter-band { margin-bottom: 16px; }
.filter-grid { display: grid; grid-template-columns: repeat(3, minmax(160px, 1fr)); gap: 0 14px; }
.filter-grid :deep(.el-form-item) { margin-bottom: 12px; }
.filter-grid :deep(.el-select), .filter-grid :deep(.el-input) { width: 100%; }
.filter-actions { display: flex; align-items: flex-end; gap: 8px; padding-bottom: 12px; }
.order-table-band { min-width: 0; overflow: hidden; }
.table-heading { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding-bottom: 12px; }
.table-heading > div { display: flex; align-items: baseline; gap: 9px; }
.table-heading strong { color: var(--el-text-color-primary); font-size: 15px; font-weight: 600; }
.table-heading span { color: var(--el-text-color-secondary); font-size: 12px; }
.issue-count { color: var(--el-color-danger) !important; }
.order-cell, .business-cell, .provider-cell, .amount-cell { display: grid; min-width: 0; gap: 4px; }
.order-link { overflow: hidden; padding: 0; border: 0; background: transparent; color: var(--el-color-primary); font: inherit; font-weight: 500; text-align: left; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; }
.order-cell > span, .business-cell small, .provider-cell small, .amount-cell small { overflow: hidden; color: var(--el-text-color-secondary); font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
.business-cell > span, .provider-cell > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.amount-cell { justify-items: end; }
.amount-cell strong { color: var(--el-text-color-primary); font-weight: 600; white-space: nowrap; }
.row-actions { display: flex; align-items: center; min-height: 32px; white-space: nowrap; }
.row-actions :deep(.el-button + .el-button) { margin-left: 8px; }
.payment-orders :deep(.el-table__row.is-payment-issue > td.el-table__cell) { background: var(--el-color-danger-light-9); }
.kra-pagination { overflow-x: auto; }
.order-detail { min-height: 220px; }
.detail-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding-bottom: 16px; border-bottom: 1px solid var(--el-border-color-lighter); }
.detail-statuses, .detail-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.detail-error { margin-top: 16px; }
.section-heading { margin: 22px 0 12px; color: var(--el-text-color-primary); font-size: 14px; font-weight: 600; letter-spacing: 0; }
.copy-value { display: inline-flex; align-items: center; gap: 4px; max-width: 100%; word-break: break-all; }
.amount-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); border-top: 1px solid var(--el-border-color-lighter); border-left: 1px solid var(--el-border-color-lighter); }
.amount-item { display: grid; min-width: 0; gap: 5px; padding: 12px 14px; border-right: 1px solid var(--el-border-color-lighter); border-bottom: 1px solid var(--el-border-color-lighter); }
.amount-item span { color: var(--el-text-color-secondary); font-size: 12px; }
.amount-item strong { overflow: hidden; color: var(--el-text-color-primary); font-size: 14px; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
.amount-alert { margin-top: 12px; }
.order-timeline { margin: 0; padding-top: 4px; }
.refund-summary { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px 14px; margin-bottom: 18px; padding: 12px 14px; border: 1px solid var(--el-border-color-lighter); border-radius: 6px; background: var(--el-fill-color-lighter); }
.refund-summary span { color: var(--el-text-color-secondary); font-size: 12px; }
.refund-summary strong { overflow-wrap: anywhere; color: var(--el-text-color-primary); font-size: 13px; text-align: right; }
.form-tip { width: 100%; margin-top: 5px; color: var(--el-text-color-secondary); font-size: 12px; line-height: 1.4; }
@media (max-width: 1000px) { .filter-grid { grid-template-columns: repeat(2, minmax(160px, 1fr)); } .amount-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 720px) { .page-heading { align-items: flex-start; } .page-heading p { max-width: 250px; } .status-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); } .summary-item { padding: 12px; } .summary-item strong { font-size: 20px; } .filter-grid { grid-template-columns: 1fr; } .filter-actions { align-items: stretch; padding-bottom: 4px; } .filter-actions :deep(.el-button) { flex: 1; } .detail-toolbar { align-items: stretch; flex-direction: column; } .detail-actions :deep(.el-button) { margin-left: 0; } .amount-grid { grid-template-columns: 1fr; } }
@media (max-width: 440px) { .status-summary { grid-template-columns: 1fr; } .table-heading { align-items: flex-start; flex-direction: column; gap: 5px; } .refund-summary { grid-template-columns: 1fr; } .refund-summary strong { text-align: left; } }
</style>
