<!-- SPEC: PJ2026-010405 云端控制台 / Billing. -->
<!-- Implementation reference: draft-2026-07-13-p0-cloud-console. -->
<script setup lang="ts">
import { ChartBar, CreditCard } from "lucide-vue-next";
import AsyncBoundary from "@/components/common/AsyncBoundary.vue";
import BaseDialog from "@/components/common/BaseDialog.vue";
import DataGrid from "@/components/common/DataGrid.vue";
import EmptyState from "@/components/common/EmptyState.vue";
import ResourceCollection from "@/components/common/ResourceCollection.vue";
import BillingCodeIndex from "@/components/billing/BillingCodeIndex.vue";
import BillingCodeInspector from "@/components/billing/BillingCodeInspector.vue";
import OperationsWorkspace from "@/components/console/OperationsWorkspace.vue";
import ViewModeSwitch from "@/components/console/ViewModeSwitch.vue";
import AuditTimeline from "@/components/console/AuditTimeline.vue";
import PageCommandBar from "@/components/layout/PageCommandBar.vue";
import SectionFrame from "@/components/layout/SectionFrame.vue";
import StatusStrip from "@/components/layout/StatusStrip.vue";
import { useBillingWorkspace } from "@/composables/admin/useBillingWorkspace";

const {
  codes, redemptions, loading, error, pending, selectedCodeId, createOpen,
  createdCode, filters, form, activeTab, viewMode, selectedCode, asyncState,
  statusItems, codeColumns, redemptionColumns, refreshAll, persistFilters,
  selectCode, closeInspector, setStatus, createCode, codeKey, redemptionKey,
  formatNumber, formatDate,
} = useBillingWorkspace();
</script>

