<template>
  <div class="art-page-view">
    <!-- 1. 筛选区 -->
    <ElCard v-if="showSearchBar" class="art-search-card" shadow="never">
      <!-- 1.1 快捷筛选标签 -->
      <div class="mb-3 flex flex-wrap items-center gap-2">
        <span class="text-sm text-gray-500">{{ $t('billing.quickFilter.title') }}:</span>
        <ElTag
          :effect="formFilters.status === undefined ? 'dark' : 'plain'"
          :type="formFilters.status === undefined ? 'primary' : 'info'"
          class="cursor-pointer"
          @click="handleQuickStatus(undefined)"
        >
          {{ $t('billing.quickFilter.all') }}
        </ElTag>
        <ElTag
          v-for="opt in statusOptions"
          :key="opt.value"
          :effect="formFilters.status === opt.value ? 'dark' : 'plain'"
          :type="formFilters.status === opt.value ? opt.tagType : 'info'"
          class="cursor-pointer"
          @click="handleQuickStatus(opt.value)"
        >
          {{ opt.label }}
        </ElTag>
      </div>

      <!-- 1.2 多条件筛选 -->
      <ElForm :model="formFilters" label-width="80px">
        <ElRow :gutter="16">
          <ElCol :span="6">
            <ElFormItem :label="$t('billing.search.tenant')">
              <ElSelect
                v-model="formFilters.tenantId"
                :placeholder="$t('billing.search.tenantPlaceholder')"
                clearable
                filterable
                remote
                :remote-method="handleSearchTenant"
                :loading="tenantLoading"
                style="width: 100%"
              >
                <ElOption
                  v-for="item in tenantOptions"
                  :key="item.id"
                  :label="item.name"
                  :value="item.id"
                />
              </ElSelect>
            </ElFormItem>
          </ElCol>
          <ElCol :span="6">
            <ElFormItem :label="$t('billing.search.billingType')">
              <ElSelect
                v-model="formFilters.billingType"
                :placeholder="$t('billing.search.billingTypePlaceholder')"
                clearable
                style="width: 100%"
              >
                <ElOption
                  v-for="type in billingTypeOptions"
                  :key="type.value"
                  :label="type.label"
                  :value="type.value"
                />
              </ElSelect>
            </ElFormItem>
          </ElCol>
          <ElCol :span="6">
            <ElFormItem :label="$t('billing.search.status')">
              <ElSelect
                v-model="formFilters.status"
                :placeholder="$t('billing.search.statusPlaceholder')"
                clearable
                style="width: 100%"
              >
                <ElOption
                  v-for="status in statusOptions"
                  :key="status.value"
                  :label="status.label"
                  :value="status.value"
                />
              </ElSelect>
            </ElFormItem>
          </ElCol>
          <ElCol :span="6">
            <ElFormItem :label="$t('billing.search.dateRange')">
              <ElDatePicker
                v-model="formFilters.dateRange"
                type="daterange"
                :placeholder="$t('billing.search.dateRangePlaceholder')"
                value-format="YYYY-MM-DD"
                style="width: 100%"
              />
            </ElFormItem>
          </ElCol>

          <ElCol :span="6">
            <ElFormItem :label="$t('billing.search.amountRange')">
              <div class="flex items-center gap-2 w-full">
                <ElInputNumber
                  v-model="formFilters.minAmount"
                  :placeholder="$t('billing.search.minAmountPlaceholder')"
                  :min="0"
                  :precision="2"
                  controls-position="right"
                  class="flex-1"
                />
                <span class="text-gray-400">-</span>
                <ElInputNumber
                  v-model="formFilters.maxAmount"
                  :placeholder="$t('billing.search.maxAmountPlaceholder')"
                  :min="0"
                  :precision="2"
                  controls-position="right"
                  class="flex-1"
                />
              </div>
            </ElFormItem>
          </ElCol>
          <ElCol :span="6">
            <ElFormItem :label="$t('billing.search.keyword')">
              <ElInput
                v-model="formFilters.keyword"
                :placeholder="$t('billing.search.keywordPlaceholder')"
                clearable
              />
            </ElFormItem>
          </ElCol>
          <ElCol :span="12" class="text-right">
            <ElButton @click="handleReset">{{ $t('table.searchBar.reset') }}</ElButton>
            <ElButton type="primary" @click="handleSearch" v-ripple>
              {{ $t('table.searchBar.search') }}
            </ElButton>
          </ElCol>
        </ElRow>
      </ElForm>
    </ElCard>

    <!-- 2. 表格区 -->
    <ElCard class="art-table-card" shadow="never">
      <ArtTableHeader
        v-model:columns="columns"
        v-model:showSearchBar="showSearchBar"
        :loading="loading"
        @refresh="refreshData"
      >
        <template #actions>
          <ElButton :loading="isExporting" @click="handleOpenExportDialog" v-ripple>
            <ArtSvgIcon icon="ri:download-2-line" />
            {{ $t('billing.action.export') }}
          </ElButton>
          <ElButton type="primary" @click="handleCreate" v-ripple>
            <ArtSvgIcon icon="ri:add-line" />
            {{ $t('billing.action.create') }}
          </ElButton>
        </template>
      </ArtTableHeader>

      <!-- 批量操作工具栏 -->
      <BatchActionToolbar
        v-if="selectedRows.length > 0"
        :selected-rows="selectedRows"
        :loading="isBatchOperating"
        @confirm="handleBatchConfirmPayment"
        @cancel="handleBatchCancel"
        @export="handleOpenExportDialog"
      />

      <ArtTable
        ref="tableRef"
        :data="data"
        :columns="columns"
        :loading="loading"
        :pagination="pagination"
        row-key="id"
        @selection-change="handleSelectionChange"
        @sort-change="handleSortChange"
        @row-dblclick="handleRowDblClick"
        @pagination:current-change="handleCurrentChange"
        @pagination:size-change="handleSizeChange"
      >
        <template #statementNo="{ row }">
          <span class="font-mono">{{ row.statementNo }}</span>
        </template>
        <template #billingType="{ row }">
          <ElTag type="info">{{ formatBillingType(row.billingType) }}</ElTag>
        </template>
        <template #amount="{ row }">
          <div class="flex flex-col">
            <span class="font-semibold"
              >{{ $t('billing.field.amountDue') }}: {{ formatAmount(row.amountDue) }}</span
            >
            <span class="text-sm text-gray-500"
              >{{ $t('billing.field.amountPaid') }}: {{ formatAmount(row.amountPaid) }}</span
            >
          </div>
        </template>
        <template #status="{ row }">
          <BillingStatusTag :status="row.status" />
        </template>
        <template #dueDate="{ row }">
          {{ formatDateTime(row.dueDate) }}
        </template>
        <template #createdAt="{ row }">
          {{ formatDateTime(row.createdAt) }}
        </template>
        <template #action="{ row }">
          <div class="action-wrap">
            <button class="art-action-btn" @click="handleDetail(row)">
              <ArtSvgIcon icon="ri:information-line" class="art-action-icon" />
              <span>{{ t('dictionary.common.detail') }}</span>
            </button>
            <button class="art-action-btn info" @click="handleExportPdf(row)">
              <ArtSvgIcon icon="ri:file-pdf-2-line" class="art-action-icon" />
              <span>{{ $t('billing.action.exportPdf') }}</span>
            </button>
            <button
              v-if="row.status === BillingStatus.Pending || row.status === BillingStatus.Overdue"
              class="art-action-btn success"
              @click="handleMarkPaid(row)"
            >
              <ArtSvgIcon icon="ri:check-line" class="art-action-icon" />
              <span>{{ $t('billing.action.recordPayment') }}</span>
            </button>
            <button
              v-if="row.status === BillingStatus.Pending"
              class="art-action-btn warning"
              @click="handleCancel(row)"
            >
              <ArtSvgIcon icon="ri:close-line" class="art-action-icon" />
              <span>{{ $t('billing.action.cancel') }}</span>
            </button>
          </div>
        </template>
      </ArtTable>
    </ElCard>

    <!-- 3. 弹窗/抽屉 -->
    <BillingDetailDrawer
      ref="detailDrawerRef"
      @record-payment="handleMarkPaidByDrawer"
      @export-pdf="handleExportPdfByDrawer"
      @cancel="handleCancelByDrawer"
      @billing-updated="handleBillingUpdated"
    />
    <CreateBillingDialog ref="createDialogRef" @success="refreshData" />
    <RecordPaymentDialog ref="paymentDialogRef" @success="handleRecordPaymentSuccess" />
    <ExportDialog
      ref="exportDialogRef"
      :selected-rows="selectedRows"
      @export="handleExportConfirm"
    />
  </div>
