<template>
  <div class="api-page">
    <div class="secondary-overlay" v-if="!secondaryVerified">
      <div class="secondary-card">
        <h3>API 后台 · 二级验证</h3>
        <p class="muted">请输入管理员二级密码后查看 API 调用与账户。</p>
        <input
          class="secondary-input"
          type="password"
          v-model="secondaryPassword"
          :disabled="secondaryLoading"
          placeholder="二级密码"
          @keyup.enter="handleVerifySecondary"
        />
        <div class="secondary-actions">
          <button type="button" :disabled="secondaryLoading" @click="handleVerifySecondary">
            {{ secondaryLoading ? '校验中...' : '确认' }}
          </button>
          <button
            type="button"
            class="ghost-btn"
            :disabled="secondaryLoading"
            @click="checkSecondary"
          >
            重新检测
          </button>
        </div>
        <p v-if="secondaryError" class="secondary-error">{{ secondaryError }}</p>
      </div>
    </div>

    <header class="api-header">
      <div>
        <h1>API 管理面板</h1>
        <p>监控 Token / 容器 / 上传审计，并管理 API 账户。</p>
        <small class="muted">数据时间：{{ timeAgo(snapshot?.generated_at) }}</small>
      </div>
      <div class="header-actions">
        <label><input type="checkbox" v-model="autoRefresh" /> 自动刷新</label>
        <button type="button" :disabled="loading" @click="fetchAll">
          {{ loading ? '刷新中...' : '手动刷新' }}
        </button>
      </div>
    </header>

    <section class="panel">
      <div class="metrics-grid">
        <div class="metric-card">
          <p>API 用户数</p>
          <strong>{{ overview.totals?.users || 0 }}</strong>
        </div>
        <div class="metric-card">
          <p>总请求</p>
          <strong>{{ formatNumber(totalRequests) }}</strong>
        </div>
        <div class="metric-card">
          <p>累计 Token</p>
          <strong>{{ formatNumber(overview.token_totals?.total_tokens || 0) }}</strong>
          <span class="muted"
            >输入 {{ formatNumber(overview.token_totals?.input_tokens || 0) }} / 输出
            {{ formatNumber(overview.token_totals?.output_tokens || 0) }}</span
          >
        </div>
        <div class="metric-card">
          <p>活动容器</p>
          <strong>{{ overview.totals?.containers_active || 0 }}</strong>
          <span class="muted"
            >可用槽位 {{ overview.totals?.available_container_slots ?? '—' }}</span
          >
        </div>
      </div>
    </section>

    <section class="panel">
      <div class="section-head">
        <h2>API 账户管理</h2>
        <div class="create-form">
          <input v-model="createForm.username" type="text" placeholder="新账号名 (小写英数/-/_)" />
          <input v-model="createForm.note" type="text" placeholder="备注（可选）" />
          <button type="button" :disabled="creating" @click="createUser">
            {{ creating ? '创建中...' : '创建账号' }}
          </button>
        </div>
      </div>
      <div class="table-wrapper">
        <table>
          <thead>
            <tr>
              <th>账号</th>
              <th>备注</th>
              <th>创建时间</th>
              <th>请求</th>
              <th>最近调用</th>
              <th>Token 累计</th>
              <th>存储</th>
              <th>容器</th>
              <th>密钥</th>
              <th>操作</th>
            </tr>
          </thead>
          <tbody>
            <tr v-if="!users.length">
              <td colspan="10">暂无 API 账号</td>
            </tr>
            <tr v-for="u in users" :key="u.username">
              <td>
                <strong>{{ u.username }}</strong>
              </td>
              <td>{{ u.note || '—' }}</td>
              <td>{{ formatTime(u.created_at) }}</td>
              <td>{{ formatNumber(u.usage?.total_requests || 0) }}</td>
              <td>{{ timeAgo(u.usage?.last_request_at) }}</td>
              <td>{{ formatNumber(u.tokens?.total_tokens || 0) }}</td>
              <td>{{ formatBytes(u.storage?.total_bytes) }}</td>
              <td>
                <span v-if="u.container?.running" class="status-badge online">运行中</span>
                <span v-else class="status-badge offline">未运行</span>
              </td>
              <td class="token-cell">
                <span class="token-text">
                  {{
                    revealTokens[u.username]?.visible
                      ? revealTokens[u.username]?.token || '—'
                      : '••••••••••'
                  }}
                </span>
                <button
                  class="icon-btn"
                  @click="toggleReveal(u.username)"
                  :disabled="revealLoading === u.username"
                >
                  <svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
                    <path
                      d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12Zm11 3a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z"
                      fill="none"
                      stroke="currentColor"
                      stroke-width="1.5"
                    />
                  </svg>
                </button>
                <button
                  class="icon-btn"
                  @click="copyToken(u.username)"
                  :disabled="revealLoading === u.username"
                >
                  📋
                </button>
              </td>
              <td>
                <button
                  class="link danger"
                  @click="deleteUser(u.username)"
                  :disabled="deleting === u.username"
                >
                  删除
                </button>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
      <p v-if="createError" class="error-text">{{ createError }}</p>
    </section>

    <section class="panel grid">
      <div>
        <div class="section-head">
          <h3>容器状态</h3>
          <small class="muted">实时抓取</small>
        </div>
        <div class="table-wrapper small scrollable">
          <table>
            <thead>
              <tr>
                <th>Key</th>
                <th>CPU</th>
                <th>内存%</th>
                <th>内存</th>
                <th>网络 RX/TX</th>
                <th>状态</th>
              </tr>
            </thead>
            <tbody>
              <tr v-if="!containerItems.length">
                <td colspan="6">暂无容器</td>
              </tr>
              <tr v-for="c in containerItems" :key="c.key">
                <td>{{ c.key }}</td>
                <td>{{ formatPercentNumber(c.stats?.cpu_percent) }}</td>
                <td>{{ formatPercentNumber(c.stats?.memory?.percent) }}</td>
                <td>{{ formatBytes(c.stats?.memory?.used_bytes) }}</td>
                <td>
                  {{ formatBytes(c.stats?.net_io?.rx_bytes) }} /
                  {{ formatBytes(c.stats?.net_io?.tx_bytes) }}
                </td>
                <td>
                  <span :class="['status-badge', c.running ? 'online' : 'offline']">
                    {{ c.running ? '运行中' : '未运行' }}
                  </span>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>
      <div>
        <div class="section-head">
          <h3>上传审计</h3>
          <small class="muted">来自隔离区日志</small>
        </div>
        <ul class="upload-feed scrollable">
          <li v-if="!uploads.recent_events?.length" class="upload-item">暂无记录</li>
          <li v-for="item in uploads.recent_events || []" :key="item.upload_id" class="upload-item">
            <div>
              <strong>{{ item.original_name || '未命名文件' }}</strong>
              <div class="muted small">
                用户 {{ item.username }} · {{ formatBytes(item.size) }} ·
                {{ item.source || 'unknown' }}
              </div>
            </div>
            <div class="muted small">
              {{ timeAgo(item.timestamp) }}
              <span :class="['status-badge', item.accepted ? 'online' : 'danger']">
                {{ item.accepted ? '通过' : '阻断' }}
              </span>
            </div>
          </li>
        </ul>
      </div>
    </section>

    <section class="panel">
      <div class="balance-header">
        <div>
          <h3>余额查询</h3>
          <p class="muted">Kimi / DeepSeek / Qwen</p>
        </div>
        <button type="button" :disabled="balanceLoading" @click="fetchBalance">
          {{ balanceLoading ? '查询中...' : '刷新余额' }}
        </button>
      </div>
      <div class="balance-grid">
        <div
          v-for="card in balanceCards"
          :key="card.key"
          class="balance-card"
          :class="{ error: !card.success }"
        >
          <div class="balance-card-head">
            <h4>{{ card.label }}</h4>
            <span :class="['status-badge', card.success ? 'online' : 'danger']">
              {{ card.success ? '正常' : '失败' }}
            </span>
          </div>
          <strong>{{ card.amount ?? '—' }} {{ card.currency }}</strong>
          <span class="muted small" v-if="card.amountCny">≈ ¥{{ card.amountCny }}</span>
          <p class="muted small" v-else-if="card.error">{{ card.error }}</p>
        </div>
      </div>
    </section>
  </div>
