﻿const {
  App,
  Button,
  ConfigProvider,
  Form,
  Input,
  InputNumber,
  Layout,
  Menu,
  Modal,
  Popconfirm,
  Progress,
  Select,
  Space,
  Switch,
  Table,
  Tag,
  Typography,
  message
} = antd;

const Icons = icons;
const LabIcon = Icons.ExperimentOutlined || Icons.ApiOutlined || Icons.AppstoreOutlined || Icons.AppstoreAddOutlined;
const IntervalLabIcon = Icons.FieldTimeOutlined || Icons.ScheduleOutlined || Icons.ClockCircleOutlined;
const RequestConfigIcon = Icons.ApiOutlined || Icons.CloudServerOutlined || Icons.LinkOutlined || Icons.SettingOutlined;
const { Header, Sider, Content } = Layout;
const DEFAULT_CURSOR_TOKEN_JSON = JSON.stringify({
  accessToken: '',
  refreshToken: '',
  access_token: '',
  refresh_token: ''
}, null, 2);

function api(path, options = {}) {
  const token = localStorage.getItem('cla_token');
  return fetch(path, {
    ...options,
    headers: {
      'Content-Type': 'application/json',
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
      ...(options.headers || {})
    }
  }).then(async response => {
    const data = await response.json().catch(() => ({}));
    if (!response.ok) throw new Error(data.error || '请求失败');
    return data;
  });
}

function timeValue(value) {
  const time = Date.parse(value || '');
  return Number.isFinite(time) ? time : 0;
}

function poolStatusRank(status) {
  return { ready: 0, cooldown: 1, exhausted: 2 }[status || 'ready'] ?? 3;
}

function tokenExpiredRank(record) {
  if (record.tokenExpired === false) return 0;
  if (record.tokenExpired === true) return 2;
  return 1;
}

function compareAccountsDefault(a, b) {
  const aExp = timeValue(a.tokenExpiresAt);
  const bExp = timeValue(b.tokenExpiresAt);
  if (aExp !== bExp) {
    if (!aExp) return 1;
    if (!bExp) return -1;
    return aExp - bExp;
  }
  const poolDelta = poolStatusRank(a.poolStatus) - poolStatusRank(b.poolStatus);
  if (poolDelta) return poolDelta;
  const expiredDelta = tokenExpiredRank(a) - tokenExpiredRank(b);
  if (expiredDelta) return expiredDelta;
  return textValue(a.email).localeCompare(textValue(b.email), 'en');
}

function textValue(value) {
  return String(value || '');
}

function accountUsed(record) {
  return Number(record.usageCount || 0) > 0 || Boolean(record.lastIssuedAt || record.lastLoginAt);
}

function usageUsedPct(usedPercent) {
  if (usedPercent === null || usedPercent === undefined || Number.isNaN(Number(usedPercent))) return null;
  const n = Number(usedPercent);
  if (!Number.isFinite(n)) return null;
  return Math.max(0, Math.min(100, n));
}

function formatCentsUsd(cents) {
  const n = Number(cents);
  if (!Number.isFinite(n)) return '-';
  return `$${(n / 100).toFixed(2)}`;
}

function formatPercentValue(value) {
  const n = usageUsedPct(value);
  return n === null ? '-' : `${Math.round(n * 100) / 100}%`;
}

function formatUsageTime(ms) {
  const n = Number(ms);
  if (!Number.isFinite(n)) {
    const parsed = Date.parse(String(ms || ''));
    if (!Number.isFinite(parsed)) return '-';
    return dayjs(parsed).format('YYYY-MM-DD HH:mm:ss');
  }
  const d = dayjs(n < 1e12 ? n * 1000 : n);
  return d.isValid() ? d.format('YYYY-MM-DD HH:mm:ss') : String(ms);
}

function formatDateTimeSecond(value) {
  return value ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : '';
}

function normalizeDateTimeSecond(value) {
  const raw = String(value || '').trim();
  if (!raw) return '';
  const matched = raw.match(/^(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2}:\d{2})/);
  if (matched) return `${matched[1]} ${matched[2]}`;
  if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/.test(raw)) return `${raw}:00`;
  return raw.replace('T', ' ').replace(/\.\d+Z?$/i, '').replace(/Z$/i, '');
}

function Login({ onLogin }) {
  const [loading, setLoading] = React.useState(false);
  const [captcha, setCaptcha] = React.useState(null);
  const [captchaLoading, setCaptchaLoading] = React.useState(false);
  const [form] = Form.useForm();

  async function loadCaptcha() {
    setCaptchaLoading(true);
    try {
      const data = await api('/api/captcha');
      setCaptcha(data);
      form.setFieldsValue({ captcha: '' });
    } catch (error) {
      message.error(error.message);
    } finally {
      setCaptchaLoading(false);
    }
  }

  React.useEffect(() => {
    loadCaptcha();
  }, []);

  async function submit(values) {
    if (!captcha?.captchaId) {
      message.error('验证码加载失败，请刷新验证码');
      return;
    }
    setLoading(true);
    try {
      const data = await api('/api/admin/login', {
        method: 'POST',
        body: JSON.stringify({ ...values, captchaId: captcha.captchaId })
      });
      localStorage.setItem('cla_token', data.token);
      onLogin(data.user);
    } catch (error) {
      message.error(error.message);
      loadCaptcha();
    } finally {
      setLoading(false);
    }
  }

  return (
    <div className="login-page">
      <div className="login-panel">
        <div className="login-brand">
          <div className="login-mark"><Icons.SafetyCertificateOutlined /></div>
          <div>
            <h1 className="login-title">Cursor 登录助手后台</h1>
            <p className="login-subtitle">账号池、设备与授权管理</p>
          </div>
        </div>
        <Form form={form} layout="vertical" onFinish={submit} initialValues={{ username: 'admin' }}>
          <Form.Item label="用户名" name="username" rules={[{ required: true, message: '请输入用户名' }]}>
            <Input size="large" prefix={<Icons.UserOutlined />} />
          </Form.Item>
          <Form.Item label="密码" name="password" rules={[{ required: true, message: '请输入密码' }]}>
            <Input.Password size="large" prefix={<Icons.LockOutlined />} />
          </Form.Item>
          <Form.Item label="验证码" required>
            <div className="captcha-row">
              <Form.Item
                name="captcha"
                noStyle
                rules={[
                  { required: true, message: '请输入验证码' },
                  { len: 4, message: '验证码为 4 位字母' }
                ]}
              >
                <Input
                  size="large"
                  maxLength={4}
                  autoComplete="off"
                  className="captcha-input"
                  prefix={<Icons.SafetyCertificateOutlined />}
                  onChange={event => form.setFieldsValue({ captcha: event.target.value.toUpperCase() })}
                />
              </Form.Item>
              <button
                className="captcha-image-button"
                type="button"
                onClick={loadCaptcha}
                disabled={captchaLoading}
                aria-label="刷新验证码"
                title="刷新验证码"
              >
                {captcha?.image
                  ? <img alt="验证码" src={`data:image/svg+xml;charset=utf-8,${encodeURIComponent(captcha.image)}`} />
                  : <span>{captchaLoading ? '加载中' : '刷新'}</span>}
              </button>
            </div>
          </Form.Item>
          <Button className="login-submit" type="primary" htmlType="submit" block loading={loading} icon={<Icons.LoginOutlined />}>
            登录
          </Button>
        </Form>
      </div>
    </div>
  );
}

