<!-- SPEC: PJ2026-010405 云端控制台 / Users. -->
<!-- Implementation reference: draft-2026-07-13-p0-cloud-console. -->
<script setup lang="ts">
import InspectorPanel from "@/components/console/InspectorPanel.vue";
import AuditTimeline from "@/components/console/AuditTimeline.vue";
import type { AdminBillingUserSummary, ApiKeyRecord, AuthUser, BillingPlanDetail, ResourceEntitlementSummary, UsageLedgerRow, UsageReservationRow } from "@/types";

type DetailTab = "profile" | "credits" | "entitlements" | "keys" | "reservations" | "ledger";
interface EditForm { email: string; username: string; displayName: string; password: string; role: string; status: string; planId: string }
interface CreditForm { deltaCredits: number; reason: string; idempotencyKey: string }

defineProps<{ userId: string; user: AuthUser | null; summary: AdminBillingUserSummary | null; plans: BillingPlanDetail[]; tab: string; loading: boolean; error: string | null; pending: string | null; editForm: EditForm; creditForm: CreditForm; entitlements: ResourceEntitlementSummary[]; apiKeys: ApiKeyRecord[]; reservations: UsageReservationRow[]; ledgerRows: UsageLedgerRow[] }>();
const emit = defineEmits<{ close: []; tab: [tab: string]; save: []; adjust: [] }>();
const formatNumber = (value: unknown): string => Number(value ?? 0).toLocaleString();
function formatDate(value: string | null | undefined): string { if (!value) return "-"; const date = new Date(value); return Number.isNaN(date.getTime()) ? value : date.toLocaleString(); }
const formatSigned = (value: unknown): string => { const number = Number(value ?? 0); return `${number > 0 ? "+" : ""}${number.toLocaleString()}`; };
const auditKey = (item: UsageLedgerRow): string => item.id;
</script>

<template>
  <InspectorPanel :title="user?.displayName || user?.username || userId" :subtitle="userId" @close="emit('close')">
    <template #tabs><button v-for="item in ['profile', 'credits', 'entitlements', 'keys', 'reservations', 'ledger'] as DetailTab[]" :key="item" type="button" role="tab" :aria-selected="tab === item" @click="emit('tab', item)">{{ item }}</button></template>
    <p v-if="loading" class="muted-box">详情加载中…</p><p v-else-if="error" class="form-error">{{ error }}</p>
    <div v-if="!loading && tab === 'profile'" class="form-grid"><label>邮箱<input v-model="editForm.email" class="input" /></label><label>用户名<input v-model="editForm.username" class="input" /></label><label>显示名<input v-model="editForm.displayName" class="input" /></label><label>新密码<input v-model="editForm.password" class="input" type="password" /></label><label>角色<select v-model="editForm.role" class="input"><option value="user">user</option><option value="admin">admin</option></select></label><label>状态<select v-model="editForm.status" class="input"><option value="active">active</option><option value="pending">pending</option><option value="disabled">disabled</option></select></label><label>Plan<select v-model="editForm.planId" class="input"><option v-for="item in plans" :key="item.plan?.id" :value="item.plan?.id">{{ item.plan?.displayName || item.plan?.id }}</option></select></label><button class="table-action" type="button" :disabled="pending === `save:${userId}`" @click="emit('save')">保存资料</button></div>
    <div v-else-if="tab === 'credits'" class="form-grid"><p class="muted-box">Available {{ formatNumber(summary?.credits?.available) }} · Reserved {{ formatNumber(summary?.credits?.reserved) }}</p><label>Delta<input v-model.number="creditForm.deltaCredits" class="input" type="number" /></label><label>Reason<input v-model="creditForm.reason" class="input" /></label><label>Idempotency key<input v-model="creditForm.idempotencyKey" class="input" /></label><button class="table-action" type="button" :disabled="pending === `credits:${userId}`" @click="emit('adjust')">调整 Credits</button></div>
    <div v-else-if="tab === 'entitlements'" class="inspector-fact-list"><article v-for="item in entitlements" :key="item.id || item.resourceType"><strong>{{ item.resourceType }}</strong><small>{{ item.enabled === false ? "disabled" : "enabled" }} · monthly {{ formatNumber(item.monthlyUsageCredits) }} / {{ item.monthlyQuotaUnlimited ? "unlimited" : formatNumber(item.monthlyQuotaCredits) }}</small><small>concurrent {{ formatNumber(item.activeReservations) }} / {{ item.concurrencyUnlimited ? "unlimited" : formatNumber(item.maxConcurrentReservations) }} · RPM {{ formatNumber(item.rpmUsed) }} / {{ item.rpmUnlimited ? "unlimited" : formatNumber(item.rpmLimit) }}</small></article><p v-if="!entitlements.length" class="muted-box">暂无 entitlement。</p></div>
    <div v-else-if="tab === 'keys'" class="inspector-fact-list"><article v-for="item in apiKeys" :key="item.id"><strong>{{ item.name || item.id }}</strong><small>{{ item.prefix || "-" }} · {{ item.revokedAt ? "revoked" : "active" }}</small><small>created {{ formatDate(item.createdAt) }} · last used {{ formatDate(item.lastUsedAt) }}</small></article><p v-if="!apiKeys.length" class="muted-box">暂无 API key。</p></div>
    <div v-else-if="tab === 'reservations'" class="inspector-fact-list"><article v-for="item in reservations" :key="item.reservationId"><strong>{{ item.reservationId }}</strong><small>{{ item.serviceId }} · {{ item.resourceType || "generic" }} · {{ item.status }}</small><small>{{ formatNumber(item.estimatedCredits) }} credits · expires {{ formatDate(item.expiresAt) }}</small></article><p v-if="!reservations.length" class="muted-box">没有 active reservation。</p></div>
    <AuditTimeline v-else :items="ledgerRows" :item-key="auditKey" label="用户 ledger 时间线" empty-text="暂无用户 ledger。"><template #item="{ item }"><strong>{{ item.kind }} · {{ formatSigned(item.deltaCredits) }}</strong><small>{{ formatDate(item.createdAt) }} · {{ item.reason }} · {{ item.status }}</small><small>{{ formatNumber(item.balanceBefore) }} → {{ formatNumber(item.balanceAfter) }} · {{ item.idempotencyKey || "-" }}</small></template></AuditTimeline>
  </InspectorPanel>
</template>
