<template>
  <div class="kra-table-box payment-config-page">
    <header class="page-heading">
      <div>
        <h2>支付渠道配置</h2>
        <p>管理渠道凭证、回调地址和测试交易参数。</p>
      </div>
      <el-button :icon="Refresh" :loading="loading" :disabled="busy" @click="refreshConfigs">刷新</el-button>
    </header>

    <div v-loading="loading" class="config-layout">
      <aside class="provider-panel" aria-label="支付渠道列表">
        <div class="panel-heading"><span>渠道</span><span>{{ configs.length }}</span></div>
        <button
          v-for="item in configs"
          :key="item.provider"
          type="button"
          class="provider-item"
          :class="{ active: item.provider === selectedProvider }"
          @click="selectProvider(item.provider)"
        >
          <span class="provider-copy">
            <strong>{{ item.name || providerText(item.provider) }}</strong>
            <small>{{ item.provider }}</small>
          </span>
          <span class="provider-state" :class="{ enabled: item.enabled }">
            {{ item.enabled ? '已启用' : '已停用' }}
          </span>
        </button>
      </aside>

      <section v-if="selected" class="editor-panel">
        <header class="editor-heading">
          <div class="editor-title-group">
            <div class="editor-title-row">
              <h3>{{ selected.name || providerText(selected.provider) }}</h3>
              <el-tag v-if="isDirty(selected)" type="warning" effect="plain">未保存</el-tag>
              <el-tag v-else-if="selected.enabled" type="success" effect="plain">运行中</el-tag>
            </div>
            <p>{{ selected.description || selected.provider }}</p>
          </div>
          <div class="enable-control">
            <span>{{ selected.enabled ? '已启用' : '已停用' }}</span>
            <el-switch
              :model-value="selected.enabled"
              :loading="operation === 'toggle'"
              :disabled="busy"
              aria-label="启用支付渠道"
              @change="toggleProvider"
            />
          </div>
        </header>

        <el-alert
          v-if="hasMaskedSecret(selected)"
          type="info"
          :closable="false"
          show-icon
          class="editor-alert"
          title="凭证已脱敏；保留 ****** 将继续使用当前凭证。"
        />
        <el-alert
          v-else-if="!selected.configured"
          type="warning"
          :closable="false"
          show-icon
          class="editor-alert"
          title="该渠道尚未保存。填写配置并保存后才能启用或测试。"
        />

        <el-form :model="selected.config" label-position="top" class="config-form" @submit.prevent>
          <div class="field-grid">
            <el-form-item
              v-for="field in selected.fields || []"
              :key="field.key"
              :required="field.required"
              :error="fieldError(field.key)"
            >
              <template #label>
                <span class="field-label"><span>{{ field.label }}</span><small>{{ field.key }}</small></span>
              </template>

              <el-select
                v-if="field.type === 'select'"
                v-model="selected.config[field.key]"
                class="field-control"
                filterable
                :placeholder="field.placeholder || '请选择'"
                @update:model-value="clearFieldError(field.key)"
              >
                <el-option v-for="option in field.options || []" :key="String(option.value)" :label="option.label" :value="option.value" />
              </el-select>
              <el-switch
                v-else-if="field.type === 'switch'"
                v-model="selected.config[field.key]"
                @update:model-value="clearFieldError(field.key)"
              />
              <el-input-number
                v-else-if="field.type === 'number'"
                v-model="selected.config[field.key]"
                class="field-control"
                :min="field.key === 'test_amount' ? 1 : 0"
                :step="1"
                controls-position="right"
                @update:model-value="clearFieldError(field.key)"
              />
              <el-input
                v-else-if="field.type === 'textarea'"
                v-model="selected.config[field.key]"
                class="field-control"
                type="textarea"
                :rows="field.secret ? 5 : 4"
                :placeholder="field.placeholder"
                spellcheck="false"
                @update:model-value="clearFieldError(field.key)"
              />
              <el-input
                v-else
                v-model="selected.config[field.key]"
                class="field-control"
                :type="field.secret ? 'password' : 'text'"
                :show-password="field.secret"
                :placeholder="field.placeholder"
                spellcheck="false"
                @update:model-value="clearFieldError(field.key)"
              />
              <p v-if="fieldHint(field)" class="field-hint">{{ fieldHint(field) }}</p>
            </el-form-item>
          </div>
        </el-form>

        <footer class="editor-actions">
          <span class="save-state">{{ saveStateText }}</span>
          <el-button
            :icon="Connection"
            :loading="operation === 'test'"
            :disabled="busy || !selected.configured || isDirty(selected)"
            @click="testProvider"
          >
            测试渠道
          </el-button>
          <el-button
            v-if="selected.configured"
            type="danger"
            plain
            :icon="Delete"
            :loading="operation === 'delete'"
            :disabled="busy"
            @click="remove"
          >
            删除配置
          </el-button>
          <el-button
            type="primary"
            :icon="Check"
            :loading="operation === 'save'"
            :disabled="busy || !isDirty(selected)"
            @click="saveSelected"
          >
            保存配置
          </el-button>
        </footer>
      </section>

      <el-empty v-else description="暂无支付渠道" />
    </div>

    <el-dialog v-model="testVisible" title="渠道测试结果" width="min(620px, calc(100vw - 32px))" destroy-on-close>
      <div v-if="testResult" class="test-result">
        <el-result
          :icon="testResult.passed ? 'success' : 'error'"
          :title="testResultTitle"
          :sub-title="testResult.tradeNo ? `测试订单：${testResult.tradeNo}` : undefined"
        />
        <div class="test-stages">
          <div v-for="stage in testResult.stages || []" :key="`${stage.name}-${stage.tradeNo || ''}`" class="test-stage">
            <el-icon :class="`stage-${stage.status}`"><component :is="stageIcon(stage.status)" /></el-icon>
            <div><strong>{{ stageName(stage.name) }}</strong><p>{{ stage.message || stageStatusText(stage.status) }}</p></div>
            <span v-if="stage.durationMs != null">{{ stage.durationMs }} ms</span>
          </div>
        </div>
      </div>
      <template #footer><el-button @click="testVisible = false">关闭</el-button></template>
    </el-dialog>
  </div>
