<script setup lang="ts">
import { readError } from '@/shared/utils/error'
import { Search } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { computed, onMounted, ref } from 'vue'

import { arbitrateDispute, fetchAdminDisputes, type Dispute } from '@/features/disputes'
import { fetchAdminFileBlob } from '@/shared/api/files'
import AuthImage from '@/shared/components/business/AuthImage.vue'
import {
  disputeStatusLabel,
  handoffStatusLabel,
  orderStatusLabel,
  settlementStatusLabel,
} from '@/shared/utils/statusLabels'
import { formatDateTime } from '@/shared/utils/time'
import { formatListingNo } from '@/shared/utils/listingDisplay'
import { yuanToCent } from '@/shared/utils/money'
import AdminTablePagination from '../components/AdminTablePagination.vue'

const loading = ref(false)
const submitting = ref(false)
const disputes = ref<Dispute[]>([])
const activeDispute = ref<Dispute | null>(null)
const evidenceDispute = ref<Dispute | null>(null)
const result = ref('release_deposit')
const remark = ref('')
const amount = ref<number | undefined>()
const currentPage = ref(1)
const currentPageSize = ref(20)
const total = ref(0)
const keywordFilter = ref('')
const statusFilter = ref('')
const typeFilter = ref('')
const isPartialRefund = computed(() => result.value === 'partial_refund')
const pendingCount = computed(
  () => disputes.value.filter(item => ['open', 'processing'].includes(item.status)).length
)
const resolvedCount = computed(
  () => disputes.value.filter(item => item.status === 'resolved').length
)
const cancelledCount = computed(
  () => disputes.value.filter(item => item.status === 'cancelled').length
)
const canSubmitArbitration = computed(() => {
  if (submitting.value || !activeDispute.value) return false
  if (!result.value || !remark.value.trim()) return false
  if (isPartialRefund.value && (!amount.value || amount.value <= 0)) return false
  return true
})
function canArbitrate(row: Dispute) {
  return ['open', 'processing'].includes(row.status)
}

function disputeTypeLabel(type: string) {
  const map: Record<string, string> = {
    cannot_login: '无法登录',
    false_description: '描述不符',
    account_banned: '账号封禁',
    asset_loss: '资产损失',
    haf_coin_dispute: '哈夫币争议',
    handoff_timeout: '交接超时',
    return_timeout: '归还超时',
    checkout_amount: '结账金额争议',
    checkout_dispute: '结账争议',
  }
  return map[type] || type || '-'
}

function arbitrationResultLabel(value: string) {
  const map: Record<string, string> = {
    full_refund: '全额退款',
    partial_refund: '部分退款',
    deduct_deposit: '扣押金',
    release_deposit: '释放押金',
    compensate_owner: '赔付号主',
    order_close: '关闭订单',
    mark_abnormal: '标记异常',
  }
  return map[value] || value || '-'
}

function disputeStatusTone(status: string) {
  const map: Record<string, 'success' | 'warning' | 'info' | 'danger'> = {
    open: 'warning',
    processing: 'warning',
    resolved: 'success',
    closed: 'info',
    cancelled: 'info',
  }
  return map[status] || 'info'
}

function roleLabel(row: Dispute, userID: number) {
  if (userID === row.owner_id) return '号主'
  if (userID === row.renter_id) return '租客'
  return '用户'
}

function userPhone(row: Dispute, userID: number) {
  if (userID === row.owner_id) return row.owner_phone || '-'
  if (userID === row.renter_id) return row.renter_phone || '-'
  return '-'
}

function evidenceCount(row: Dispute) {
  return evidenceItems(row).length
}

onMounted(loadDisputes)

async function loadDisputes() {
  loading.value = true
  try {
    const res = await fetchAdminDisputes(currentPage.value, currentPageSize.value, {
      keyword: keywordFilter.value.trim() || undefined,
      status: statusFilter.value || undefined,
      type: typeFilter.value || undefined,
    })
    disputes.value = res.items
    total.value = res.total
  } finally {
    loading.value = false
  }
}

async function handlePageChange() {
  await loadDisputes()
}

async function queryDisputes() {
  currentPage.value = 1
  await loadDisputes()
}

function openArbitration(row: Dispute) {
  if (!canArbitrate(row)) return
  activeDispute.value = row
  result.value = row.arbitration_result || 'release_deposit'
  remark.value = row.arbitration_remark || ''
  amount.value = undefined
}

function resetFilters() {
  keywordFilter.value = ''
  statusFilter.value = ''
  typeFilter.value = ''
  void queryDisputes()
}