</template>

<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { useSecondaryPass } from './useSecondaryPass';

const {
  verified: secondaryVerified,
  loading: secondaryLoading,
  error: secondaryError,
  check: checkSecondary,
  verify: verifySecondary
} = useSecondaryPass();
const secondaryPassword = ref('');

const loading = ref(false);
const autoRefresh = ref(true);
let timer: number | undefined;

const snapshot = ref<any>(null);
const users = ref<any[]>([]);
const revealTokens = ref<Record<string, { token: string; visible: boolean }>>({});
const revealLoading = ref('');
const deleting = ref('');
const creating = ref(false);
const createForm = ref({ username: '', note: '' });
const createError = ref('');

const balanceData = ref<Record<string, any> | null>(null);
const balanceLoading = ref(false);

const overview = computed(() => snapshot.value?.overview || {});
const uploads = computed(() => snapshot.value?.uploads || {});
const containerItems = computed(() => {
  const items = snapshot.value?.containers || {};
  return Object.keys(items).map((key) => ({ key, ...(items[key] || {}) }));
});
const totalRequests = computed(() =>
  users.value.reduce(
    (sum, u) => sum + (u.usage && u.usage.total_requests ? u.usage.total_requests : 0),
    0
  )
);

const balanceCards = computed(() => {
  const keys = ['kimi', 'deepseek', 'qwen'];
  return keys.map((k) => {
    const data = (balanceData.value as any)?.[k] || {};
    return {
      key: k,
      label: k.toUpperCase(),
      success: data.success === true,
      amount: data.available ?? data.total ?? null,
      amountCny: data.amount_cny ?? data.amountCny ?? null,
      currency: data.currency || '',
      error: data.error
    };
  });
});