function Dashboard() {
  const [data, setData] = React.useState({ stats: {}, recentEvents: [] });
  const [loading, setLoading] = React.useState(false);

  const load = React.useCallback(async () => {
    setLoading(true);
    try {
      setData(await api('/api/dashboard'));
    } catch (error) {
      message.error(error.message);
    } finally {
      setLoading(false);
    }
  }, []);

  React.useEffect(() => {
    load();
  }, [load]);

  const stats = [
    ['当天获取账号', data.stats.fetchedToday || 0],
    ['当天使用账号', data.stats.usedToday || 0],
    ['本月获取账号', data.stats.fetchedMonth || 0],
    ['本月使用账号', data.stats.usedMonth || 0],
    ['账号总数', data.stats.accounts || 0],
    ['启用账号', data.stats.activeAccounts || 0],
    ['活码', data.stats.activationCodes || 0],
    ['有效活码', data.stats.activeActivationCodes || 0],
    ['设备总数', data.stats.devices || 0],
    ['在线设备', data.stats.onlineDevices || 0]
  ];

  return (
    <>
      <div className="toolbar">
        <h2>总览</h2>
        <Button icon={<Icons.ReloadOutlined />} onClick={load} loading={loading} />
      </div>
      <div className="stats-grid">
        {stats.map(([label, value]) => (
          <div className="stat-card" key={label}>
            <div className="stat-label">{label}</div>
            <div className="stat-value">{value}</div>
          </div>
        ))}
      </div>
      <div className="table-panel">
        <Table
          rowKey="id"
          size="middle"
          dataSource={data.recentEvents}
          pagination={false}
          columns={eventColumns()}
        />
      </div>
    </>
  );
}