function evidenceItems(row: Dispute | null) {
  const raw = row?.evidence_urls
  if (!raw) return []
  if (Array.isArray(raw)) return raw
  return []
}

function extractObjectKey(url: string) {
  try {
    const parsed = new URL(url, window.location.origin)
    return parsed.searchParams.get('key') || ''
  } catch {
    return ''
  }
}

function isObjectKeyLike(value: string) {
  const trimmed = value.trim()
  if (!trimmed || trimmed.includes('\n')) return false
  if (/^(https?:|blob:|data:|mailto:|tel:)/i.test(trimmed) || trimmed.startsWith('/')) return false
  return /^[a-z0-9][a-z0-9/_-]*\/[^?#]+\.[a-z0-9]{2,8}$/i.test(trimmed)
}

function evidenceObjectKey(value: string) {
  return extractObjectKey(value) || (isObjectKeyLike(value) ? value.trim() : '')
}

function evidenceSource(value: string) {
  const key = evidenceObjectKey(value)
  if (key && isObjectKeyLike(value)) {
    return `/api/admin/files/object?key=${encodeURIComponent(key)}`
  }
  return value
}

function evidenceName(value: string, index: number) {
  const key = evidenceObjectKey(value)
  const source = key || value
  try {
    const parsed = new URL(source, window.location.origin)
    const part = decodeURIComponent(parsed.pathname.split('/').filter(Boolean).pop() || '')
    if (part) return part
  } catch {
    // 非 URL 文本证据直接走下面的普通解析。
  }
  const part = decodeURIComponent(source.split('/').filter(Boolean).pop() || '')
  return part || `证据 ${index + 1}`
}

function evidenceDetail(value: string) {
  const key = evidenceObjectKey(value)
  if (key) return decodeURIComponent(key)
  if (value.startsWith('/api/files/object') || value.startsWith('/api/admin/files/object'))
    return value
  try {
    const parsed = new URL(value, window.location.origin)
    return parsed.href
  } catch {
    return value
  }
}

function evidenceKind(value: string) {
  const name = evidenceName(value, 0).toLowerCase()
  if (/\.(png|jpe?g|webp|gif|bmp|avif)$/.test(name)) return 'image'
  if (name.endsWith('.pdf')) return 'pdf'
  if (value.startsWith('http') || value.startsWith('/api/')) return 'link'
  return 'text'
}

function evidenceKindLabel(value: string) {
  const map: Record<string, string> = {
    image: '图片',
    pdf: 'PDF',
    link: '链接',
    text: '文本',
  }
  return map[evidenceKind(value)] || '证据'
}

async function copyEvidence(value: string) {
  try {
    await navigator.clipboard.writeText(evidenceDetail(value))
    ElMessage.success('证据信息已复制')
  } catch {
    ElMessage.error('复制失败')
  }
}

async function openEvidence(url: string) {
  const key = extractObjectKey(url)
  const objectKey = key || (isObjectKeyLike(url) ? url.trim() : '')
  if (objectKey) {
    try {
      const blob = await fetchAdminFileBlob(objectKey)
      const objectURL = URL.createObjectURL(blob)
      window.open(objectURL, '_blank')
      window.setTimeout(() => URL.revokeObjectURL(objectURL), 60_000)
    } catch (error) {
      ElMessage.error(readError(error, '证据文件打开失败'))
    }
    return
  }
  if (/^https?:\/\//i.test(url) || url.startsWith('/api/')) {
    window.open(url, '_blank')
    return
  }
  await copyEvidence(url)
}

async function handleArbitrate() {
  if (!activeDispute.value) return
  if (submitting.value) return
  if (!remark.value.trim()) {
    ElMessage.warning('请填写客服裁决说明')
    return
  }
  if (isPartialRefund.value && (!amount.value || amount.value <= 0)) {
    ElMessage.warning('请填写部分退款金额')
    return
  }
  submitting.value = true
  try {
    await arbitrateDispute(activeDispute.value.id, {
      result: result.value,
      remark: remark.value.trim(),
      amount_cent: amount.value ? yuanToCent(amount.value) : undefined,
    })
    ElMessage.success('仲裁结果已保存，双方已收到通知')
    activeDispute.value = null
    remark.value = ''
    await loadDisputes()
  } catch (error) {
    ElMessage.error(readError(error, '仲裁失败'))
  } finally {
    submitting.value = false
  }
}
</script>

<template>
  <section class="page">
    <div class="page-header-row">
      <div class="page-header">
        <p class="eyebrow">申诉仲裁</p>
        <h1>仲裁中心</h1>
        <p>集中处理订单申诉、结账争议、证据查看和客服裁决。</p>
      </div>
    </div>

    <div class="dispute-summary">
      <div class="summary-item">
        <span>当前页待处理</span>
        <strong>{{ pendingCount }}</strong>
      </div>
      <div class="summary-item">
        <span>当前页已处理</span>
        <strong>{{ resolvedCount }}</strong>
      </div>
      <div class="summary-item">
        <span>当前页已取消</span>
        <strong>{{ cancelledCount }}</strong>
      </div>
      <div class="summary-item">
        <span>当前页合计</span>
        <strong>{{ disputes.length }}</strong>
      </div>
    </div>

    <div class="filter-panel">
      <el-input
        v-model="keywordFilter"
        clearable
        placeholder="订单号 / 商品编号 / 手机号"
        class="filter-control keyword-filter"
        @keyup.enter="queryDisputes"
        @clear="queryDisputes"
      />
      <el-select
        v-model="statusFilter"
        clearable
        placeholder="全部状态"
        class="filter-control"
        @change="queryDisputes"
      >
        <el-option label="待处理" value="open" />
        <el-option label="处理中" value="processing" />
        <el-option label="已处理" value="resolved" />
        <el-option label="已关闭" value="closed" />
        <el-option label="已取消" value="cancelled" />
      </el-select>
      <el-select
        v-model="typeFilter"
        clearable
        placeholder="全部类型"
        class="filter-control"
        @change="queryDisputes"
      >
        <el-option label="无法登录" value="cannot_login" />
        <el-option label="描述不符" value="false_description" />
        <el-option label="账号封禁" value="account_banned" />
        <el-option label="资产损失" value="asset_loss" />
        <el-option label="哈夫币争议" value="haf_coin_dispute" />
        <el-option label="交接超时" value="handoff_timeout" />
        <el-option label="归还超时" value="return_timeout" />
        <el-option label="结账争议" value="checkout_dispute" />
      </el-select>
      <el-button type="primary" :icon="Search" @click="queryDisputes">查询</el-button>
      <el-button @click="resetFilters">重置</el-button>
    </div>

    <el-table v-loading="loading" class="table-panel dispute-table" :data="disputes">
      <el-table-column label="商品编号" width="140">
        <template #default="{ row }">{{ formatListingNo(row.listing_no) }}</template>
      </el-table-column>
      <el-table-column prop="order_no" label="订单号" min-width="210" />
      <el-table-column label="账号信息" min-width="190" show-overflow-tooltip>
        <template #default="{ row }">
          <div class="main-cell">
            <strong>{{ row.title }}</strong>
            <span
              >{{ orderStatusLabel(row.order_status) }} ·
              {{ handoffStatusLabel(row.handoff_status) }}</span
            >
          </div>
        </template>
      </el-table-column>
      <el-table-column label="申诉类型" width="140">
        <template #default="{ row }">{{ disputeTypeLabel(row.type) }}</template>
      </el-table-column>
      <el-table-column label="申诉状态" width="115">
        <template #default="{ row }">
          <el-tag :type="disputeStatusTone(row.status)" effect="light">
            {{ disputeStatusLabel(row.status) }}
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column label="双方信息" min-width="240">
        <template #default="{ row }">
          <div class="party-cell">
            <span
              >申诉方：{{ roleLabel(row, row.initiator_id) }}
              {{ userPhone(row, row.initiator_id) }}</span
            >
            <span
              >被申诉方：{{ roleLabel(row, row.target_user_id) }}
              {{ userPhone(row, row.target_user_id) }}</span
            >
          </div>
        </template>
      </el-table-column>
      <el-table-column label="申诉说明" min-width="240" show-overflow-tooltip>
        <template #default="{ row }">{{ row.description || '-' }}</template>
      </el-table-column>
      <el-table-column label="证据" width="90">
        <template #default="{ row }">{{ evidenceCount(row) }} 份</template>
      </el-table-column>
      <el-table-column label="创建时间" min-width="180">
        <template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
      </el-table-column>
      <el-table-column label="结算状态" width="115">
        <template #default="{ row }">{{ settlementStatusLabel(row.settlement_status) }}</template>
      </el-table-column>
      <el-table-column label="仲裁结果" width="135" show-overflow-tooltip>
        <template #default="{ row }">{{ arbitrationResultLabel(row.arbitration_result) }}</template>
      </el-table-column>
      <el-table-column label="处理时间" min-width="180">
        <template #default="{ row }">{{
          row.handled_at ? formatDateTime(row.handled_at) : '-'
        }}</template>
      </el-table-column>
      <el-table-column label="操作" width="170" fixed="right">
        <template #default="{ row }">
          <el-button
            size="small"
            :disabled="evidenceItems(row).length === 0"
            @click="evidenceDispute = row"
            >证据</el-button
          >
          <el-button size="small" :disabled="!canArbitrate(row)" @click="openArbitration(row)"
            >仲裁</el-button
          >
        </template>
      </el-table-column>
    </el-table>

    <AdminTablePagination
      v-if="total > 0"
      v-model:current-page="currentPage"
      v-model:page-size="currentPageSize"
      :total="total"
      :loading="loading"
      @page-change="handlePageChange"
    />

    <el-dialog
      :model-value="!!activeDispute"
      title="申诉仲裁"
      width="560px"
      @update:model-value="activeDispute = null"
    >
      <div v-if="activeDispute" class="dialog-body">
        <p>
          <strong>{{ activeDispute.order_no }}</strong> · 商品编号
          {{ formatListingNo(activeDispute.listing_no) }} · {{ activeDispute.title }}
        </p>
        <p>
          {{ disputeTypeLabel(activeDispute.type) }} ·
          {{ disputeStatusLabel(activeDispute.status) }} ·
          {{ orderStatusLabel(activeDispute.order_status) }}
        </p>
        <p>{{ activeDispute.description }}</p>
        <el-select v-model="result" class="full-control" placeholder="选择裁决结果">
          <el-option label="全额退款" value="full_refund" />
          <el-option label="部分退款" value="partial_refund" />
          <el-option label="扣押金" value="deduct_deposit" />
          <el-option label="释放押金" value="release_deposit" />
          <el-option label="赔付号主" value="compensate_owner" />
          <el-option label="关闭订单" value="order_close" />
          <el-option label="标记异常" value="mark_abnormal" />
        </el-select>
        <el-input-number
          v-if="['partial_refund', 'deduct_deposit', 'compensate_owner'].includes(result)"
          v-model="amount"
          class="full-control panel-action"
          :min="0"
          :precision="0"
          :step="10"
          placeholder="裁决金额"
        />
        <p v-if="result === 'partial_refund'">
          部分退款金额表示退给租客的金额，剩余冻结金额结算给号主。
        </p>
        <p v-if="['deduct_deposit', 'compensate_owner'].includes(result)">
          金额表示从押金中赔付给号主的部分；不填则默认处理全额押金。
        </p>
        <el-input
          v-model="remark"
          class="panel-action"
          type="textarea"
          :rows="4"
          placeholder="填写客服裁决说明"
        />
      </div>
      <template #footer>
        <el-button :disabled="submitting" @click="activeDispute = null">取消</el-button>
        <el-button
          type="primary"
          :loading="submitting"
          :disabled="!canSubmitArbitration"
          @click="handleArbitrate"
          >保存裁决</el-button
        >
      </template>
    </el-dialog>

    <el-dialog
      :model-value="!!evidenceDispute"
      title="申诉证据"
      width="760px"
      @update:model-value="evidenceDispute = null"
    >
      <div v-if="evidenceDispute" class="evidence-dialog">
        <div class="evidence-header">
          <div>
            <strong>{{ evidenceDispute.order_no }}</strong>
            <span>商品编号 {{ formatListingNo(evidenceDispute.listing_no) }}</span>
          </div>
          <el-tag :type="disputeStatusTone(evidenceDispute.status)" effect="light">
            {{ disputeStatusLabel(evidenceDispute.status) }}
          </el-tag>
        </div>

        <div class="evidence-meta-grid">
          <div>
            <span>账号</span>
            <strong>{{ evidenceDispute.title }}</strong>
          </div>
          <div>
            <span>类型</span>
            <strong>{{ disputeTypeLabel(evidenceDispute.type) }}</strong>
          </div>
          <div>
            <span>证据数量</span>
            <strong>{{ evidenceCount(evidenceDispute) }} 份</strong>
          </div>
          <div>
            <span>创建时间</span>
            <strong>{{ formatDateTime(evidenceDispute.created_at) }}</strong>
          </div>
        </div>

        <div class="evidence-description">
          <span>申诉说明</span>
          <p>{{ evidenceDispute.description || '-' }}</p>
        </div>

        <el-empty v-if="evidenceItems(evidenceDispute).length === 0" description="暂无证据文件" />
        <div v-else class="evidence-list">
          <div
            v-for="(item, index) in evidenceItems(evidenceDispute)"
            :key="`${item}-${index}`"
            class="evidence-card"
          >
            <div class="evidence-preview" :class="'evidence-preview-' + evidenceKind(item)">
              <AuthImage
                v-if="evidenceKind(item) === 'image'"
                :source="evidenceSource(item)"
                admin
                :alt="evidenceName(item, index)"
                image-class="evidence-image"
                fallback-class="evidence-image-fallback"
              />
              <div v-else class="evidence-file-icon">
                {{ evidenceKindLabel(item) }}
              </div>
            </div>
            <div class="evidence-info">
              <div class="evidence-title-row">
                <strong>{{ evidenceName(item, index) }}</strong>
                <el-tag size="small" effect="plain">{{ evidenceKindLabel(item) }}</el-tag>
              </div>
              <p>{{ evidenceDetail(item) }}</p>
              <div class="evidence-actions">
                <el-button size="small" type="primary" plain @click="openEvidence(item)">
                  打开
                </el-button>
                <el-button size="small" @click="copyEvidence(item)">复制</el-button>
              </div>
            </div>
          </div>
        </div>
      </div>
      <template #footer>
        <el-button type="primary" @click="evidenceDispute = null">关闭</el-button>
      </template>
    </el-dialog>
  </section>