</template>

<script setup>
import { computed, onMounted, reactive, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Check, CircleCheck, CircleClose, Connection, Delete, MoreFilled, Refresh } from '@element-plus/icons-vue'
import { deleteIntegrationConfig, getIntegrationConfigs, saveIntegrationConfig } from '@/api/integration'
import { testPaymentProvider } from '@/api/payment'

defineOptions({ name: 'PaymentConfig' })

const PROVIDER_NAMES = {
  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': '抖音小游戏支付'
}
const STAGE_NAMES = { config: '配置校验', test_settings: '测试参数', adapter: '渠道适配器', local_order: '本地测试订单', create: '渠道下单', query: '渠道查单', refund: '渠道退款' }

const configs = ref([])
const selectedProvider = ref('')
const loading = ref(false)
const operation = ref('')
const errors = reactive({})
const testVisible = ref(false)
const testResult = ref(null)
let loadRequestID = 0

const selected = computed(() => configs.value.find((item) => item.provider === selectedProvider.value) || configs.value[0])
const busy = computed(() => Boolean(operation.value))
const saveStateText = computed(() => {
  if (!selected.value) return ''
  if (!selected.value.configured) return '尚未创建配置'
  if (isDirty(selected.value)) return '存在未保存的修改'
  return selected.value.enabled ? '配置已保存，渠道已启用' : '配置已保存，渠道已停用'
})
const testResultTitle = computed(() => {
  if (!testResult.value?.passed) return '渠道测试未通过'
  const refundStage = testResult.value.stages?.find((stage) => stage.name === 'refund')
  if (refundStage?.status === 'passed' && /受理|接受|等待|pending|processing/i.test(refundStage.message || '')) return '支付链路已受理，等待退款确认'
  return testResult.value.fullFlow ? '完整支付链路测试通过' : '渠道连通性测试通过'
})