function Accounts() {
  const [items, setItems] = React.useState([]);
  const [loading, setLoading] = React.useState(false);
  const [modalOpen, setModalOpen] = React.useState(false);
  const [editing, setEditing] = React.useState(null);
  const [selectedRowKeys, setSelectedRowKeys] = React.useState([]);
  const [nameKeyword, setNameKeyword] = React.useState('');
  const [usageOpen, setUsageOpen] = React.useState(false);
  const [usageLoading, setUsageLoading] = React.useState(false);
  const [usageAccount, setUsageAccount] = React.useState(null);
  const [usageData, setUsageData] = React.useState(null);
  const [form] = Form.useForm();

  const filteredItems = React.useMemo(() => {
    const keyword = nameKeyword.trim().toLowerCase();
    if (!keyword) return items;
    return items.filter(item => textValue(item.label).toLowerCase().includes(keyword));
  }, [items, nameKeyword]);

  async function load() {
    setLoading(true);
    try {
      const data = await api('/api/accounts?includeSecrets=1');
      setItems([...(data.items || [])].sort(compareAccountsDefault));
      setSelectedRowKeys(keys => keys.filter(key => data.items.some(item => item.id === key)));
    } catch (error) {
      message.error(error.message);
    } finally {
      setLoading(false);
    }
  }

  React.useEffect(() => {
    load();
  }, []);

  async function viewUsage(record) {
    setUsageAccount(record);
    setUsageData(null);
    setUsageOpen(true);
    setUsageLoading(true);
    try {
      const data = await api(`/api/accounts/${record.id}/usage`);
      setUsageData(data);
    } catch (error) {
      message.error(error.message);
      setUsageOpen(false);
    } finally {
      setUsageLoading(false);
    }
  }

  async function openEditor(record) {
    setEditing(record || null);
    form.resetFields();
    form.setFieldsValue(record ? {
      ...record,
      credential: record.credential || '',
      token: record.token || DEFAULT_CURSOR_TOKEN_JSON,
      cursorProfile: record.cursorProfile || ''
    } : {
      status: 'active',
      poolStatus: 'ready',
      maxUses: 1,
      label: 'Free Plan',
      credential: '',
      token: DEFAULT_CURSOR_TOKEN_JSON,
      cursorProfile: '',
      tags: []
    });
    setModalOpen(true);
  }

  async function save() {
    const values = await form.validateFields();
    const payload = {
      ...values,
      tags: typeof values.tags === 'string'
        ? values.tags.split(',').map(item => item.trim()).filter(Boolean)
        : values.tags || []
    };
    try {
      await api(editing ? `/api/accounts/${editing.id}` : '/api/accounts', {
        method: editing ? 'PATCH' : 'POST',
        body: JSON.stringify(payload)
      });
      setModalOpen(false);
      message.success('已保存账号');
      load();
    } catch (error) {
      message.error(error.message);
    }
  }

  async function remove(record) {
    try {
      await api(`/api/accounts/${record.id}`, { method: 'DELETE' });
      message.success('已删除账号');
      load();
    } catch (error) {
      message.error(error.message);
    }
  }

  async function bulkDelete() {
    try {
      const data = await api('/api/accounts/bulk-delete', {
        method: 'POST',
        body: JSON.stringify({ ids: selectedRowKeys })
      });
      message.success(`已删除 ${data.removed || selectedRowKeys.length} 个账号`);
      setSelectedRowKeys([]);
      load();
    } catch (error) {
      message.error(error.message);
    }
  }

  async function bulkSetReady() {
    try {
      const data = await api('/api/accounts/bulk-set-ready', {
        method: 'POST',
        body: JSON.stringify({ ids: selectedRowKeys })
      });
      message.success(`已将 ${data.updated || selectedRowKeys.length} 个账号设为可用，发放次数已重置为 0`);
      setSelectedRowKeys([]);
      load();
    } catch (error) {
      message.error(error.message);
    }
  }

  return (
    <>
      <div className="toolbar">
        <h2>账号管理</h2>
        <Space wrap>
          <Input.Search
            allowClear
            placeholder="按名称搜索"
            value={nameKeyword}
            onChange={event => setNameKeyword(event.target.value)}
            onSearch={value => setNameKeyword(value)}
            style={{ width: 220 }}
          />
          <Button icon={<Icons.ReloadOutlined />} onClick={load} loading={loading} />
          <Button
            icon={<Icons.CheckCircleOutlined />}
            disabled={!selectedRowKeys.length}
            onClick={bulkSetReady}
          >
            批量设置可用
          </Button>
          <Popconfirm
            title={`删除选中的 ${selectedRowKeys.length} 个账号？`}
            onConfirm={bulkDelete}
            disabled={!selectedRowKeys.length}
          >
            <Button danger icon={<Icons.DeleteOutlined />} disabled={!selectedRowKeys.length}>批量删除</Button>
          </Popconfirm>
          <Button type="primary" icon={<Icons.PlusOutlined />} onClick={() => openEditor(null)}>新账号</Button>
        </Space>
      </div>
      <div className="table-panel">
        <Table
          rowKey="id"
          loading={loading}
          dataSource={filteredItems}
          rowSelection={{
            fixed: true,
            columnWidth: 52,
            selectedRowKeys,
            onChange: keys => setSelectedRowKeys(keys)
          }}
          scroll={{ x: 1200 }}
          columns={[
            {
              title: '邮箱/账号',
              dataIndex: 'email',
              fixed: 'left',
              width: 220,
              sorter: (a, b) => textValue(a.email).localeCompare(textValue(b.email))
            },
            // 暂时隐藏：名称 / 状态 / 密钥 / Token
            // {
            //   title: '名称',
            //   dataIndex: 'label',
            //   width: 160,
            //   sorter: (a, b) => textValue(a.label).localeCompare(textValue(b.label))
            // },
            // {
            //   title: '状态',
            //   dataIndex: 'status',
            //   width: 120,
            //   filters: [
            //     { text: '启用', value: 'active' },
            //     { text: '停用', value: 'disabled' }
            //   ],
            //   onFilter: (value, record) => (record.status || 'active') === value,
            //   sorter: (a, b) => textValue(a.status || 'active').localeCompare(textValue(b.status || 'active')),
            //   render: value => {
            //     const status = value || 'active';
            //     return <span className={`status-dot ${status}`}>{status === 'active' ? '启用' : '停用'}</span>;
            //   }
            // },
            // {
            //   title: '密钥',
            //   dataIndex: 'hasCredential',
            //   width: 100,
            //   filters: [
            //     { text: '已保存', value: 'saved' },
            //     { text: '未保存', value: 'missing' }
            //   ],
            //   onFilter: (value, record) => value === 'saved' ? record.hasCredential : !record.hasCredential,
            //   sorter: (a, b) => Number(Boolean(a.hasCredential)) - Number(Boolean(b.hasCredential)),
            //   render: value => value ? <Tag color="green">已保存</Tag> : <Tag>未保存</Tag>
            // },
            // {
            //   title: 'Token',
            //   dataIndex: 'hasToken',
            //   width: 100,
            //   filters: [
            //     { text: '可用', value: 'usable' },
            //     { text: '默认结构', value: 'shape' },
            //     { text: '未配置', value: 'missing' }
            //   ],
            //   onFilter: (value, record) => {
            //     if (value === 'usable') return Boolean(record.hasToken);
            //     if (value === 'shape') return !record.hasToken && Boolean(record.hasTokenShape);
            //     return !record.hasToken && !record.hasTokenShape;
            //   },
            //   sorter: (a, b) => {
            //     const tokenRank = record => record.hasToken ? 2 : record.hasTokenShape ? 1 : 0;
            //     return tokenRank(a) - tokenRank(b);
            //   },
            //   render: (value, record) => {
            //     if (record.tokenExpired === true) return <Tag color="red">已过期</Tag>;
            //     if (value) return <Tag color="green">可用</Tag>;
            //     return record.hasTokenShape ? <Tag color="orange">默认结构</Tag> : <Tag>未配置</Tag>;
            //   }
            // },
            {
              title: 'Token 是否过期',
              dataIndex: 'tokenExpired',
              width: 130,
              filters: [
                { text: '已过期', value: 'expired' },
                { text: '未过期', value: 'fresh' },
                { text: '未知', value: 'unknown' }
              ],
              onFilter: (value, record) => {
                if (value === 'expired') return record.tokenExpired === true;
                if (value === 'fresh') return record.tokenExpired === false;
                return record.tokenExpired === null || record.tokenExpired === undefined;
              },
              sorter: (a, b) => tokenExpiredRank(a) - tokenExpiredRank(b),
              render: value => {
                if (value === true) return <Tag color="red">已过期</Tag>;
                if (value === false) return <Tag color="green">未过期</Tag>;
                return <Tag>未知</Tag>;
              }
            },
            {
              title: 'Token 到期时间',
              dataIndex: 'tokenExpiresAt',
              width: 180,
              defaultSortOrder: 'ascend',
              sorter: {
                compare: (a, b) => {
                  const aExp = timeValue(a.tokenExpiresAt);
                  const bExp = timeValue(b.tokenExpiresAt);
                  if (!aExp && !bExp) return 0;
                  if (!aExp) return 1;
                  if (!bExp) return -1;
                  return aExp - bExp;
                },
                multiple: 1
              },
              render: value => value ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : '-'
            },
            {
              title: '池状态',
              dataIndex: 'poolStatus',
              width: 130,
              filters: [
                { text: '可发放', value: 'issuable' },
                { text: '显示可用但不可发', value: 'ready_blocked' },
                { text: '已尽', value: 'exhausted' },
                { text: '冷却中', value: 'cooldown' }
              ],
              onFilter: (value, record) => {
                if (value === 'issuable') return Boolean(record.issuable);
                if (value === 'ready_blocked') return (record.poolStatus || 'ready') === 'ready' && !record.issuable;
                return (record.poolStatus || 'ready') === value;
              },
              sorter: (a, b) => poolStatusRank(a.poolStatus) - poolStatusRank(b.poolStatus),
              render: (value, record) => {
                if (record.issuable) {
                  return <Tag color="green">可发放</Tag>;
                }
                const status = value || 'ready';
                if (status === 'ready') {
                  return (
                    <Tag color="gold" title={record.issueBlockReason || '不可发放'}>
                      {record.issueBlockReason || '不可发放'}
                    </Tag>
                  );
                }
                const label = status === 'exhausted' ? '已尽' : '冷却中';
                return <Tag color={status === 'exhausted' ? 'orange' : 'blue'}>{label}</Tag>;
              }
            },
            {
              title: '发放次数',
              width: 120,
              sorter: (a, b) => Number(a.usageCount || 0) - Number(b.usageCount || 0),
              render: (_, record) => `${record.usageCount || 0}/${record.maxUses || 1}`
            },
            {
              title: '使用状态',
              width: 130,
              filters: [
                { text: '已使用', value: 'used' },
                { text: '未使用', value: 'unused' }
              ],
              onFilter: (value, record) => value === 'used' ? accountUsed(record) : !accountUsed(record),
              sorter: (a, b) => Number(accountUsed(a)) - Number(accountUsed(b)),
              render: (_, record) => accountUsed(record)
                ? <Tag color="blue">已使用</Tag>
                : <Tag>未使用</Tag>
            },
            // 暂时隐藏：标签 / 最近登录
            // {
            //   title: '标签',
            //   dataIndex: 'tags',
            //   width: 180,
            //   render: value => (value || []).map(tag => <Tag key={tag}>{tag}</Tag>)
            // },
            {
              title: '创建时间',
              dataIndex: 'createdAt',
              width: 180,
              sorter: (a, b) => timeValue(a.createdAt) - timeValue(b.createdAt),
              render: value => value ? dayjs(value).format('YYYY-MM-DD HH:mm') : '-'
            },
            // {
            //   title: '最近登录',
            //   dataIndex: 'lastLoginAt',
            //   width: 180,
            //   sorter: (a, b) => timeValue(a.lastLoginAt) - timeValue(b.lastLoginAt),
            //   render: value => value ? dayjs(value).format('YYYY-MM-DD HH:mm') : '-'
            // },
            {
              title: '操作',
              width: 230,
              fixed: 'right',
              render: (_, record) => (
                <Space>
                  <Button
                    icon={<Icons.BarChartOutlined />}
                    onClick={() => viewUsage(record)}
                  >
                    查看额度
                  </Button>
                  <Button icon={<Icons.EditOutlined />} onClick={() => openEditor(record)} />
                  <Popconfirm title="删除这个账号？" onConfirm={() => remove(record)}>
                    <Button danger icon={<Icons.DeleteOutlined />} />
                  </Popconfirm>
                </Space>
              )
            }
          ]}
        />
      </div>
      <Modal
        title={editing ? '编辑账号' : '新账号'}
        open={modalOpen}
        onOk={save}
        onCancel={() => setModalOpen(false)}
        okText="保存"
        cancelText="取消"
      >
        <Form form={form} layout="vertical">
          <Form.Item label="邮箱/账号" name="email" rules={[{ required: true, message: '请输入账号' }]}>
            <Input />
          </Form.Item>
          <Form.Item label="名称" name="label">
            <Input />
          </Form.Item>
          <Form.Item label="状态" name="status">
            <Select options={[{ label: '启用', value: 'active' }, { label: '停用', value: 'disabled' }]} />
          </Form.Item>
          <Form.Item label="池状态" name="poolStatus">
            <Select options={[
              { label: '可用', value: 'ready' },
              { label: '已尽', value: 'exhausted' },
              { label: '冷却中', value: 'cooldown' }
            ]} />
          </Form.Item>
          <Form.Item label="最大发放次数" name="maxUses">
            <InputNumber min={1} max={9999} />
          </Form.Item>
          <Form.Item label="登录密钥/密码" name="credential">
            <Input.Password placeholder="按接口默认自动填充，支持直接修改" />
          </Form.Item>
          <Form.Item label="Cursor Token JSON" name="token">
            <Input.TextArea rows={5} placeholder={DEFAULT_CURSOR_TOKEN_JSON} />
          </Form.Item>
          <Form.Item label="Cursor Profile" name="cursorProfile">
            <Input />
          </Form.Item>
          <Form.Item label="标签" name="tags">
            <Select mode="tags" tokenSeparators={[',']} />
          </Form.Item>
          <Form.Item label="备注" name="notes">
            <Input.TextArea rows={3} />
          </Form.Item>
        </Form>
      </Modal>
      <Modal
        title={`查看额度${usageAccount ? ` · ${usageAccount.email || usageAccount.label || ''}` : ''}`}
        open={usageOpen}
        onCancel={() => setUsageOpen(false)}
        footer={[
          <Button key="close" onClick={() => setUsageOpen(false)}>关闭</Button>,
          <Button
            key="refresh"
            type="primary"
            loading={usageLoading}
            icon={<Icons.ReloadOutlined />}
            onClick={() => usageAccount && viewUsage(usageAccount)}
          >
            刷新
          </Button>
        ]}
        width={640}
      >
        {(() => {
          if (usageLoading && !usageData) {
            return <div className="usage-quota-loading">正在查询 Cursor 额度...</div>;
          }
          if (!usageData) return null;
          const summary = usageData.summary || {};
          const period = usageData.periodUsage || {};
          const exhausted = Boolean(summary.exhausted);
          const totalUsed = exhausted ? 100 : usageUsedPct(summary.usedPercent);
          const remaining = exhausted ? 0 : usageUsedPct(summary.remainingPercent);
          const autoUsed = usageUsedPct(summary.autoPercentUsed);
          const apiUsed = usageUsedPct(summary.apiPercentUsed);
          const models = Array.isArray(summary.models) ? summary.models : [];
          const authModels = Array.isArray(summary.authModels) ? summary.authModels : [];
          const spendText = summary.limitCents
            ? `${formatCentsUsd(summary.includedSpendCents)} / ${formatCentsUsd(summary.limitCents)}`
            : formatCentsUsd(summary.includedSpendCents || summary.totalSpendCents);
          return (
            <div className="usage-quota">
              <Space wrap className="usage-quota-tags">
                <Tag>{summary.planName || '未知套餐'}</Tag>
                {exhausted ? <Tag color="red">已用尽</Tag> : (totalUsed !== null && totalUsed >= 80) ? <Tag color="orange">接近上限</Tag> : <Tag color="green">仍有剩余</Tag>}
                {summary.displayMessage ? <Tag>{summary.displayMessage}</Tag> : null}
              </Space>
              <div className="usage-quota-grid">
                <div className="usage-quota-stat">
                  <div className="label">总用量剩余</div>
                  <div className="value">{remaining === null ? '-' : `${Math.round(remaining * 100) / 100}%`}</div>
                  <Progress
                    percent={totalUsed === null ? 0 : totalUsed}
                    showInfo={false}
                    status={exhausted ? 'exception' : (totalUsed !== null && totalUsed >= 80) ? 'active' : 'success'}
                  />
                </div>
                <div className="usage-quota-stat">
                  <div className="label">Auto 已用</div>
                  <div className="value">{formatPercentValue(autoUsed)}</div>
                </div>
                <div className="usage-quota-stat">
                  <div className="label">API 已用</div>
                  <div className="value">{formatPercentValue(apiUsed)}</div>
                </div>
                <div className="usage-quota-stat">
                  <div className="label">已用比例</div>
                  <div className="value">{formatPercentValue(totalUsed)}</div>
                </div>
              </div>
              <div className="usage-quota-meta">
                <div>账单周期：{formatUsageTime(summary.billingCycleStart || period.billingCycleStart)} ~ {formatUsageTime(summary.billingCycleEnd || period.billingCycleEnd)}</div>
                <div>套餐价格：{summary.price || '-'}</div>
                <div>本周期花费：{spendText}{summary.remainingCents != null ? `（剩余 ${formatCentsUsd(summary.remainingCents)}）` : ''}</div>
                <div>
                  请求统计：
                  {summary.authRequests
                    ? `${summary.authRequests} 次`
                    : (models.length ? `${models.length} 个模型有用量` : '0 次')}
                  {' / '}
                  tokens {summary.totalTokens || summary.authTokens || 0}
                  {summary.totalCostCents ? ` / 费用 ${formatCentsUsd(summary.totalCostCents)}` : ''}
                </div>
                {authModels.length ? (
                  <div>
                    模型请求：
                    {authModels.slice(0, 8).map(item => `${item.model} ${item.numRequests}次`).join('；')}
                    {authModels.length > 8 ? `；等 ${authModels.length} 项` : ''}
                  </div>
                ) : null}
                {models.length ? (
                  <div>
                    模型用量：
                    {models.slice(0, 8).map(item => `${item.model} ${item.tokens}tok`).join('；')}
                    {models.length > 8 ? `；等 ${models.length} 项` : ''}
                  </div>
                ) : null}
              </div>
            </div>
          );
        })()}
      </Modal>
    </>
  );
}