</template>

<style scoped>
.dispute-summary {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 14px;
}

.summary-item {
  display: grid;
  gap: 6px;
  padding: 14px 16px;
  background: #ffffff;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
}

.summary-item span {
  color: #64748b;
  font-size: 13px;
}

.summary-item strong {
  color: #0f172a;
  font-size: 24px;
  line-height: 1;
}

.filter-panel {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
  padding: 12px;
  background: #ffffff;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
}

.filter-control {
  width: 180px;
}

.keyword-filter {
  width: 280px;
}

.main-cell,
.party-cell {
  display: grid;
  gap: 4px;
}

.main-cell strong {
  color: #0f172a;
  font-weight: 700;
}

.main-cell span,
.party-cell span {
  color: #64748b;
  font-size: 12px;
  line-height: 1.4;
}

.dispute-table :deep(.el-table__cell) {
  vertical-align: top;
}

.evidence-dialog {
  display: grid;
  gap: 14px;
}

.evidence-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}

.evidence-header div {
  display: grid;
  gap: 5px;
}

.evidence-header strong {
  color: #0f172a;
  font-size: 17px;
}

.evidence-header span,
.evidence-meta-grid span,
.evidence-description span {
  color: #64748b;
  font-size: 12px;
  font-weight: 700;
}

.evidence-meta-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
}