</template>

<script setup lang="ts">
  import { computed, ref, reactive, onMounted } from 'vue'
  import { useI18n } from 'vue-i18n'
  import { ElMessage, ElMessageBox } from 'element-plus'
  import * as XLSX from 'xlsx'
  import FileSaver from 'file-saver'
  import ArtTable from '@/components/core/tables/art-table/index.vue'
  import ArtTableHeader from '@/components/core/tables/art-table-header/index.vue'
  import ArtSvgIcon from '@/components/core/base/art-svg-icon/index.vue'
  import BillingStatusTag from '@/components/billing/BillingStatusTag.vue'
  import BillingDetailDrawer from './components/BillingDetailDrawer.vue'
  import CreateBillingDialog from './components/CreateBillingDialog.vue'
  import RecordPaymentDialog from './components/RecordPaymentDialog.vue'
  import BatchActionToolbar from './components/BatchActionToolbar.vue'
  import ExportDialog from './components/ExportDialog.vue'
  import { useTable } from '@/hooks/core/useTable'
  import {
    batchUpdateStatus,
    fetchBillingDetail,
    fetchBillingList,
    fetchBillingListForExport
  } from '@/api/billing'
  import { fetchGetTenantList } from '@/api/tenant'
  import { BillingType } from '@/enums/Billing'
  import {
    formatAmount,
    formatBillingStatus,
    formatBillingType,
    formatDate,
    formatDateTime
  } from '@/utils/billing'
  import type { ColumnOption } from '@/types/component'
  import {
    ElButton,
    ElCard,
    ElCol,
    ElDatePicker,
    ElForm,
    ElFormItem,
    ElInput,
    ElInputNumber,
    ElOption,
    ElRow,
    ElSelect,
    ElTag
  } from 'element-plus'
  import type { Sort } from 'element-plus'

  defineOptions({ name: 'BillingList' })

  const { t } = useI18n()

  // 0. 搜索栏显示状态
  const showSearchBar = ref(true)

  // 1. 状态值（与后端枚举保持一致）
  const BillingStatus = {
    Pending: 0,
    Paid: 1,
    Overdue: 2,
    Cancelled: 3
  } as const

  type BillingStatusValue = (typeof BillingStatus)[keyof typeof BillingStatus]

  // 2. 筛选表单
  const formFilters = reactive({
    tenantId: '' as string,
    billingType: undefined as BillingType | undefined,
    status: undefined as BillingStatusValue | undefined,
    dateRange: [] as string[],
    minAmount: undefined as number | null | undefined,
    maxAmount: undefined as number | null | undefined,
    keyword: ''
  })

  // 3. 状态/类型选项（用于筛选与快捷标签）
  type TagType = 'primary' | 'success' | 'warning' | 'info' | 'danger'

  const statusOptions = computed(() => {
    return [
      {
        label: t('billing.status.pending'),
        value: BillingStatus.Pending,
        tagType: 'warning' as TagType
      },
      { label: t('billing.status.paid'), value: BillingStatus.Paid, tagType: 'success' as TagType },
      {
        label: t('billing.status.overdue'),
        value: BillingStatus.Overdue,
        tagType: 'danger' as TagType
      },
      {
        label: t('billing.status.cancelled'),
        value: BillingStatus.Cancelled,
        tagType: 'info' as TagType
      }
    ]
  })

  const billingTypeOptions = computed(() => {
    return [
      { label: t('billing.billingType.subscription'), value: BillingType.Subscription },
      { label: t('billing.billingType.quotaPurchase'), value: BillingType.QuotaPurchase },
      { label: t('billing.billingType.manual'), value: BillingType.Manual },
      { label: t('billing.billingType.renewal'), value: BillingType.Renewal }
    ]
  })

  // 4. 租户远程搜索
  const tenantLoading = ref(false)
  const tenantOptions = ref<Api.Tenant.TenantDto[]>([])

  const handleSearchTenant = async (query: string) => {
    if (!query?.trim()) return

    tenantLoading.value = true
    try {
      const res = await fetchGetTenantList({
        Keyword: query.trim(),
        Page: 1,
        PageSize: 20
      })
      tenantOptions.value = res.items || []
    } catch (error) {
      console.error('Search tenant error:', error)
    } finally {
      tenantLoading.value = false
    }
  }

  // 5. 表格列配置
  const columns = ref<ColumnOption<Api.Billing.BillingListDto>[]>([
    { type: 'selection', width: 50, fixed: 'left' },
    { type: 'globalIndex', label: t('table.column.index'), width: 70, fixed: 'left' },
    {
      label: t('billing.field.statementNo'),
      prop: 'statementNo',
      slotName: 'statementNo',
      useSlot: true,
      minWidth: 200,
      showOverflowTooltip: true
    },
    {
      label: t('billing.field.tenantName'),
      prop: 'tenantName',
      minWidth: 180,
      showOverflowTooltip: true
    },
    {
      label: t('billing.field.billingType'),
      prop: 'billingType',
      slotName: 'billingType',
      useSlot: true,
      width: 130
    },
    {
      label: t('billing.field.amount'),
      prop: 'amountDue',
      slotName: 'amount',
      useSlot: true,
      minWidth: 220,
      sortable: true,
      sortMethod: () => 0
    },
    {
      label: t('billing.field.status'),
      prop: 'status',
      slotName: 'status',
      useSlot: true,
      width: 110
    },
    {
      label: t('billing.field.dueDate'),
      prop: 'dueDate',
      slotName: 'dueDate',
      useSlot: true,
      width: 160,
      sortable: true,
      sortMethod: () => 0
    },
    {
      label: t('billing.field.createdAt'),
      prop: 'createdAt',
      slotName: 'createdAt',
      useSlot: true,
      width: 160,
      sortable: true,
      sortMethod: () => 0
    },
    {
      prop: 'action',
      label: t('table.column.action'),
      slotName: 'action',
      useSlot: true,
      width: 360,
      fixed: 'right'
    }
  ])

  // 5. 表格逻辑
  const {
    data,
    loading,
    pagination,
    handleCurrentChange,
    handleSizeChange,
    refreshData,
    searchParams,
    getData: getDataByPage
  } = useTable<typeof fetchBillingList, Api.Billing.BillingListDto>({
    core: {
      apiFn: fetchBillingList,
      apiParams: {},
      paginationKey: {
        current: 'PageNumber',
        size: 'PageSize'
      },
      columnsFactory: () => columns.value
    }
  })

  // 6. 搜索与重置
  const handleSearch = () => {
    const normalizeNumber = (value: number | null | undefined) => {
      if (value === null || value === undefined) return undefined
      return Number.isFinite(value) ? value : undefined
    }

    // 6.1 同步服务端查询参数（按 Swagger 字段直传）
    searchParams.TenantId = formFilters.tenantId || undefined
    searchParams.BillingType = formFilters.billingType
    searchParams.Status = formFilters.status
    searchParams.MinAmount = normalizeNumber(formFilters.minAmount)
    searchParams.MaxAmount = normalizeNumber(formFilters.maxAmount)
    searchParams.Keyword = formFilters.keyword.trim() || undefined

    if (formFilters.dateRange && formFilters.dateRange.length === 2) {
      searchParams.StartDate = formFilters.dateRange[0]
      searchParams.EndDate = formFilters.dateRange[1]
    } else {
      searchParams.StartDate = undefined
      searchParams.EndDate = undefined
    }

    // 6.2 搜索默认回到第一页（避免“筛选不生效”的错觉）
    void getDataByPage({ PageNumber: 1 })
  }

  const handleReset = () => {
    formFilters.tenantId = ''
    formFilters.billingType = undefined
    formFilters.status = undefined
    formFilters.dateRange = []
    formFilters.minAmount = undefined
    formFilters.maxAmount = undefined
    formFilters.keyword = ''
    handleSearch()
  }

  // 7.（日期/金额/状态/类型）统一走 utils/billing.ts

  // 9. 排序（服务端）
  const handleSortChange = (sort: Sort) => {
    // 1. 清空排序
    if (!sort.order || !sort.prop) {
      searchParams.SortBy = undefined
      searchParams.SortDesc = undefined
      void getDataByPage()
      return
    }

    // 2. 仅允许后端支持的字段
    const sortByMap: Record<string, string> = {
      createdAt: 'CreatedAt',
      dueDate: 'DueDate',
      amountDue: 'AmountDue'
    }

    const sortBy = sortByMap[sort.prop]
    if (!sortBy) {
      ElMessage.warning(t('billing.message.sortFieldNotSupported'))
      return
    }

    searchParams.SortBy = sortBy
    searchParams.SortDesc = sort.order === 'descending'
    void getDataByPage()
  }

  // 10. 快捷筛选（状态）
  const handleQuickStatus = (status: BillingStatusValue | undefined) => {
    formFilters.status = status
    handleSearch()
  }

  // 11. 弹窗引用与表格选择
  const tableRef = ref<InstanceType<typeof ArtTable> | null>(null)
  const detailDrawerRef = ref<InstanceType<typeof BillingDetailDrawer> | null>(null)
  const createDialogRef = ref<InstanceType<typeof CreateBillingDialog> | null>(null)
  const paymentDialogRef = ref<InstanceType<typeof RecordPaymentDialog> | null>(null)
  const exportDialogRef = ref<InstanceType<typeof ExportDialog> | null>(null)

  const selectedRows = ref<Api.Billing.BillingListDto[]>([])
  const isExporting = ref(false)
  const isBatchOperating = ref(false)

  const handleSelectionChange = (rows: Api.Billing.BillingListDto[]) => {
    selectedRows.value = rows
  }

  // 12. 页面操作（详情 / 创建 / 支付 / 取消）
  const handleRowDblClick = (row: Api.Billing.BillingListDto) => {
    handleDetail(row)
  }

  const handleDetail = (row: Api.Billing.BillingListDto) => {
    detailDrawerRef.value?.open(row.id)
  }

  const handleCreate = () => {
    createDialogRef.value?.open()
  }

  const handleMarkPaid = (row: Api.Billing.BillingListDto) => {
    const remainAmount = Math.max(0, (row.amountDue || 0) - (row.amountPaid || 0))
    paymentDialogRef.value?.open(row.id, remainAmount)
  }

  const handleMarkPaidByDrawer = (billingId: string, remainAmount: number) => {
    paymentDialogRef.value?.open(billingId, remainAmount)
  }

  // 12.3 收款成功回调：同步刷新列表与详情
  const handleRecordPaymentSuccess = async (billingId: string) => {
    // 1. 优先局部更新（避免全表刷新）
    await handleBillingUpdated(billingId)

    // 2. 再触发一次全表刷新：确保列表视图与服务端状态完全一致（等价于用户手动刷新页面）
    await refreshData()

    // 3. 若详情抽屉正在展示该账单，同步刷新详情
    void detailDrawerRef.value?.refresh(billingId)
  }

  // 12.4 账单更新回调：从详情获取最新数据并局部更新列表
  const handleBillingUpdated = async (payload: string | Api.Billing.BillingDetailDto) => {
    try {
      // 1. 获取最新账单详情（优先使用子组件传回的最新快照，避免二次请求不一致）
      const detail = typeof payload === 'string' ? await fetchBillingDetail(payload) : payload

      // 2. 局部更新列表中对应的行（避免全表刷新）
      const row = data.value.find((r) => r.id === detail.id)
      if (row) {
        row.status = detail.status
        row.amountPaid = detail.amountPaid
        row.amountDue = detail.amountDue
        row.updatedAt = detail.updatedAt

        // 3. 触发表格刷新：部分表格实现仅在 data 引用变化时更新渲染
        data.value = data.value.slice()
      } else {
        // 4. 若当前页不包含该账单（或数据源被替换），降级为全表刷新
        void refreshData()
      }
    } catch (error) {
      console.error('Update billing row error:', error)
      // 如果详情获取失败，降级为全表刷新
      void refreshData()
    }
  }

  // 13. PDF导出（前端打印：另存为 PDF）
  const exportToPdf = async (billId: string) => {
    // ✅ 关键修复1：先同步打开窗口（在用户手势上下文中）
    // ✅ 关键修复2：移除 noreferrer 避免返回 null
    // 0. 入参校验（避免空 ID 导致静默失败）
    if (!billId) {
      ElMessage.error(t('billing.message.exportFailed'))
      return
    }

    const printWindow = (() => {
      try {
        return window.open('', '_blank')
      } catch {
        return null
      }
    })()

    if (!printWindow) {
      ElMessage.error(t('billing.message.exportPdfPopupBlocked'))
      return
    }

    // ✅ 显示美化的 loading 占位内容
    // 2. 安全：防止 tabnabbing（仍保留窗口引用用于写入/打印）
    try {
      printWindow.opener = null
    } catch {
      // ignore
    }

    printWindow.document.write(`
      <!DOCTYPE html>
      <html>
        <head>
          <meta charset="UTF-8">
          <title>Loading...</title>
          <style>
            body {
              display: flex;
              align-items: center;
              justify-content: center;
              height: 100vh;
              margin: 0;
              font-family: system-ui, -apple-system, sans-serif;
              background: #f5f5f5;
            }
            .loading { text-align: center; }
            .spinner {
              width: 40px;
              height: 40px;
              margin: 0 auto 16px;
              border: 4px solid #f3f3f3;
              border-top: 4px solid #409EFF;
              border-radius: 50%;
              animation: spin 1s linear infinite;
            }
            @keyframes spin {
              0% { transform: rotate(0deg); }
              100% { transform: rotate(360deg); }
            }
          </style>
        </head>
        <body>
          <div class="loading">
            <div class="spinner"></div>
            <div>${t('billing.message.generatingPdf')}</div>
          </div>
        </body>
      </html>
    `)

    isExporting.value = true
    try {
      // ✅ 异步获取数据（窗口已打开，不会被拦截）
      const detail = await fetchBillingDetail(billId)

      // ✅ 边界检查：用户可能已手动关闭窗口
      if (printWindow.closed) {
        ElMessage.info(t('billing.message.exportCancelled'))
        return
      }

      const html = buildBillPrintHtml(detail)

      // ✅ 替换内容
      printWindow.document.open()
      printWindow.document.write(html)
      printWindow.document.close()

      // ✅ 等待资源加载完成后打印（兼容性更好）
      setTimeout(() => {
        if (!printWindow.closed) {
          printWindow.focus()
          printWindow.print()
        }
      }, 200)

      ElMessage.success(t('billing.message.exportPdfOpened'))
    } catch (error) {
      console.error('Export PDF error:', error)

      // ✅ 失败时关闭窗口并清理
      if (!printWindow.closed) {
        printWindow.close()
      }

      ElMessage.error(t('billing.message.exportFailed'))
    } finally {
      isExporting.value = false
    }
  }

  const handleCancelByDrawer = (billingId: string) => {
    const row = data.value.find((x) => x.id === billingId)
    if (row) void handleCancel(row)
  }

  const handleExportPdfByDrawer = (billingId: string) => {
    if (!billingId) {
      ElMessage.error(t('billing.message.exportFailed'))
      return
    }
    void exportToPdf(billingId)
  }

  const handleExportPdf = (row: Api.Billing.BillingListDto) => {
    if (!row?.id) {
      ElMessage.error(t('billing.message.exportFailed'))
      return
    }
    void exportToPdf(row.id)
  }

  // 12.1 打开导出对话框
  const handleOpenExportDialog = () => {
    exportDialogRef.value?.open()
  }

  // 12.2 导出确认处理
  const handleExportConfirm = async (config: {
    format: 'excel' | 'pdf' | 'csv'
    scope: 'current' | 'selected' | 'all'
    fields: string[]
    dateRange?: string[]
  }) => {
    try {
      isExporting.value = true

      let rowsToExport: Api.Billing.BillingListDto[] = []

      // 1. 根据范围获取数据
      if (config.scope === 'current') {
        rowsToExport = data.value
      } else if (config.scope === 'selected') {
        rowsToExport = selectedRows.value
      } else {
        // 2. 全部数据（按当前筛选条件拉取）
        const exportParams: Partial<Api.Billing.BillingListParams> = {
          TenantId: searchParams.TenantId as string | undefined,
          BillingType: searchParams.BillingType as Api.Billing.BillingType | undefined,
          Status: searchParams.Status as Api.Billing.TenantBillingStatus | undefined,
          StartDate: searchParams.StartDate as string | undefined,
          EndDate: searchParams.EndDate as string | undefined,
          MinAmount: searchParams.MinAmount as number | undefined,
          MaxAmount: searchParams.MaxAmount as number | undefined,
          Keyword: searchParams.Keyword as string | undefined,
          SortBy: searchParams.SortBy as string | undefined,
          SortDesc: searchParams.SortDesc as boolean | undefined
        }

        if (config.dateRange && config.dateRange.length === 2) {
          exportParams.StartDate = config.dateRange[0]
          exportParams.EndDate = config.dateRange[1]
        }

        rowsToExport = await fetchBillingListForExport(exportParams, {
          pageSize: 200,
          maxRows: 10000
        })
      }

      if (rowsToExport.length === 0) {
        ElMessage.warning(t('billing.message.exportNoData'))
        return
      }

      // 3. 根据格式导出
      if (config.format === 'excel') {
        await exportToExcel(rowsToExport, config.fields)
      } else if (config.format === 'pdf') {
        if (rowsToExport.length !== 1) {
          ElMessage.warning(t('billing.message.exportPdfNeedSingleSelection'))
          return
        }
        await exportToPdf(rowsToExport[0].id)
      } else if (config.format === 'csv') {
        await exportToCsv(rowsToExport, config.fields)
      }
    } catch (error) {
      console.error('Export error:', error)
      ElMessage.error(t('billing.message.exportFailed'))
    } finally {
      isExporting.value = false
    }
  }

  // 13. Excel导出
  const exportToExcel = async (rows: Api.Billing.BillingListDto[], fields: string[]) => {
    const fieldMap: Record<string, string> = {
      statementNo: t('billing.field.statementNo'),
      tenantName: t('billing.field.tenantName'),
      billingType: t('billing.field.billingType'),
      amountDue: t('billing.field.amountDue'),
      amountPaid: t('billing.field.amountPaid'),
      status: t('billing.field.status'),
      dueDate: t('billing.field.dueDate'),
      periodStart: t('billing.field.periodStart'),
      periodEnd: t('billing.field.periodEnd'),
      createdAt: t('billing.field.createdAt')
    }

    const sheetData = rows.map((row) => {
      const rowData: Record<string, unknown> = {}
      fields.forEach((field) => {
        const label = fieldMap[field]
        if (!label) return

        switch (field) {
          case 'statementNo':
            rowData[label] = row.statementNo
            break
          case 'tenantName':
            rowData[label] = row.tenantName || '-'
            break
          case 'billingType':
            rowData[label] = formatBillingType(row.billingType)
            break
          case 'amountDue':
            rowData[label] = row.amountDue
            break
          case 'amountPaid':
            rowData[label] = row.amountPaid
            break
          case 'status':
            rowData[label] = formatBillingStatus(row.status)
            break
          case 'dueDate':
            rowData[label] = formatDateTime(row.dueDate)
            break
          case 'periodStart':
            rowData[label] = formatDate(row.periodStart)
            break
          case 'periodEnd':
            rowData[label] = formatDate(row.periodEnd)
            break
          case 'createdAt':
            rowData[label] = formatDateTime(row.createdAt)
            break
        }
      })
      return rowData
    })

    const worksheet = XLSX.utils.json_to_sheet(sheetData)
    const workbook = XLSX.utils.book_new()
    XLSX.utils.book_append_sheet(workbook, worksheet, t('billing.export.sheetName'))
    const buffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' })
    const blob = new Blob([buffer], {
      type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    })

    const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
    FileSaver.saveAs(blob, `${t('billing.export.fileName')}_${timestamp}.xlsx`)

    ElMessage.success(t('billing.message.exportExcelSuccess', { count: rows.length }))
  }

  // 14. CSV导出
  const exportToCsv = async (rows: Api.Billing.BillingListDto[], fields: string[]) => {
    const fieldMap: Record<string, string> = {
      statementNo: t('billing.field.statementNo'),
      tenantName: t('billing.field.tenantName'),
      billingType: t('billing.field.billingType'),
      amountDue: t('billing.field.amountDue'),
      amountPaid: t('billing.field.amountPaid'),
      status: t('billing.field.status'),
      dueDate: t('billing.field.dueDate'),
      periodStart: t('billing.field.periodStart'),
      periodEnd: t('billing.field.periodEnd'),
      createdAt: t('billing.field.createdAt')
    }

    const headers = fields
      .map((field) => fieldMap[field])
      .filter(Boolean)
      .join(',')
    const csvRows = rows.map((row) => {
      return fields
        .map((field) => {
          let value = ''
          switch (field) {
            case 'statementNo':
              value = row.statementNo
              break
            case 'tenantName':
              value = row.tenantName || '-'
              break
            case 'billingType':
              value = formatBillingType(row.billingType)
              break
            case 'amountDue':
              value = String(row.amountDue)
              break
            case 'amountPaid':
              value = String(row.amountPaid)
              break
            case 'status':
              value = formatBillingStatus(row.status)
              break
            case 'dueDate':
              value = formatDateTime(row.dueDate)
              break
            case 'periodStart':
              value = formatDate(row.periodStart)
              break
            case 'periodEnd':
              value = formatDate(row.periodEnd)
              break
            case 'createdAt':
              value = formatDateTime(row.createdAt)
              break
          }
          return `"${value}"`
        })
        .join(',')
    })

    const csv = [headers, ...csvRows].join('\n')
    const blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8;' })

    const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
    FileSaver.saveAs(blob, `${t('billing.export.fileName')}_${timestamp}.csv`)

    ElMessage.success(t('billing.message.exportSuccess', { count: rows.length }))
  }

  // 16. 批量操作
  const clearTableSelection = () => {
    tableRef.value?.elTableRef?.clearSelection?.()
    selectedRows.value = []
  }

  const handleBatchConfirmPayment = async () => {
    if (isBatchOperating.value) return
    const pendingRows = selectedRows.value.filter((row) => row.status === BillingStatus.Pending)
    if (pendingRows.length === 0) {
      ElMessage.warning(t('billing.message.batchNoPending'))
      return
    }

    try {
      isBatchOperating.value = true

      await batchUpdateStatus({
        billingIds: pendingRows.map((x) => x.id),
        newStatus: BillingStatus.Paid,
        notes: t('billing.message.batchMarkPaidNotes')
      })

      ElMessage.success(t('billing.message.batchMarkPaidSuccess', { count: pendingRows.length }))
      clearTableSelection()

      // 1. 等待刷新完成，确保列表状态立刻同步
      await refreshData()
    } catch (error) {
      console.error('Batch confirm payment error:', error)
    } finally {
      isBatchOperating.value = false
    }
  }

  const handleBatchCancel = async () => {
    if (isBatchOperating.value) return
    const pendingRows = selectedRows.value.filter((row) => row.status === BillingStatus.Pending)
    if (pendingRows.length === 0) {
      ElMessage.warning(t('billing.message.batchNoPending'))
      return
    }

    try {
      isBatchOperating.value = true

      await batchUpdateStatus({
        billingIds: pendingRows.map((x) => x.id),
        newStatus: BillingStatus.Cancelled,
        notes: t('billing.message.batchCancelNotes')
      })

      ElMessage.success(t('billing.message.batchCancelSuccess', { count: pendingRows.length }))
      clearTableSelection()

      // 1. 等待刷新完成，确保列表状态立刻同步
      await refreshData()
    } catch (error) {
      console.error('Batch cancel error:', error)
    } finally {
      isBatchOperating.value = false
    }
  }

  const handleCancel = async (row: Api.Billing.BillingListDto) => {
    try {
      await ElMessageBox.confirm(t('billing.message.cancelConfirm'), t('common.warning'), {
        confirmButtonText: t('common.confirm'),
        cancelButtonText: t('common.cancel'),
        type: 'warning'
      })

      // 1. 批量接口可复用单条：统一入口
      await batchUpdateStatus({
        billingIds: [row.id],
        newStatus: BillingStatus.Cancelled,
        notes: t('billing.message.cancelByAdmin')
      })

      ElMessage.success(t('billing.message.cancelSuccess'))

      // 2. 等待刷新完成，确保列表状态立刻同步
      await refreshData()
    } catch (error) {
      // 2. 用户取消操作
      if (error !== 'cancel') {
        console.error('Cancel bill error:', error)
      }
    }
  }

  // 11. 打印 HTML 构建（用于 PDF 导出）
  const escapeHtml = (value: string) => {
    return value
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;')
  }

  const buildBillPrintHtml = (detail: Api.Billing.BillingDetailDto) => {
    const paymentsHtml = (detail.payments || [])
      .map((p) => {
        return `
          <tr>
            <td>${escapeHtml(formatAmount(p.amount))}</td>
            <td>${escapeHtml(String(p.transactionNo || '-'))}</td>
            <td>${escapeHtml(String(p.proofUrl || '-'))}</td>
            <td>${escapeHtml(String(p.paidAt ? formatDateTime(p.paidAt) : '-'))}</td>
            <td>${escapeHtml(String(p.notes || '-'))}</td>
          </tr>
        `
      })
      .join('')

    const title = `${t('billing.drawer.title')} - ${detail.statementNo}`

    return `<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>${escapeHtml(title)}</title>
    <style>
      @page { size: A4; margin: 12mm; }
      body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif; color: #111827; }
      h1 { font-size: 18px; margin: 0 0 12px; }
      h2 { font-size: 14px; margin: 18px 0 8px; }
      .meta { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 16px; font-size: 12px; }
      .meta .item { display: flex; gap: 6px; }
      .meta .label { color: #6B7280; min-width: 92px; }
      .meta .value { color: #111827; word-break: break-word; }
      table { width: 100%; border-collapse: collapse; font-size: 12px; }
      th, td { border: 1px solid #E5E7EB; padding: 8px; vertical-align: top; }
      th { background: #F9FAFB; text-align: left; }
      .muted { color: #6B7280; }
      .nowrap { white-space: nowrap; }
    </style>
  </head>
  <body>
    <h1>${escapeHtml(title)}</h1>

    <h2>${escapeHtml(t('billing.drawer.basicInfo'))}</h2>
    <div class="meta">
      <div class="item"><div class="label">${escapeHtml(t('billing.field.statementNo'))}</div><div class="value">${escapeHtml(detail.statementNo)}</div></div>
      <div class="item"><div class="label">${escapeHtml(t('billing.field.tenantName'))}</div><div class="value">${escapeHtml(detail.tenantName || '-')}</div></div>
      <div class="item"><div class="label">${escapeHtml(t('billing.field.status'))}</div><div class="value">${escapeHtml(formatBillingStatus(detail.status))}</div></div>
      <div class="item"><div class="label">${escapeHtml(t('billing.field.dueDate'))}</div><div class="value">${escapeHtml(formatDateTime(detail.dueDate))}</div></div>
      <div class="item"><div class="label">${escapeHtml(t('billing.field.amountDue'))}</div><div class="value">${escapeHtml(formatAmount(detail.amountDue))}</div></div>
      <div class="item"><div class="label">${escapeHtml(t('billing.field.amountPaid'))}</div><div class="value">${escapeHtml(formatAmount(detail.amountPaid))}</div></div>
      <div class="item"><div class="label">${escapeHtml(t('billing.field.periodStart'))}</div><div class="value">${escapeHtml(formatDate(detail.periodStart))}</div></div>
      <div class="item"><div class="label">${escapeHtml(t('billing.field.periodEnd'))}</div><div class="value">${escapeHtml(formatDate(detail.periodEnd))}</div></div>
      <div class="item"><div class="label">${escapeHtml(t('billing.field.createdAt'))}</div><div class="value">${escapeHtml(formatDateTime(detail.createdAt))}</div></div>
    </div>

    <h2>${escapeHtml(t('billing.drawer.paymentList'))}</h2>
    <table>
      <thead>
        <tr>
          <th class="nowrap">${escapeHtml(t('billing.payment.amount'))}</th>
          <th>${escapeHtml(t('billing.payment.transactionNo'))}</th>
          <th>${escapeHtml(t('billing.payment.proofUrl'))}</th>
          <th class="nowrap">${escapeHtml(t('billing.payment.paidAt'))}</th>
          <th>${escapeHtml(t('billing.payment.notes'))}</th>
        </tr>
      </thead>
      <tbody>
        ${paymentsHtml || `<tr><td colspan="5" class="muted">${escapeHtml(t('billing.export.noPayments'))}</td></tr>`}
      </tbody>
    </table>
  </body>
</html>`
  }

  // 12. 初始化
  onMounted(() => {
    refreshData()
  })