const cloneConfig = (value) => JSON.parse(JSON.stringify(value || {}))
const normalizeConfig = (item) => {
  const normalized = { ...item, enabled: Boolean(item.enabled), configured: Boolean(item.configured), config: cloneConfig(item.config), fields: Array.isArray(item.fields) ? item.fields : [] }
  normalized._savedEnabled = normalized.enabled
  normalized._savedConfig = cloneConfig(normalized.config)
  return normalized
}
const providerText = (provider) => PROVIDER_NAMES[provider] || provider
const fieldError = (key) => errors[key] || ''
const clearFieldError = (key) => { delete errors[key] }
const isEmpty = (value) => value === null || typeof value === 'undefined' || (typeof value === 'string' && value.trim() === '')
const isDirty = (item) => JSON.stringify({ enabled: item.enabled, config: item.config }) !== JSON.stringify({ enabled: item._savedEnabled, config: item._savedConfig })
const hasMaskedSecret = (item) => (item.fields || []).some((field) => field.secret && item.config[field.key] === '******')
const fieldHint = (field) => {
  if (field.key === 'test_mode') return '仅在需要执行真实渠道测试时开启。'
  if (field.key === 'test_amount') return '使用最小货币单位，例如 CNY 1 表示 0.01 元。'
  if (field.key === 'test_extra') return '必须是 JSON 对象；可传 openid、auth_code 等测试参数。'
  if (field.key === 'notify_url') return '异步支付方式必须填写可被支付平台访问的 HTTPS 地址。'
  return field.description || ''
}
const stageName = (name) => STAGE_NAMES[name] || name
const stageStatusText = (status) => status === 'passed' ? '通过' : status === 'skipped' ? '跳过' : '失败'
const stageIcon = (status) => status === 'passed' ? CircleCheck : status === 'failed' ? CircleClose : MoreFilled

function validate(item, enabled = item.enabled) {
  Object.keys(errors).forEach((key) => delete errors[key])
  let valid = true
  for (const field of item.fields || []) {
    const value = item.config[field.key]
    let message = ''
    if (enabled && field.required && isEmpty(value)) message = `请填写${field.label}`
    else if (field.type === 'number' && !isEmpty(value) && (!Number.isFinite(Number(value)) || Number(value) < 0)) message = `${field.label}必须是非负数`
    else if (field.key === 'test_amount' && !isEmpty(value) && Number(value) <= 0) message = '测试金额必须大于 0'
    else if (field.key === 'test_extra' && String(value || '').trim()) {
      try {
        const parsed = JSON.parse(String(value))
        if (!parsed || Array.isArray(parsed) || typeof parsed !== 'object') message = '测试扩展参数必须是 JSON 对象'
      } catch { message = '测试扩展参数必须是合法 JSON' }
    } else if ((field.type === 'url' || field.key.endsWith('_url')) && value) {
      try {
        const url = new URL(String(value))
        if (!['http:', 'https:'].includes(url.protocol)) message = `${field.label}必须使用 HTTP 或 HTTPS`
      } catch { message = `${field.label}格式无效` }
    }
    if (!item.configured && field.secret && value === '******') message = '请重新填写' + field.label
    if (message) { errors[field.key] = message; valid = false }
  }
  if (!valid) ElMessage.warning('请先修正配置项')
  return valid
}

async function load() {
  if (loading.value) return
  const requestID = ++loadRequestID
  loading.value = true
  try {
    const res = await getIntegrationConfigs('payment')
    if (requestID !== loadRequestID || res.code !== 0) return
    configs.value = (res.data || []).map(normalizeConfig)
    if (!configs.value.some((item) => item.provider === selectedProvider.value)) selectedProvider.value = configs.value[0]?.provider || ''
  } catch {
    // The request layer already presents transport errors.
  } finally { loading.value = false }
}

async function refreshConfigs() {
  if (busy.value) return
  if (selected.value && isDirty(selected.value)) {
    try {
      await ElMessageBox.confirm('当前渠道有未保存修改，刷新会丢失这些修改。确认刷新吗？', '刷新支付配置', { type: 'warning', confirmButtonText: '确认刷新' })
    } catch { return }
  }
  await load()
}