const formatNumber = (val: any) => {
  const num = Number(val || 0);
  return Number.isFinite(num) ? num.toLocaleString('zh-CN') : '0';
};
const formatBytes = (v: any) => {
  const num = Number(v || 0);
  if (!num) return '0B';
  const units = ['B', 'KB', 'MB', 'GB', 'TB'];
  let n = num;
  let i = 0;
  while (n >= 1024 && i < units.length - 1) {
    n /= 1024;
    i += 1;
  }
  return `${n.toFixed(1)}${units[i]}`;
};
const formatTime = (iso?: string) => {
  if (!iso) return '—';
  try {
    return new Date(iso).toLocaleString();
  } catch {
    return iso;
  }
};
const timeAgo = (iso?: string) => {
  if (!iso) return '—';
  const ts = new Date(iso).getTime();
  if (!Number.isFinite(ts)) return '—';
  const diff = Date.now() - ts;
  const mins = Math.floor(diff / 60000);
  if (mins < 1) return '刚刚';
  if (mins < 60) return `${mins} 分钟前`;
  const hours = Math.floor(mins / 60);
  if (hours < 24) return `${hours} 小时前`;
  const days = Math.floor(hours / 24);
  return `${days} 天前`;
};
const formatPercentNumber = (v: any) => {
  if (v === null || v === undefined) return '—';
  const num = Number(v);
  if (!Number.isFinite(num)) return '—';
  return `${num.toFixed(1)}%`;
};

const fetchDashboard = async () => {
  if (!secondaryVerified.value) return;
  loading.value = true;
  try {
    const resp = await fetch('/api/admin/api-dashboard', { credentials: 'same-origin' });
    const data = await resp.json();
    if (!resp.ok || !data.success) throw new Error(data.error || '加载失败');
    snapshot.value = data.data;
  } catch (err: any) {
    console.error(err);
  } finally {
    loading.value = false;
  }
};

const fetchUsers = async () => {
  if (!secondaryVerified.value) return;
  try {
    const resp = await fetch('/api/admin/api-users', { credentials: 'same-origin' });
    const data = await resp.json();
    if (!resp.ok || !data.success) throw new Error(data.error || '加载失败');
    const map: Record<string, any> = {};
    users.value = (data.data || []).map((u: any) => ({
      ...u,
      usage: u.usage || { total_requests: 0, endpoints: {}, last_request_at: null },
      tokens: u.tokens || { total_tokens: 0, input_tokens: 0, output_tokens: 0 },
      storage: u.storage || { total_bytes: 0 }
    }));
    users.value.forEach((u) => {
      map[u.username] = revealTokens.value[u.username] || { token: '', visible: false };
    });
    revealTokens.value = map;
  } catch (err: any) {
    console.error(err);
  }
};

const fetchAll = async () => {
  await Promise.all([fetchDashboard(), fetchUsers()]);
};

const scheduleAutoRefresh = () => {
  if (timer) {
    clearInterval(timer);
    timer = undefined;
  }
  if (autoRefresh.value && secondaryVerified.value) {
    timer = window.setInterval(fetchAll, 30000);
  }
};