function Devices() {
  const [items, setItems] = React.useState([]);
  const [loading, setLoading] = React.useState(false);

  async function load() {
    setLoading(true);
    try {
      const data = await api('/api/devices');
      setItems(data.items);
    } catch (error) {
      message.error(error.message);
    } finally {
      setLoading(false);
    }
  }

  React.useEffect(() => {
    load();
  }, []);

  async function setDeviceAdminStatus(record, adminStatus) {
    try {
      await api(`/api/devices/${record.id}`, {
        method: 'PATCH',
        body: JSON.stringify({ adminStatus })
      });
      message.success(adminStatus === 'disabled' ? '设备已停用' : '设备已启用');
      load();
    } catch (error) {
      message.error(error.message);
    }
  }

  return (
    <>
      <div className="toolbar">
        <h2>设备管理</h2>
        <Button icon={<Icons.ReloadOutlined />} onClick={load} loading={loading} />
      </div>
      <div className="table-panel">
        <Table
          rowKey="id"
          loading={loading}
          dataSource={items}
          scroll={{ x: 1080 }}
          columns={[
            { title: '设备 ID', dataIndex: 'id', width: 190 },
            { title: '名称', dataIndex: 'name', width: 180 },
            { title: '版本', dataIndex: 'version', width: 120 },
            {
              title: '在线状态',
              dataIndex: 'status',
              width: 120,
              filters: [
                { text: '在线', value: 'online' },
                { text: '离线', value: 'offline' }
              ],
              onFilter: (value, record) => (record.status || 'offline') === value,
              render: value => <span className={`status-dot ${value}`}>{value === 'online' ? '在线' : '离线'}</span>
            },
            {
              title: '管理状态',
              dataIndex: 'adminStatus',
              width: 120,
              filters: [
                { text: '启用', value: 'enabled' },
                { text: '停用', value: 'disabled' }
              ],
              onFilter: (value, record) => (record.adminStatus || 'enabled') === value,
              render: value => {
                const status = value || 'enabled';
                return <Tag color={status === 'disabled' ? 'red' : 'green'}>{status === 'disabled' ? '停用' : '启用'}</Tag>;
              }
            },
            { title: 'IP', dataIndex: 'ip', width: 150 },
            { title: '当前账号', dataIndex: 'currentAccountId', width: 180, render: value => value || '-' },
            { title: '最后心跳', dataIndex: 'lastSeenAt', width: 180, render: value => value ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : '-' },
            {
              title: '操作',
              width: 130,
              fixed: 'right',
              render: (_, record) => (
                <Space>
                  {(record.adminStatus || 'enabled') === 'disabled' ? (
                    <Button icon={<Icons.CheckCircleOutlined />} onClick={() => setDeviceAdminStatus(record, 'enabled')}>启用</Button>
                  ) : (
                    <Popconfirm title="停用后该设备将不能继续领取账号，确认停用？" onConfirm={() => setDeviceAdminStatus(record, 'disabled')}>
                      <Button danger icon={<Icons.PoweroffOutlined />}>停用</Button>
                    </Popconfirm>
                  )}
                </Space>
              )
            }
          ]}
        />
      </div>
    </>
  );
}