.evidence-meta-grid > div {
  display: grid;
  gap: 5px;
  min-width: 0;
  padding: 10px 12px;
  background: #f8fafc;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
}

.evidence-meta-grid strong {
  min-width: 0;
  overflow: hidden;
  color: #1f2937;
  font-size: 13px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.evidence-description {
  display: grid;
  gap: 6px;
  padding: 12px;
  background: #ffffff;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
}

.evidence-description p {
  margin: 0;
  color: #334155;
  font-size: 14px;
  line-height: 1.6;
  white-space: pre-wrap;
}

.evidence-list {
  display: grid;
  gap: 12px;
  max-height: 420px;
  padding-right: 4px;
  overflow: auto;
}

.evidence-card {
  display: grid;
  grid-template-columns: 132px minmax(0, 1fr);
  gap: 14px;
  padding: 12px;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  background: #ffffff;
}

.evidence-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 96px;
  overflow: hidden;
  background: #f8fafc;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
}

.evidence-file-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 58px;
  height: 58px;
  border-radius: 8px;
  background: #eef2ff;
  color: #4f46e5;
  font-weight: 800;
}

.evidence-card :deep(.evidence-image) {
  display: block;
  width: 100%;
  height: 96px;
  object-fit: cover;
}

.evidence-card :deep(.evidence-image-fallback) {
  min-height: 96px;
  border: 0;
}

.evidence-info {
  display: grid;
  align-content: start;
  gap: 8px;
  min-width: 0;
}

.evidence-title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.evidence-title-row strong {
  min-width: 0;
  overflow: hidden;
  color: #0f172a;
  font-size: 14px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.evidence-info p {
  margin: 0;
  color: #64748b;
  font-size: 12px;
  line-height: 1.5;
  overflow-wrap: anywhere;
}

.evidence-actions {
  display: flex;
  gap: 8px;
}

@media (max-width: 960px) {
  .dispute-summary {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .filter-panel {
    align-items: stretch;
    flex-direction: column;
  }

  .filter-control {
    width: 100%;
  }

  .evidence-meta-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .evidence-card {
    grid-template-columns: 1fr;
  }
}
</style>