const createUser = async () => {
  if (!secondaryVerified.value) return;
  createError.value = '';
  const username = createForm.value.username.trim().toLowerCase();
  if (!username) {
    createError.value = '请输入账号名';
    return;
  }
  const pattern = /^[a-z0-9_-]{3,32}$/;
  if (!pattern.test(username)) {
    createError.value = '账号名需为 3-32 位小写字母/数字/_/-';
    return;
  }
  creating.value = true;
  try {
    const resp = await fetch('/api/admin/api-users', {
      method: 'POST',
      credentials: 'same-origin',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username, note: createForm.value.note })
    });
    const data = await resp.json();
    if (!resp.ok || !data.success) throw new Error(data.error || '创建失败');
    await fetchUsers();
    revealTokens.value[username] = { token: data.data.token, visible: true };
    createForm.value = { username: '', note: '' };
  } catch (err: any) {
    createError.value = err.message || '创建失败';
  } finally {
    creating.value = false;
  }
};

const deleteUser = async (username: string) => {
  if (!secondaryVerified.value) return;
  if (!confirm(`确认删除 API 用户 ${username} 吗？`)) return;
  deleting.value = username;
  try {
    const resp = await fetch(`/api/admin/api-users/${encodeURIComponent(username)}`, {
      method: 'DELETE',
      credentials: 'same-origin'
    });
    const data = await resp.json();
    if (!resp.ok || !data.success) throw new Error(data.error || '删除失败');
    await fetchUsers();
  } catch (err: any) {
    alert(err.message || '删除失败');
  } finally {
    deleting.value = '';
  }
};

const toggleReveal = async (username: string) => {
  const entry = revealTokens.value[username];
  if (entry?.visible) {
    revealTokens.value[username] = { ...(entry || {}), visible: false };
    return;
  }
  revealLoading.value = username;
  try {
    const resp = await fetch(`/api/admin/api-users/${encodeURIComponent(username)}/token`, {
      credentials: 'same-origin'
    });
    const data = await resp.json();
    if (!resp.ok || !data.success) throw new Error(data.error || '获取失败');
    revealTokens.value[username] = { token: data.data.token, visible: true };
  } catch (err: any) {
    alert(err.message || '获取 token 失败');
  } finally {
    revealLoading.value = '';
  }
};

const copyToken = async (username: string) => {
  const entry = revealTokens.value[username];
  if (!entry?.token) {
    await toggleReveal(username);
  }
  const token = revealTokens.value[username]?.token;
  if (!token) return;
  try {
    await navigator.clipboard.writeText(token);
  } catch (err) {
    console.error(err);
  }
};

const fetchBalance = async () => {
  if (!secondaryVerified.value) return;
  balanceLoading.value = true;
  try {
    const resp = await fetch('/api/admin/balance', { credentials: 'same-origin' });
    const data = await resp.json();
    if (!resp.ok || !data.success) throw new Error(data.error || '查询失败');
    balanceData.value = data.data;
  } catch (err) {
    console.error(err);
  } finally {
    balanceLoading.value = false;
  }
};

const handleVerifySecondary = async () => {
  await verifySecondary(secondaryPassword.value);
  if (secondaryVerified.value) {
    secondaryPassword.value = '';
    await fetchAll();
    scheduleAutoRefresh();
  }
};

onMounted(async () => {
  await checkSecondary();
  if (secondaryVerified.value) {
    await fetchAll();
    scheduleAutoRefresh();
  }
});

watch(autoRefresh, scheduleAutoRefresh);
watch(secondaryVerified, (val) => {
  if (val) {
    fetchAll();
    scheduleAutoRefresh();
  } else if (timer) {
    clearInterval(timer);
    timer = undefined;
  }
});

onBeforeUnmount(() => {
  if (timer) clearInterval(timer);
});
</script>

<style scoped>
:global(body) {
  margin: 0;
  background: #f7f3ea;
  font-family: 'Iowan Old Style', ui-serif, Georgia, Cambria, 'Times New Roman', serif;
  color: #2a2013;
}

:global(#admin-api-app) {
  min-height: 100vh;
  padding: 24px 32px 48px;
  box-sizing: border-box;
}