async function selectProvider(provider) {
  if (provider === selectedProvider.value || busy.value) return
  if (selected.value && isDirty(selected.value)) {
    try {
      await ElMessageBox.confirm('当前渠道有未保存修改，切换后会丢失这些修改。确认切换吗？', '切换支付渠道', { type: 'warning', confirmButtonText: '确认切换' })
    } catch { return }
  }
  Object.keys(errors).forEach((key) => delete errors[key])
  selectedProvider.value = provider
}

async function persist(item, type) {
  if (busy.value || !validate(item, item.enabled)) return false
  operation.value = type
  try {
    const res = await saveIntegrationConfig('payment', item.provider, { enabled: item.enabled, config: item.config })
    if (res.code !== 0) return false
    const provider = item.provider
    await load()
    selectedProvider.value = provider
    return true
  } catch { return false }
  finally { operation.value = '' }
}

async function saveSelected() {
  if (!selected.value) return
  if (await persist(selected.value, 'save')) ElMessage.success('支付渠道配置已保存')
}

async function toggleProvider(enabled) {
  const item = selected.value
  if (!item || busy.value) return
  const previous = item.enabled
  item.enabled = Boolean(enabled)
  if (item.enabled && !validate(item, true)) { item.enabled = previous; return }
  if (await persist(item, 'toggle')) {
    ElMessage.success(`${item.name || providerText(item.provider)} 已${item.enabled ? '启用' : '停用'}`)
    return
  }
  item.enabled = previous
  ElMessage.warning('渠道状态未改变')
}

async function testProvider() {
  const item = selected.value
  if (!item || busy.value) return
  if (isDirty(item)) { ElMessage.warning('请先保存当前配置'); return }
  if (!item.config?.test_mode) { ElMessage.warning('请先开启“允许执行渠道测试”并保存'); return }
  const environment = String(item.config?.environment || '').toLowerCase()
  if (environment === 'production' || environment === 'prod') {
    operation.value = 'test-confirm'
    try {
      await ElMessageBox.confirm('当前渠道使用生产环境，测试会真实创建最小金额订单。确认使用专用测试商户执行吗？', '生产环境测试确认', { type: 'warning', confirmButtonText: '确认测试' })
    } catch { operation.value = ''; return }
  }
  operation.value = 'test'
  try {
    const res = await testPaymentProvider(item.provider)
    testResult.value = res.data || { passed: false, stages: [{ name: 'test', status: 'failed', message: res.msg || '未知错误' }] }
    testVisible.value = true
  } catch {
    // The request layer already presents transport errors.
  } finally { operation.value = '' }
}

async function remove() {
  const item = selected.value
  if (!item || busy.value) return
  try {
    await ElMessageBox.confirm(`删除 ${item.name || providerText(item.provider)} 配置后，该渠道将立即不可用。确认继续吗？`, '删除支付配置', { type: 'warning', confirmButtonText: '确认删除' })
  } catch { return }
  operation.value = 'delete'
  try {
    const res = await deleteIntegrationConfig('payment', item.provider)
    if (res.code !== 0) return
    ElMessage.success('支付渠道配置已删除')
    await load()
  } catch {
    // The request layer already presents transport errors.
  } finally { operation.value = '' }
}

onMounted(load)
</script>