function Events() {
  const [items, setItems] = React.useState([]);
  const [loading, setLoading] = React.useState(false);

  async function load() {
    setLoading(true);
    try {
      const data = await api('/api/events?limit=200');
      setItems(data.items);
    } catch (error) {
      message.error(error.message);
    } finally {
      setLoading(false);
    }
  }

  React.useEffect(() => {
    load();
  }, []);

  return (
    <>
      <div className="toolbar">
        <h2>事件日志</h2>
        <Button icon={<Icons.ReloadOutlined />} onClick={load} loading={loading} />
      </div>
      <div className="table-panel">
        <Table rowKey="id" loading={loading} dataSource={items} columns={eventColumns()} scroll={{ x: 900 }} />
      </div>
    </>
  );
}

function MemberCenter() {
  const [items, setItems] = React.useState([]);
  const [loading, setLoading] = React.useState(false);
  const [modalOpen, setModalOpen] = React.useState(false);
  const [bulkOpen, setBulkOpen] = React.useState(false);
  const [editing, setEditing] = React.useState(null);
  const [selectedRowKeys, setSelectedRowKeys] = React.useState([]);
  const [codeForm] = Form.useForm();
  const [bulkForm] = Form.useForm();

  async function load() {
    setLoading(true);
    try {
      const codes = await api('/api/activation-codes');
      setItems(codes.items || []);
      setSelectedRowKeys(keys => keys.filter(key => (codes.items || []).some(item => item.id === key)));
    } catch (error) {
      message.error(error.message);
    } finally {
      setLoading(false);
    }
  }

  React.useEffect(() => {
    load();
  }, []);

  function openEditor(record) {
    setEditing(record || null);
    codeForm.resetFields();
    codeForm.setFieldsValue(record ? {
      ...record,
      start_time: formatDateTimeSecond(record.start_time),
      end_time: formatDateTimeSecond(record.end_time),
      deviceIdsText: (record.deviceIds || []).join('\n')
    } : {
      status: 'active',
      maxDevices: 1,
      level_name: '普通会员',
      account_type: 'Auto',
      start_time: formatDateTimeSecond(new Date().toISOString()),
      end_time: '2099-12-31 23:59:59'
    });
    setModalOpen(true);
  }

  async function saveCode() {
    const values = await codeForm.validateFields();
    const payload = {
      ...values,
      start_time: normalizeDateTimeSecond(values.start_time),
      end_time: normalizeDateTimeSecond(values.end_time),
      deviceIds: String(values.deviceIdsText || '')
        .split(/\r?\n|,/)
        .map(item => item.trim())
        .filter(Boolean)
    };
    delete payload.deviceIdsText;
    try {
      await api(editing ? `/api/activation-codes/${editing.id}` : '/api/activation-codes', {
        method: editing ? 'PATCH' : 'POST',
        body: JSON.stringify(payload)
      });
      setModalOpen(false);
      message.success('活码已保存');
      load();
    } catch (error) {
      message.error(error.message);
    }
  }

  async function bulkGenerate() {
    const values = await bulkForm.validateFields();
    try {
      await api('/api/activation-codes/bulk-generate', {
        method: 'POST',
        body: JSON.stringify({
          ...values,
          end_time: normalizeDateTimeSecond(values.end_time)
        })
      });
      setBulkOpen(false);
      message.success('活码已生成');
      load();
    } catch (error) {
      message.error(error.message);
    }
  }

  async function remove(record) {
    try {
      await api(`/api/activation-codes/${record.id}`, { method: 'DELETE' });
      message.success('活码已删除');
      load();
    } catch (error) {
      message.error(error.message);
    }
  }

  async function bulkDelete() {
    if (!selectedRowKeys.length) {
      message.warning('请先选择要删除的活码');
      return;
    }
    try {
      const data = await api('/api/activation-codes/bulk-delete', {
        method: 'POST',
        body: JSON.stringify({ ids: selectedRowKeys })
      });
      message.success(`已删除 ${data.removed || selectedRowKeys.length} 个活码`);
      setSelectedRowKeys([]);
      load();
    } catch (error) {
      message.error(error.message);
    }
  }

  async function clearDevices(record) {
    try {
      await api(`/api/activation-codes/${record.id}`, {
        method: 'PATCH',
        body: JSON.stringify({ deviceIds: [] })
      });
      message.success('绑定设备已清空');
      load();
    } catch (error) {
      message.error(error.message);
    }
  }

  return (
    <>
      <div className="toolbar">
        <h2>会员授权</h2>
        <Space>
          <Button icon={<Icons.ReloadOutlined />} onClick={load} loading={loading} />
          {selectedRowKeys.length ? (
            <Popconfirm
              title={`删除选中的 ${selectedRowKeys.length} 个活码？`}
              onConfirm={bulkDelete}
            >
              <Button danger icon={<Icons.DeleteOutlined />}>
                批量删除 ({selectedRowKeys.length})
              </Button>
            </Popconfirm>
          ) : null}
          <Button icon={<Icons.AppstoreAddOutlined />} onClick={() => setBulkOpen(true)}>批量生成</Button>
          <Button type="primary" icon={<Icons.PlusOutlined />} onClick={() => openEditor(null)}>新活码</Button>
        </Space>
      </div>
      <div className="table-panel">
        <Table
          rowKey="id"
          loading={loading}
          dataSource={items}
          rowSelection={{
            fixed: true,
            columnWidth: 52,
            selectedRowKeys,
            onChange: keys => setSelectedRowKeys(keys)
          }}
          scroll={{ x: 1100 }}
          columns={[
            { title: '活码', dataIndex: 'code', width: 240 },
            {
              title: '状态',
              dataIndex: 'status',
              width: 100,
              render: value => <Tag color={value === 'active' ? 'green' : value === 'expired' ? 'orange' : 'default'}>{value}</Tag>
            },
            { title: '会员', dataIndex: 'level_name', width: 150, render: value => value || '-' },
            { title: '账号类型', dataIndex: 'account_type', width: 120, render: value => value || '-' },
            { title: '到期时间', dataIndex: 'end_time', width: 190, render: value => formatDateTimeSecond(value) || '-' },
            { title: '设备', width: 120, render: (_, record) => `${record.useCount || 0}/${record.maxDevices || 1}` },
            { title: '最后使用', dataIndex: 'lastUsedAt', width: 170, render: value => value ? dayjs(value).format('YYYY-MM-DD HH:mm') : '-' },
            {
              title: '操作',
              width: 190,
              fixed: 'right',
              render: (_, record) => (
                <Space>
                  <Button icon={<Icons.EditOutlined />} onClick={() => openEditor(record)} />
                  <Button icon={<Icons.DisconnectOutlined />} onClick={() => clearDevices(record)} />
                  <Popconfirm title="删除这个活码？" onConfirm={() => remove(record)}>
                    <Button danger icon={<Icons.DeleteOutlined />} />
                  </Popconfirm>
                </Space>
              )
            }
          ]}
        />
      </div>
      <Modal
        title={editing ? '编辑活码' : '新活码'}
        open={modalOpen}
        onOk={saveCode}
        onCancel={() => setModalOpen(false)}
        okText="保存"
        cancelText="取消"
        width={720}
      >
        <Form form={codeForm} layout="vertical">
          <Form.Item label="活码" name="code">
            <Input placeholder="留空自动生成" />
          </Form.Item>
          <Form.Item label="状态" name="status">
            <Select options={[
              { label: '有效', value: 'active' },
              { label: '停用', value: 'disabled' },
              { label: '过期', value: 'expired' }
            ]} />
          </Form.Item>
          <Form.Item label="会员名称" name="level_name">
            <Input />
          </Form.Item>
          <Form.Item label="账号类型" name="account_type">
            <Input />
          </Form.Item>
          <Form.Item label="开始时间" name="start_time">
            <Input placeholder="例如：2026-07-23 14:30:00" />
          </Form.Item>
          <Form.Item label="到期时间" name="end_time">
            <Input placeholder="例如：2099-12-31 23:59:59" />
          </Form.Item>
          <Form.Item label="最大绑定设备数" name="maxDevices">
            <InputNumber min={1} max={9999} />
          </Form.Item>
          <Form.Item label="已绑定设备 ID" name="deviceIdsText">
            <Input.TextArea rows={4} />
          </Form.Item>
          <Form.Item label="备注" name="notes">
            <Input.TextArea rows={3} />
          </Form.Item>
        </Form>
      </Modal>
      <Modal
        title="批量生成活码"
        open={bulkOpen}
        onOk={bulkGenerate}
        onCancel={() => setBulkOpen(false)}
        okText="生成"
        cancelText="取消"
      >
        <Form form={bulkForm} layout="vertical" initialValues={{
          count: 10,
          prefix: 'CLA',
          status: 'active',
          level_name: '普通会员',
          account_type: 'Auto',
          maxDevices: 1,
          end_time: '2099-12-31 23:59:59'
        }}>
          <Form.Item label="数量" name="count" rules={[{ required: true, message: '请输入数量' }]}>
            <InputNumber min={1} max={500} />
          </Form.Item>
          <Form.Item label="前缀" name="prefix">
            <Input />
          </Form.Item>
          <Form.Item label="会员名称" name="level_name">
            <Input />
          </Form.Item>
          <Form.Item label="账号类型" name="account_type">
            <Input />
          </Form.Item>
          <Form.Item label="到期时间" name="end_time">
            <Input placeholder="例如：2099-12-31 23:59:59" />
          </Form.Item>
          <Form.Item label="最大绑定设备数" name="maxDevices">
            <InputNumber min={1} max={9999} />
          </Form.Item>
        </Form>
      </Modal>
    </>
  );
}