.api-page {
  max-width: 1400px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

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

.header-actions {
  display: flex;
  gap: 10px;
  align-items: center;
}

.panel {
  background: #f6ecda;
  border-radius: 20px;
  padding: 18px;
  border: 1px solid rgba(118, 103, 84, 0.35);
  box-shadow: 0 10px 22px rgba(0, 0, 0, 0.08);
}

.metrics-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
}

.metric-card {
  background: rgba(255, 255, 255, 0.94);
  border-radius: 16px;
  padding: 14px;
  border: 1px solid rgba(44, 32, 19, 0.12);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6);
}

.metric-card p {
  margin: 0;
  color: #6b5b44;
  font-size: 13px;
  letter-spacing: 0.05em;
}

.metric-card strong {
  font-size: 26px;
  display: block;
  margin-top: 6px;
}

.muted {
  color: #6b5b44;
}

.muted.small {
  font-size: 12px;
}

.section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.create-form {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.create-form input {
  padding: 10px 12px;
  border-radius: 12px;
  border: 1px solid rgba(0, 0, 0, 0.12);
}

button {
  border: none;
  border-radius: 12px;
  padding: 10px 14px;
  background: var(--claude-highlight);
  color: #2a2013;
  font-weight: 600;
  cursor: pointer;
}

.ghost-btn {
  background: transparent;
  border: 1px dashed rgba(44, 32, 19, 0.35);
}

.table-wrapper {
  width: 100%;
  overflow: auto;
}

.table-wrapper.scrollable {
  max-height: 320px;
}

table {
  width: 100%;
  border-collapse: collapse;
}

th,
td {
  padding: 10px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
  text-align: left;
  font-size: 14px;
}

.token-cell {
  display: flex;
  align-items: center;
  gap: 8px;
}

.icon-btn {
  background: rgba(255, 255, 255, 0.9);
  border: 1px solid rgba(0, 0, 0, 0.12);
  border-radius: 8px;
  padding: 6px;
  cursor: pointer;
}

.link.danger {
  background: transparent;
  color: #b5473d;
  border: 1px solid rgba(181, 71, 61, 0.4);
}

.status-badge {
  display: inline-flex;
  align-items: center;
  padding: 4px 8px;
  border-radius: 10px;
  font-size: 12px;
}

.status-badge.online {
  background: rgba(73, 160, 120, 0.15);
  color: #2f6b4f;
}

.status-badge.offline {
  background: rgba(0, 0, 0, 0.06);
  color: #5f5140;
}

.status-badge.danger {
  background: rgba(189, 93, 58, 0.15);
  color: #b5473d;
}

.upload-feed {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.upload-feed.scrollable {
  max-height: 280px;
  overflow: auto;
}

.upload-item {
  padding: 10px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.92);
  border: 1px solid rgba(0, 0, 0, 0.06);
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 14px;
}

.balance-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 12px;
}

.balance-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 10px;
}

.balance-card {
  background: rgba(255, 255, 255, 0.92);
  border: 1px solid rgba(44, 32, 19, 0.12);
  border-radius: 14px;
  padding: 12px;
}

.balance-card.error {
  border-color: rgba(181, 71, 61, 0.35);
}

.balance-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.error-text {
  color: #b5473d;
  margin-top: 8px;
}

.secondary-overlay {
  position: fixed;
  inset: 0;
  background: rgba(20, 12, 5, 0.35);
  backdrop-filter: blur(6px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 999;
}

.secondary-card {
  width: 360px;
  max-width: 90vw;
  background: #f6ecda;
  border: 1px solid rgba(118, 103, 84, 0.35);
  border-radius: 18px;
  padding: 20px;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.16);
}

.secondary-card h3 {
  margin: 0 0 8px;
}

.secondary-card p {
  margin: 0 0 12px;
  color: #5b4b35;
}

.secondary-input {
  width: 100%;
  box-sizing: border-box;
  padding: 12px 14px;
  border-radius: 12px;
  border: 1px solid rgba(44, 32, 19, 0.2);
  margin-bottom: 12px;
  font-size: 15px;
}

.secondary-actions {
  display: flex;
  gap: 10px;
  align-items: center;
}

.secondary-actions .ghost-btn {
  background: transparent;
  border: 1px dashed rgba(44, 32, 19, 0.35);
  padding: 10px 14px;
  border-radius: 12px;
  cursor: pointer;
}

.secondary-error {
  color: #b5473d;
  margin-top: 10px;
}
</style>