</script>

<style scoped lang="scss">
  .art-page-view {
    padding: 16px;
  }

  .art-search-card {
    margin-bottom: 16px;
  }

  .art-table-card {
    :deep(.el-card__body) {
      padding: 16px;
    }
  }

  .action-wrap {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
  }

  .art-action-btn {
    display: inline-flex;
    gap: 6px;
    align-items: center;
    min-width: 64px;
    padding: 6px 12px;
    font-size: 13px;
    line-height: 1;
    color: var(--el-text-color-primary);
    cursor: pointer;
    background: var(--el-fill-color-light);
    border: 1px solid var(--el-border-color-lighter);
    border-radius: 10px;
  }

  .art-action-btn .art-action-icon {
    display: flex;
    font-size: 16px;
  }

  .art-action-btn.primary {
    color: var(--el-color-primary);
    background: var(--el-color-primary-light-9);
    border-color: var(--el-color-primary-light-8);
  }

  .art-action-btn.success {
    color: var(--el-color-success);
    background: var(--el-color-success-light-9);
    border-color: var(--el-color-success-light-8);
  }

  .art-action-btn.warning {
    color: var(--el-color-warning);
    background: var(--el-color-warning-light-9);
    border-color: var(--el-color-warning-light-8);
  }

  .art-action-btn.info {
    color: var(--el-color-info);
    background: var(--el-color-info-light-9);
    border-color: var(--el-color-info-light-8);
  }

  .art-action-btn.danger {
    color: var(--el-color-danger);
    background: var(--el-color-danger-light-9);
    border-color: var(--el-color-danger-light-8);
  }

  .art-action-btn:hover {
    filter: brightness(0.97);
    transition: all 0.15s ease;
  }
</style>