function eventColumns() {
  return [
    { title: '时间', dataIndex: 'createdAt', width: 180, render: value => value ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : '-' },
    { title: '级别', dataIndex: 'level', width: 90, render: value => <Tag color={value === 'error' ? 'red' : value === 'warning' ? 'orange' : 'blue'}>{value}</Tag> },
    { title: '类型', dataIndex: 'type', width: 120 },
    { title: '消息', dataIndex: 'message' },
    { title: '账号', dataIndex: 'accountId', width: 180, render: value => value || '-' },
    { title: '设备', dataIndex: 'deviceId', width: 180, render: value => value || '-' }
  ];
}

function Users() {
  const [items, setItems] = React.useState([]);
  const [loading, setLoading] = React.useState(false);
  const [modalOpen, setModalOpen] = React.useState(false);
  const [editing, setEditing] = React.useState(null);
  const [form] = Form.useForm();

  async function load() {
    setLoading(true);
    try {
      const data = await api('/api/users');
      setItems(data.items || []);
    } catch (error) {
      message.error(error.message);
    } finally {
      setLoading(false);
    }
  }

  React.useEffect(() => {
    load();
  }, []);

  function openEditor(record) {
    setEditing(record || null);
    form.resetFields();
    form.setFieldsValue(record ? {
      username: record.username,
      role: record.role || 'admin',
      password: ''
    } : {
      role: 'admin'
    });
    setModalOpen(true);
  }

  async function save() {
    const values = await form.validateFields();
    try {
      await api(editing ? `/api/users/${editing.id}` : '/api/users', {
        method: editing ? 'PATCH' : 'POST',
        body: JSON.stringify(values)
      });
      setModalOpen(false);
      message.success('用户已保存');
      load();
    } catch (error) {
      message.error(error.message);
    }
  }

  async function remove(record) {
    try {
      await api(`/api/users/${record.id}`, { method: 'DELETE' });
      message.success('用户已删除');
      load();
    } catch (error) {
      message.error(error.message);
    }
  }

  return (
    <>
      <div className="toolbar">
        <h2>用户管理</h2>
        <Space>
          <Button icon={<Icons.ReloadOutlined />} onClick={load} loading={loading} />
          <Button type="primary" icon={<Icons.UserAddOutlined />} onClick={() => openEditor(null)}>新增用户</Button>
        </Space>
      </div>
      <div className="table-panel">
        <Table
          rowKey="id"
          loading={loading}
          dataSource={items}
          columns={[
            { title: '用户名', dataIndex: 'username' },
            { title: '角色', dataIndex: 'role', width: 140, render: value => <Tag color="blue">{value || 'admin'}</Tag> },
            { title: '创建时间', dataIndex: 'createdAt', width: 180, render: value => value ? dayjs(value).format('YYYY-MM-DD HH:mm') : '-' },
            { title: '更新时间', dataIndex: 'updatedAt', width: 180, render: value => value ? dayjs(value).format('YYYY-MM-DD HH:mm') : '-' },
            {
              title: '操作',
              width: 150,
              fixed: 'right',
              render: (_, record) => (
                <Space>
                  <Button icon={<Icons.EditOutlined />} onClick={() => openEditor(record)} />
                  <Popconfirm title="删除这个用户？" onConfirm={() => remove(record)}>
                    <Button danger icon={<Icons.DeleteOutlined />} />
                  </Popconfirm>
                </Space>
              )
            }
          ]}
        />
      </div>
      <Modal
        title={editing ? '编辑用户' : '新增用户'}
        open={modalOpen}
        onOk={save}
        onCancel={() => setModalOpen(false)}
        okText="保存"
        cancelText="取消"
      >
        <Form form={form} layout="vertical">
          <Form.Item label="用户名" name="username" rules={[{ required: true, message: '请输入用户名' }]}>
            <Input />
          </Form.Item>
          <Form.Item
            label={editing ? '新密码' : '密码'}
            name="password"
            rules={editing ? [] : [{ required: true, message: '请输入密码' }]}
          >
            <Input.Password placeholder={editing ? '留空则不修改密码' : ''} />
          </Form.Item>
          <Form.Item label="角色" name="role">
            <Select options={[{ label: '管理员', value: 'admin' }, { label: '操作员', value: 'operator' }]} />
          </Form.Item>
        </Form>
      </Modal>
    </>
  );
}