<template>
  <section class="route-stack billing-operations-page">
    <PageCommandBar
      eyebrow="Admin"
      title="兑换运营与账本审计"
      description="兑换码与 redemption 使用同一稳定集合合同；创建动作进入 Dialog。"
      ><template #actions
        ><ViewModeSwitch
          v-model="viewMode"
          :options="[
            { value: 'summary', label: '运营摘要', icon: CreditCard },
            { value: 'detail', label: '账本明细', icon: ChartBar },
          ]"
        /><button
          class="table-action"
          type="button"
          @click="
            createdCode = null;
            createOpen = true;
          "
        >
          创建兑换码
        </button></template
      ></PageCommandBar
    >
    <AsyncBoundary
      :state="asyncState"
      :has-content="Boolean(codes.length || redemptions.length)"
      label="加载兑换运营"
      @retry="refreshAll"
      ><template #error
        ><EmptyState
          title="兑换运营加载失败"
          :description="error || '请求未完成'"
          action-label="重试"
          @action="refreshAll"
      /></template>
      <StatusStrip :items="statusItems" />
      <OperationsWorkspace
        class="billing-operations"
        label="兑换运营工作区"
        :inspector-open="Boolean(selectedCode)"
      >
        <template #index><BillingCodeIndex :codes="codes" :selected-id="selectedCode ? codeKey(selectedCode) : null" :status="filters.status" @select="selectCode" @update:status="(value) => { filters.status = value; persistFilters(); }" /></template>
        <div class="billing-main-stack">
          <nav class="operations-tabs" role="tablist" aria-label="兑换运营数据">
            <button
              type="button"
              role="tab"
              :aria-selected="activeTab === 'codes'"
              @click="activeTab = 'codes'"
            >
              Codes</button
            ><button
              type="button"
              role="tab"
              :aria-selected="activeTab === 'redemptions'"
              @click="activeTab = 'redemptions'"
            >
              Redemptions
            </button>
          </nav>
          <template v-if="activeTab === 'codes'"
            ><SectionFrame
              v-if="viewMode === 'summary'"
              title="兑换运营摘要"
              description="状态、额度、使用率与到期时间可直接扫描。"
              :body-padding="false"
              ><ResourceCollection class="billing-summary-grid" :items="codes" :item-key="codeKey" view="cards" density="compact" :selected-id="selectedCodeId" :show-controls="false" empty-text="暂无兑换码。" aria-label="兑换码运营摘要" @select="selectCode">
                <template #card="{ item: code }"><button type="button" @click="selectCode(code)">
                  <span class="status-pill" :data-status="code.status">{{ code.status }}</span><strong>{{ code.codePrefix }}</strong><small>{{ code.displayName || code.reason }}</small>
                  <dl><div><dt>Credits</dt><dd>{{ formatNumber(code.creditAmount) }}</dd></div><div><dt>Usage</dt><dd>{{ formatNumber(code.redeemedCount) }}/{{ formatNumber(code.maxRedemptions) }}</dd></div><div><dt>Expires</dt><dd>{{ formatDate(code.expiresAt) }}</dd></div></dl>
                </button></template>
              </ResourceCollection></SectionFrame
            ><SectionFrame
              v-else
              title="兑换码紧凑清单"
              description="同一 RedeemCodeSummary 集合的高密度表格。"
              :body-padding="false"
              ><DataGrid
                :columns="codeColumns"
                :rows="codes"
                :row-key="codeKey"
                :selected-id="selectedCodeId"
                aria-label="兑换码清单"
                @select="selectCode"
                ><template #cell-code="{ row }"
                  ><code>{{ row.codePrefix }}</code
                  ><small>{{ row.displayName || row.id }}</small></template
                ><template #cell-status="{ row }"
                  ><span class="status-pill" :data-status="row.status">{{
                    row.status
                  }}</span></template
                ><template #cell-credits="{ row }">{{
                  formatNumber(row.creditAmount)
                }}</template
                ><template #cell-usage="{ row }"
                  >{{ formatNumber(row.redeemedCount) }} /
                  {{ formatNumber(row.maxRedemptions) }}</template
                ><template #cell-expires="{ row }">{{
                  formatDate(row.expiresAt)
                }}</template
                ><template #cell-actions="{ row }"
                  ><button
                    class="table-action"
                    type="button"
                    :disabled="pending === `status:${row.id}`"
                    @click.stop="setStatus(row)"
                  >
                    {{ row.status === "disabled" ? "启用" : "禁用" }}
                  </button></template
                ></DataGrid
              ></SectionFrame
            ></template
          >
          <template v-else
            ><div class="billing-filters">
              <input
                v-model="filters.userId"
                class="input"
                placeholder="user id"
                @keyup.enter="persistFilters"
              /><input
                v-model="filters.codeId"
                class="input"
                placeholder="code id"
                @keyup.enter="persistFilters"
              /><button
                class="table-action"
                type="button"
                @click="persistFilters"
              >
                过滤
              </button>
            </div>
            <SectionFrame
              v-if="viewMode === 'summary'"
              title="兑换时间线"
              description="同一 RedeemRedemptionSummary 集合的时间序列视图。"
              ><AuditTimeline :items="redemptions" :item-key="redemptionKey"
                ><template #item="{ item }"
                  ><strong
                    >{{ item.codePrefix || item.codeId }} ·
                    {{ item.userId }}</strong
                  ><small
                    >{{ formatDate(item.createdAt) }} · {{ item.status }} ·
                    {{ item.id }}</small
                  ><small>{{ item.ledgerId || "no ledger" }}</small></template
                ></AuditTimeline
              ></SectionFrame
            ><SectionFrame
              v-else
              title="兑换紧凑清单"
              description="同一 RedeemRedemptionSummary 集合的高密度表格。"
              :body-padding="false"
              ><DataGrid
                :columns="redemptionColumns"
                :rows="redemptions"
                :row-key="redemptionKey"
                :interactive="false"
                aria-label="兑换记录清单"
                ><template #cell-time="{ row }"
                  >{{ formatDate(row.createdAt)
                  }}<small>{{ row.id }}</small></template
                ><template #cell-user="{ row }"
                  ><code>{{ row.userId }}</code></template
                ><template #cell-code="{ row }"
                  ><code>{{ row.codePrefix }}</code
                  ><small>{{ row.codeId }}</small></template
                ><template #cell-credits="{ row }">{{
                  formatNumber(row.creditAmount)
                }}</template
                ><template #cell-status="{ row }"
                  ><span class="status-pill" :data-status="row.status">{{
                    row.status
                  }}</span></template
                ><template #cell-ledger="{ row }"
                  ><code>{{ row.ledgerId || "-" }}</code></template
                ></DataGrid
              ></SectionFrame
            ></template
          >
        </div>
        <template #inspector><BillingCodeInspector v-if="selectedCode" :code="selectedCode" :pending="pending === `status:${selectedCode.id}`" @close="closeInspector" @toggle="setStatus" /></template>
      </OperationsWorkspace>
      <BaseDialog
        :open="createOpen"
        title="创建兑换码"
        description="完整 code 仅在本次创建响应中显示"
        :close-on-backdrop="!pending && !createdCode"
        @close="
          createdCode = null;
          createOpen = false;
        "
        ><div class="form-grid two-col">
          <label>名称<input v-model="form.displayName" class="input" /></label
          ><label
            >Credits<input
              v-model.number="form.creditAmount"
              class="input"
              type="number" /></label
          ><label>Reason<input v-model="form.reason" class="input" /></label
          ><label
            >Max redemptions<input
              v-model.number="form.maxRedemptions"
              class="input"
              type="number" /></label
          ><label
            >Per user<input
              v-model.number="form.perUserLimit"
              class="input"
              type="number" /></label
          ><label
            >Expires<input
              v-model="form.expiresAt"
              class="input"
              type="datetime-local"
          /></label>
        </div>
        <p v-if="createdCode" class="created-redeem-code">
          <strong>立即保存完整兑换码</strong><code>{{ createdCode }}</code
          ><small>仅此响应可见；关闭后无法再次读取完整值。</small>
        </p>
        <template #footer
          ><button
            class="table-action"
            type="button"
            @click="
              createdCode = null;
              createOpen = false;
            "
          >
            {{ createdCode ? "已保存并关闭" : "取消" }}</button
          ><button
            v-if="!createdCode"
            class="table-action"
            type="button"
            :disabled="pending === 'create'"
            @click="createCode"
          >
            {{ pending === "create" ? "创建中" : "创建" }}
          </button></template
        ></BaseDialog
      >
    </AsyncBoundary>
  </section>
</template>

<style src="./BillingView.css"></style>