<style scoped>
.payment-config-page { min-height: 640px; }
.page-heading { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 18px; }
.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; }
.config-layout { display: grid; grid-template-columns: 248px minmax(0, 1fr); min-height: 560px; overflow: hidden; border: 1px solid var(--el-border-color-lighter); background: var(--el-bg-color); }
.provider-panel { padding: 12px 9px; border-right: 1px solid var(--el-border-color-lighter); background: var(--el-fill-color-blank); }
.panel-heading { display: flex; justify-content: space-between; padding: 4px 10px 11px; color: var(--el-text-color-secondary); font-size: 12px; }
.provider-item { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 10px; width: 100%; min-height: 58px; padding: 8px 10px; border: 0; border-left: 3px solid transparent; border-radius: 4px; background: transparent; color: inherit; text-align: left; cursor: pointer; }
.provider-item:hover { background: var(--el-fill-color-light); }
.provider-item.active { border-left-color: var(--el-color-primary); background: var(--el-color-primary-light-9); }
.provider-copy { display: grid; min-width: 0; gap: 3px; }
.provider-copy strong { overflow: hidden; color: var(--el-text-color-primary); font-size: 14px; font-weight: 500; text-overflow: ellipsis; white-space: nowrap; }
.provider-copy small { color: var(--el-text-color-secondary); font-size: 11px; }
.provider-state { color: var(--el-text-color-placeholder); font-size: 11px; white-space: nowrap; }
.provider-state.enabled { color: var(--el-color-success); }
.editor-panel { display: flex; min-width: 0; flex-direction: column; padding: 22px 28px 20px; }
.editor-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 24px; padding-bottom: 18px; border-bottom: 1px solid var(--el-border-color-lighter); }
.editor-title-group { min-width: 0; }
.editor-title-row { display: flex; align-items: center; gap: 9px; }
.editor-title-row h3 { margin: 0; color: var(--el-text-color-primary); font-size: 18px; font-weight: 600; letter-spacing: 0; }
.editor-title-group p { margin: 6px 0 0; color: var(--el-text-color-secondary); font-size: 13px; }
.enable-control { display: flex; align-items: center; gap: 10px; min-height: 32px; color: var(--el-text-color-regular); font-size: 13px; white-space: nowrap; }
.editor-alert { margin-top: 18px; }
.config-form { flex: 1; padding-top: 20px; }
.field-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 22px; }
.field-label { display: inline-flex; align-items: baseline; gap: 7px; min-width: 0; }
.field-label small { overflow: hidden; color: var(--el-text-color-placeholder); font-size: 11px; font-weight: 400; text-overflow: ellipsis; }
.field-control { width: 100%; }
.field-hint { width: 100%; margin: 5px 0 0; color: var(--el-text-color-secondary); font-size: 12px; line-height: 1.5; }
.editor-actions { display: flex; align-items: center; justify-content: flex-end; gap: 10px; padding-top: 16px; border-top: 1px solid var(--el-border-color-lighter); }
.save-state { margin-right: auto; color: var(--el-text-color-secondary); font-size: 12px; }
.test-result :deep(.el-result) { padding: 8px 24px 20px; }
.test-stages { border-top: 1px solid var(--el-border-color-lighter); }
.test-stage { display: grid; grid-template-columns: 22px minmax(0, 1fr) auto; align-items: start; gap: 10px; padding: 13px 4px; border-bottom: 1px solid var(--el-border-color-lighter); }
.test-stage strong { color: var(--el-text-color-primary); font-size: 13px; font-weight: 600; }
.test-stage p { margin: 3px 0 0; color: var(--el-text-color-secondary); font-size: 12px; line-height: 1.5; }
.test-stage > span { color: var(--el-text-color-placeholder); font-size: 11px; white-space: nowrap; }
.stage-passed { color: var(--el-color-success); } .stage-failed { color: var(--el-color-danger); } .stage-skipped { color: var(--el-text-color-placeholder); }
@media (max-width: 900px) { .config-layout { grid-template-columns: 1fr; } .provider-panel { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; max-height: 270px; overflow-y: auto; border-right: 0; border-bottom: 1px solid var(--el-border-color-lighter); } .panel-heading { display: none; } .provider-state { display: none; } .editor-panel { padding: 20px; } .field-grid { grid-template-columns: 1fr; } }
@media (max-width: 560px) { .payment-config-page { min-height: 0; } .page-heading { align-items: flex-start; } .provider-panel { grid-template-columns: 1fr; } .provider-state { display: inline; } .editor-panel { padding: 18px 14px; } .editor-heading { align-items: stretch; flex-direction: column; gap: 14px; } .enable-control { justify-content: space-between; } .editor-actions { align-items: stretch; flex-direction: column; } .save-state { margin-right: 0; } .editor-actions :deep(.el-button) { margin-left: 0; } }
</style>