function LabPanel({ mode = 'burst' }) {
  const isInterval = mode === 'interval';
  const [tasks, setTasks] = React.useState([]);
  const [loading, setLoading] = React.useState(false);
  const [form] = Form.useForm();

  async function load(silent = false) {
    if (!silent) setLoading(true);
    try {
      const data = await api(`/api/lab/tasks?mode=${mode}`);
      setTasks(data.items || []);
    } catch (error) {
      if (!silent) message.error(error.message);
    } finally {
      if (!silent) setLoading(false);
    }
  }

  React.useEffect(() => {
    load();
    const timer = setInterval(() => load(true), 1000);
    return () => clearInterval(timer);
  }, [mode]);

  async function createTask() {
    const values = await form.validateFields();
    try {
      const payload = {
        ...values,
        mode
      };
      if (isInterval) {
        const minSec = Math.max(10, Number(values.intervalMinSec || 60));
        const maxSec = Math.max(minSec, Number(values.intervalMaxSec || minSec));
        payload.intervalMinSec = minSec;
        payload.intervalMaxSec = maxSec;
        payload.intervalMinMs = minSec * 1000;
        payload.intervalMaxMs = maxSec * 1000;
      }
      await api('/api/lab/tasks', {
        method: 'POST',
        body: JSON.stringify(payload)
      });
      form.setFieldsValue({
        title: '',
        count: values.count,
        createCount: values.createCount || 1,
        intervalMinSec: values.intervalMinSec || 60,
        intervalMaxSec: values.intervalMaxSec || values.intervalMinSec || 60
      });
      load(true);
    } catch (error) {
      message.error(error.message);
    }
  }

  async function removeTask(taskId) {
    try {
      await api(`/api/lab/tasks/${taskId}`, { method: 'DELETE' });
      setTasks(list => list.filter(task => task.id !== taskId));
    } catch (error) {
      message.error(error.message);
    }
  }

  function taskStatusText(task) {
    if (task.status === 'cooling' && task.cooldownUntil) {
      const left = Math.max(0, Math.ceil((Number(task.cooldownUntil) - Date.now()) / 1000));
      return `${task.statusText || '冷却中'}，冷却 ${left} 秒`;
    }
    return task.statusText || '-';
  }

  return (
    <>
      <div className="toolbar">
        <h2>{isInterval ? '定时获取' : '实验室'}</h2>
        <Button icon={<Icons.ReloadOutlined />} onClick={() => load()} loading={loading} />
      </div>
      <div className="settings-panel lab-create-panel">
        <Form
          form={form}
          layout="inline"
          initialValues={{
            count: isInterval ? 60 : 10,
            createCount: 1,
            intervalMinSec: 60,
            intervalMaxSec: 120
          }}
          onFinish={createTask}
        >
          <Form.Item label="任务名称" name="title">
            <Input placeholder="可选" />
          </Form.Item>
          <Form.Item label="获取数量" name="count" rules={[{ required: true, message: '请输入数量' }]}>
            <InputNumber min={1} max={9999} precision={0} />
          </Form.Item>
          {isInterval ? (
            <>
              <Form.Item
                label="间隔最小秒"
                name="intervalMinSec"
                rules={[
                  { required: true, message: '请输入最小间隔' },
                  ({ getFieldValue }) => ({
                    validator(_, value) {
                      const maxSec = Number(getFieldValue('intervalMaxSec') || 0);
                      if (value == null || maxSec <= 0 || Number(value) <= maxSec) return Promise.resolve();
                      return Promise.reject(new Error('最小秒不能大于最大秒'));
                    }
                  })
                ]}
              >
                <InputNumber min={10} max={3600} precision={0} />
              </Form.Item>
              <Form.Item
                label="间隔最大秒"
                name="intervalMaxSec"
                rules={[
                  { required: true, message: '请输入最大间隔' },
                  ({ getFieldValue }) => ({
                    validator(_, value) {
                      const minSec = Number(getFieldValue('intervalMinSec') || 0);
                      if (value == null || minSec <= 0 || Number(value) >= minSec) return Promise.resolve();
                      return Promise.reject(new Error('最大秒不能小于最小秒'));
                    }
                  })
                ]}
              >
                <InputNumber min={10} max={3600} precision={0} />
              </Form.Item>
            </>
          ) : (
            <Form.Item label="创建个数" name="createCount">
              <InputNumber min={1} max={200} precision={0} />
            </Form.Item>
          )}
          {!isInterval ? null : (
            <Form.Item label="创建个数" name="createCount">
              <InputNumber min={1} max={50} precision={0} />
            </Form.Item>
          )}
          <Form.Item>
            <Button type="primary" htmlType="submit" icon={<Icons.PlusOutlined />}>
              {isInterval ? '创建定时任务' : '创建任务'}
            </Button>
          </Form.Item>
        </Form>
        {isInterval ? (
          <div className="lab-mode-hint">独立模式：每次获取后，在最小～最大秒之间随机等待再继续，与实验室批量模式互不影响。</div>
        ) : (
          <div className="lab-mode-hint">批量模式：按现有冷却策略尽快拉取到目标数量。</div>
        )}
      </div>
      <div className="lab-grid">
        {tasks.length ? tasks.map(task => {
          const percent = Math.min(100, Math.round((task.done / task.targetCount) * 100));
          const minSec = Math.round(Number(task.intervalMinMs || task.intervalMs || 60000) / 1000);
          const maxSec = Math.round(Number(task.intervalMaxMs || task.intervalMs || minSec * 1000) / 1000);
          const intervalLabel = minSec === maxSec ? `${minSec} 秒` : `${minSec}-${maxSec} 秒随机`;
          return (
            <div className="lab-task-card" key={task.id}>
              <div className="lab-task-head">
                <div>
                  <div className="lab-task-title">{task.title}</div>
                  <div className="lab-task-sub">
                    目标 {task.targetCount} 个 · 已请求 {task.attempts} 次
                    {isInterval ? ` · 间隔 ${intervalLabel}` : ''}
                  </div>
                </div>
                <span className={`lab-run-state ${task.status}`}>{taskStatusText(task)}</span>
              </div>
              <Progress percent={percent} status={task.status === 'error' ? 'exception' : task.status === 'done' ? 'success' : 'active'} />
              <div className="lab-task-meta">
                <span>进度：{task.done}/{task.targetCount}</span>
                <span>新增：{task.created}</span>
                <span>更新：{task.updated}</span>
                <span>最后账号：{task.lastEmail}</span>
              </div>
              {task.error ? <div className="lab-task-error">{task.error}</div> : null}
              <div className="lab-task-actions">
                <Button size="small" icon={<Icons.DeleteOutlined />} onClick={() => removeTask(task.id)}>
                  {task.status === 'running' || task.status === 'cooling' ? '停止并删除' : '删除'}
                </Button>
              </div>
            </div>
          );
        }) : (
          <div className="lab-empty">{isInterval ? '暂无定时获取任务' : '暂无运行任务'}</div>
        )}
      </div>
    </>
  );
}

function Lab() {
  return <LabPanel mode="burst" />;
}

function LabInterval() {
  return <LabPanel mode="interval" />;
}

function Settings() {
  const [form] = Form.useForm();
  const [loading, setLoading] = React.useState(false);

  async function load() {
    setLoading(true);
    try {
      const data = await api('/api/settings');
      form.setFieldsValue(data.settings);
    } catch (error) {
      message.error(error.message);
    } finally {
      setLoading(false);
    }
  }

  React.useEffect(() => {
    load();
  }, []);

  async function save(values) {
    setLoading(true);
    try {
      await api('/api/settings', {
        method: 'PATCH',
        body: JSON.stringify(values)
      });
      message.success('设置已保存');
    } catch (error) {
      message.error(error.message);
    } finally {
      setLoading(false);
    }
  }

  return (
    <>
      <div className="toolbar">
        <h2>助手设置</h2>
        <Button icon={<Icons.ReloadOutlined />} onClick={load} loading={loading} />
      </div>
      <div className="settings-panel">
        <Form form={form} layout="vertical" onFinish={save}>
          <Form.Item label="自动登录" name={['assistant', 'enableAutoLogin']} valuePropName="checked">
            <Switch />
          </Form.Item>
          <Form.Item label="间隔（秒）" name={['assistant', 'pollIntervalSec']}>
            <InputNumber min={3} max={3600} />
          </Form.Item>
          <Form.Item label="单账号最大重试次数" name={['assistant', 'maxRetryPerAccount']}>
            <InputNumber min={0} max={20} />
          </Form.Item>
          <Form.Item label="备注" name={['assistant', 'notes']}>
            <Input.TextArea rows={4} />
          </Form.Item>
          <Button type="primary" htmlType="submit" loading={loading} icon={<Icons.SaveOutlined />}>
            保存
          </Button>
        </Form>
      </div>
    </>
  );
}

function RequestConfig() {
  const [form] = Form.useForm();
  const [loading, setLoading] = React.useState(false);

  async function load() {
    setLoading(true);
    try {
      const data = await api('/api/settings');
      const legacy = data.settings?.legacyRequest || {};
      form.setFieldsValue({
        baseUrl: legacy.baseUrl || 'http://82.157.20.83:9098',
        apiPrefix: legacy.apiPrefix || '/api/cursorLoginZs',
        device_code: legacy.device_code || '',
        device_code_md5: legacy.device_code_md5 || '',
        sharedSecret: legacy.sharedSecret || 'CursorLoginAssistant@2026#Sec',
        current_version: legacy.current_version || '8.3.6',
        activation_code: legacy.activation_code || '',
        pro_type: Number(legacy.pro_type || 0)
      });
    } catch (error) {
      message.error(error.message);
    } finally {
      setLoading(false);
    }
  }

  React.useEffect(() => {
    load();
  }, []);

  async function save(values) {
    setLoading(true);
    try {
      await api('/api/settings', {
        method: 'PATCH',
        body: JSON.stringify({
          legacyRequest: {
            baseUrl: String(values.baseUrl || '').trim().replace(/\/+$/, ''),
            apiPrefix: '/' + String(values.apiPrefix || '').trim().replace(/^\/+|\/+$/g, ''),
            device_code: String(values.device_code || '').trim(),
            device_code_md5: String(values.device_code_md5 || '').trim(),
            sharedSecret: String(values.sharedSecret || ''),
            current_version: String(values.current_version || '8.3.6').trim(),
            activation_code: String(values.activation_code || '').trim(),
            pro_type: Number(values.pro_type || 0)
          }
        })
      });
      message.success('请求配置已保存，获取账号将使用新参数');
      await load();
    } catch (error) {
      message.error(error.message);
    } finally {
      setLoading(false);
    }
  }

  function fillExeDefaults() {
    form.setFieldsValue({
      baseUrl: 'http://82.157.20.83:9098',
      apiPrefix: '/api/cursorLoginZs',
      device_code: 'BFEBFBFF000B0671-CN54XM200FR-110865ea0a66425f83b854f470a97ab9',
      device_code_md5: '35a56e6c1c6ea14209258b10db8861a9',
      sharedSecret: 'CursorLoginAssistant@2026#Sec',
      current_version: '8.3.6',
      pro_type: 0
    });
  }

  return (
    <>
      <div className="toolbar">
        <div>
          <h2>请求配置</h2>
          <Typography.Text type="secondary">
            配置拉取上游账号（getCredentialsSecure）使用的参数，与 CursorLoginAssistant-836.exe 自动读取逻辑对齐。
          </Typography.Text>
        </div>
        <Space>
          <Button onClick={fillExeDefaults}>填入 exe 默认值</Button>
          <Button icon={<Icons.ReloadOutlined />} onClick={load} loading={loading} />
        </Space>
      </div>
      <div className="settings-panel">
        <Form form={form} layout="vertical" onFinish={save}>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
            <Form.Item
              label="旧服务地址"
              name="baseUrl"
              rules={[{ required: true, message: '请输入服务地址' }]}
            >
              <Input placeholder="http://82.157.20.83:9098" />
            </Form.Item>
            <Form.Item
              label="接口前缀"
              name="apiPrefix"
              rules={[{ required: true, message: '请输入接口前缀' }]}
            >
              <Input placeholder="/api/cursorLoginZs" />
            </Form.Item>
            <Form.Item
              label="device_code（硬件序列号）"
              name="device_code"
              rules={[{ required: true, message: '请输入 device_code' }]}
              style={{ gridColumn: '1 / -1' }}
            >
              <Input.TextArea rows={2} placeholder="cpu-board-uuid32" />
            </Form.Item>
            <Form.Item
              label='device_code_md5（MD5("sky_cc"+device_code)）'
              name="device_code_md5"
              rules={[{ required: true, message: '请输入 device_code_md5' }]}
            >
              <Input />
            </Form.Item>
            <Form.Item
              label="共享密钥（AES-128-CBC）"
              name="sharedSecret"
              rules={[{ required: true, message: '请输入共享密钥' }]}
            >
              <Input />
            </Form.Item>
            <Form.Item label="当前版本" name="current_version">
              <Input placeholder="8.3.6" />
            </Form.Item>
            <Form.Item label="激活码（可选）" name="activation_code">
              <Input placeholder="已激活设备可留空" />
            </Form.Item>
            <Form.Item label="pro_type" name="pro_type">
              <Select
                options={[
                  { value: 0, label: '0 Auto' },
                  { value: 1, label: '1 Pro' }
                ]}
              />
            </Form.Item>
          </div>
          <Space>
            <Button type="primary" htmlType="submit" loading={loading} icon={<Icons.SaveOutlined />}>
              保存配置
            </Button>
          </Space>
        </Form>
      </div>
    </>
  );
}

function Shell({ user, onLogout }) {
  const [active, setActive] = React.useState('dashboard');

  const pages = {
    dashboard: <Dashboard />,
    accounts: <Accounts />,
    members: <MemberCenter />,
    users: <Users />,
    devices: <Devices />,
    events: <Events />,
    lab: <Lab />,
    'lab-interval': <LabInterval />,
    'request-config': <RequestConfig />,
    settings: <Settings />
  };

  return (
    <Layout className="app-shell">
      <Sider breakpoint="lg" collapsedWidth="0">
        <div className="app-logo">Cursor 登录助手</div>
        <Menu
          theme="dark"
          mode="inline"
          selectedKeys={[active]}
          onClick={item => setActive(item.key)}
          items={[
            { key: 'dashboard', icon: <Icons.BarChartOutlined />, label: '总览' },
            { key: 'accounts', icon: <Icons.UserOutlined />, label: '账号' },
            { key: 'members', icon: <Icons.KeyOutlined />, label: '会员授权' },
            { key: 'users', icon: <Icons.TeamOutlined />, label: '用户' },
            { key: 'devices', icon: <Icons.DesktopOutlined />, label: '设备' },
            { key: 'events', icon: <Icons.ClockCircleOutlined />, label: '日志' },
            { key: 'lab', icon: <LabIcon />, label: '实验室' },
            { key: 'lab-interval', icon: <IntervalLabIcon />, label: '定时获取' },
            { key: 'request-config', icon: <RequestConfigIcon />, label: '请求配置' },
            { key: 'settings', icon: <Icons.SettingOutlined />, label: '设置' }
          ]}
        />
      </Sider>
      <Layout>
        <Header className="topbar">
          <Typography.Text strong>{user?.username || 'admin'}</Typography.Text>
          <Button icon={<Icons.LogoutOutlined />} onClick={onLogout}>退出</Button>
        </Header>
        <Content className="content">{pages[active]}</Content>
      </Layout>
    </Layout>
  );
}

function Root() {
  const [user, setUser] = React.useState(null);
  const token = localStorage.getItem('cla_token');

  React.useEffect(() => {
    if (!token) return;
    api('/api/admin/me')
      .then(data => setUser(data.user))
      .catch(() => localStorage.removeItem('cla_token'));
  }, []);

  function logout() {
    localStorage.removeItem('cla_token');
    setUser(null);
  }

  return (
    <ConfigProvider theme={{ token: { borderRadius: 8, colorPrimary: '#2563eb' } }}>
      <App>
        {token && user ? <Shell user={user} onLogout={logout} /> : <Login onLogin={setUser} />}
      </App>
    </ConfigProvider>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<Root />);
