<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>财务报销自动化</title>
<style>
:root {
  --bg: #f5f5f5;
  --card: #fff;
  --text: #333;
  --muted: #888;
  --primary: #4f46e5;
  --primary-hover: #4338ca;
  --success: #16a34a;
  --danger: #dc2626;
  --warning: #d97706;
  --border: #e5e7eb;
  --radius: 8px;
  --shadow: 0 1px 3px rgba(0,0,0,.08);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: var(--bg); color: var(--text); line-height: 1.6; }
.container { max-width: 960px; margin: 0 auto; padding: 24px 16px; }
.card { background: var(--card); border-radius: var(--radius); box-shadow: var(--shadow); padding: 24px; margin-bottom: 16px; }
.card h2 { font-size: 18px; margin-bottom: 12px; display: flex; align-items: center; gap: 8px; }
.card h2 .badge { font-size: 12px; padding: 2px 8px; border-radius: 12px; background: var(--primary); color: #fff; }
.upload-zone { border: 2px dashed var(--border); border-radius: var(--radius); padding: 40px 20px; text-align: center; cursor: pointer; transition: border-color .2s; }
.upload-zone:hover, .upload-zone.dragover { border-color: var(--primary); background: #f8f8ff; }
.upload-zone p { color: var(--muted); margin-top: 8px; }
.file-list { margin-top: 12px; }
.file-item { display: flex; align-items: center; gap: 8px; padding: 6px 10px; background: #f9fafb; border-radius: 6px; margin-bottom: 4px; font-size: 14px; }
.file-item .remove { margin-left: auto; cursor: pointer; color: var(--danger); font-size: 16px; }
label { display: block; font-size: 14px; font-weight: 500; margin-bottom: 4px; }
input, textarea { width: 100%; padding: 8px 12px; border: 1px solid var(--border); border-radius: 6px; font-size: 14px; outline: none; transition: border-color .2s; }
input:focus, textarea:focus { border-color: var(--primary); }
textarea { font-family: "SF Mono", "Fira Code", monospace; font-size: 13px; height: 160px; resize: vertical; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 12px; }
.btn { display: inline-flex; align-items: center; gap: 6px; padding: 10px 20px; border: none; border-radius: 6px; font-size: 14px; cursor: pointer; transition: background .2s; }
.btn-primary { background: var(--primary); color: #fff; }
.btn-primary:hover { background: var(--primary-hover); }
.btn-primary:disabled { opacity: .5; cursor: not-allowed; }
.btn-success { background: var(--success); color: #fff; }
.btn-success:hover { background: #15803d; }
.btn-success:disabled { opacity: .5; cursor: not-allowed; }
.actions { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }

.log-panel { background: #1e1e1e; color: #d4d4d4; border-radius: var(--radius); padding: 16px; max-height: 380px; overflow-y: auto; font-family: "SF Mono", "Fira Code", monospace; font-size: 13px; }
.log-panel .entry { margin-bottom: 2px; white-space: pre-wrap; word-break: break-all; }
.log-panel .INFO { color: #6a9955; }
.log-panel .WARNING { color: #d97706; }
.log-panel .ERROR { color: #f87171; }
.log-panel .DEBUG { color: #8b8b8b; }

.status-badge { display: inline-block; padding: 4px 12px; border-radius: 12px; font-size: 12px; font-weight: 500; }
.status-badge.uploaded { background: #e5e7eb; color: #6b7280; }
.status-badge.extracting { background: #dbeafe; color: #2563eb; }
.status-badge.done { background: #dcfce7; color: #16a34a; }
.status-badge.submitting { background: #fef3c7; color: #d97706; }
.status-badge.submitted { background: #dcfce7; color: #16a34a; }
.status-badge.error { background: #fee2e2; color: #dc2626; }

.result-table { width: 100%; border-collapse: collapse; font-size: 13px; margin-top: 8px; }
.result-table th, .result-table td { padding: 8px 10px; border-bottom: 1px solid var(--border); text-align: left; }
.result-table th { background: #f9fafb; font-weight: 500; }

.hidden { display: none !important; }
</style>
</head>
<body>
<div class="container">

  <div class="card">
    <h2>财务报销自动化</h2>
    <p style="color:var(--muted);font-size:14px;">上传 PDF 发票和支付截图，自动提取信息并填报报销系统</p>
  </div>

  <!-- 文件上传 -->
  <div class="card">
    <h2>1. 上传文件</h2>
    <div class="upload-zone" id="uploadZone">
      <div style="font-size:36px;margin-bottom:4px;">&#128194;</div>
      <p>拖拽 PDF 和图片到此处，或点击选择文件</p>
      <input type="file" id="fileInput" multiple accept=".pdf,.png,.jpg,.jpeg,.bmp,.webp" style="display:none">
    </div>
    <div class="file-list" id="fileList"></div>
  </div>

  <!-- 配置 -->
  <div class="card">
    <h2>2. 配置</h2>
    <div class="form-row">
      <div><label>用户名</label><input id="cfgUser" placeholder="工号/学号"></div>
      <div><label>密码</label><input id="cfgPass" type="password" placeholder="登录密码"></div>
    </div>
    <div class="form-row">
      <div><label>默认姓名</label><input id="cfgName" placeholder="报销人姓名"></div>
      <div><label>默认公务卡号</label><input id="cfgCard" placeholder="公务卡号后四位或完整号"></div>
    </div>
    <div><label>默认人员编号</label><input id="cfgPid" placeholder="人员编号"></div>
    <details style="margin-top:8px;">
      <summary style="font-size:14px;color:var(--muted);cursor:pointer;">高级配置 (JSON)</summary>
      <textarea id="cfgJson" style="margin-top:8px;">{}</textarea>
    </details>
  </div>

  <!-- 进度 -->
  <div class="card">
    <h2>3. 处理 <span class="status-badge" id="statusBadge" style="display:none"></span></h2>
    <div class="log-panel" id="logPanel">
      <div class="entry" style="color:var(--muted)">等待上传文件...</div>
    </div>
    <div class="actions">
      <button class="btn btn-primary" id="btnStart" disabled>开始处理</button>
      <button class="btn btn-success hidden" id="btnSubmit">提交报销</button>
      <button class="btn hidden" id="btnDownload" style="background:#6b7280;color:#fff;">下载 CSV</button>
    </div>
    <div id="resultArea" class="hidden" style="margin-top:12px;"></div>
  </div>

</div>

<script>
const $ = s => document.querySelector(s);
const $$ = s => document.querySelectorAll(s);

let sessionId = null;
let files = [];
let evtSource = null;

// ---- 文件选择 ----
const uploadZone = $('#uploadZone');
const fileInput = $('#fileInput');
const fileList = $('#fileList');

uploadZone.addEventListener('click', () => fileInput.click());
uploadZone.addEventListener('dragover', e => { e.preventDefault(); uploadZone.classList.add('dragover'); });
uploadZone.addEventListener('dragleave', () => uploadZone.classList.remove('dragover'));
uploadZone.addEventListener('drop', e => {
  e.preventDefault();
  uploadZone.classList.remove('dragover');
  addFiles(Array.from(e.dataTransfer.files));
});
fileInput.addEventListener('change', () => addFiles(Array.from(fileInput.files)));

function addFiles(newFiles) {
  for (const f of newFiles) {
    if (!files.find(x => x.name === f.name && x.size === f.size)) {
      files.push(f);
    }
  }
  renderFiles();
  checkReady();
}

function removeFile(idx) {
  files.splice(idx, 1);
  renderFiles();
  checkReady();
}

function renderFiles() {
  fileList.innerHTML = files.length === 0 ? '' : files.map((f, i) =>
    `<div class="file-item">
      <span>${['.pdf','.PDF'].some(e => f.name.endsWith(e)) ? '&#128196;' : '&#128444;'}</span>
      <span>${f.name}</span>
      <span style="color:var(--muted);font-size:12px;">${(f.size/1024).toFixed(1)} KB</span>
      <span class="remove" onclick="removeFile(${i})">&times;</span>
    </div>`
  ).join('');
}

// ---- 配置 ----
const cfgUser = $('#cfgUser');
const cfgPass = $('#cfgPass');
const cfgName = $('#cfgName');
const cfgCard = $('#cfgCard');
const cfgPid = $('#cfgPid');
const cfgJson = $('#cfgJson');

[cfgUser, cfgPass, cfgName, cfgCard, cfgPid, cfgJson].forEach(el =>
  el.addEventListener('input', checkReady)
);

function getConfig() {
  let base = {};
  try { base = JSON.parse(cfgJson.value || '{}'); } catch (e) {}
  if (cfgUser.value) base.username = cfgUser.value;
  if (cfgPass.value) base.password = cfgPass.value;
  if (cfgName.value) base.default_name = cfgName.value;
  if (cfgCard.value) base.default_card_no = cfgCard.value;
  if (cfgPid.value) base.default_person_id = cfgPid.value;
  return base;
}

function checkReady() {
  const hasFiles = files.length > 0;
  const hasUser = !!(cfgUser.value && cfgPass.value);
  $('#btnStart').disabled = !(hasFiles && hasUser);
}

// ---- 开始处理 ----
const btnStart = $('#btnStart');
const btnSubmit = $('#btnSubmit');
const btnDownload = $('#btnDownload');
const statusBadge = $('#statusBadge');
const logPanel = $('#logPanel');
const resultArea = $('#resultArea');

btnStart.addEventListener('click', async () => {
  btnStart.disabled = true;
  statusBadge.style.display = 'inline-block';
  statusBadge.textContent = '上传中...';
  statusBadge.className = 'status-badge extracting';

  const formData = new FormData();
  for (const f of files) formData.append('files', f);

  const config = getConfig();
  formData.append('config', JSON.stringify(config));

  try {
    const resp = await fetch('/api/start', { method: 'POST', body: formData });
    const data = await resp.json();
    if (data.error) { alert(data.error); btnStart.disabled = false; return; }
    sessionId = data.session_id;
    startStream();
    btnSubmit.classList.remove('hidden');
    btnDownload.classList.remove('hidden');
  } catch (e) {
    alert('请求失败: ' + e.message);
    btnStart.disabled = false;
  }
});

function startStream() {
  logPanel.innerHTML = '';
  if (evtSource) evtSource.close();
  evtSource = new EventSource(`/api/stream/${sessionId}`);

  evtSource.addEventListener('message', e => {
    const data = JSON.parse(e.data);
    if (data.type === 'log') {
      const lvl = (data.msg.match(/\[(\w+)\]/) || [])[1] || '';
      logPanel.innerHTML += `<div class="entry ${lvl}">${escHtml(data.msg)}</div>`;
      logPanel.scrollTop = logPanel.scrollHeight;
    }
    if (data.type === 'heartbeat' || data.type === 'status') {
      statusBadge.textContent = data.status;
      statusBadge.className = `status-badge ${data.status}`;
    }
    if (data.type === 'complete') {
      statusBadge.textContent = data.status;
      statusBadge.className = `status-badge ${data.status}`;
      evtSource.close();

      if (data.status === 'done') {
        btnSubmit.classList.remove('hidden');
        loadResults();
      }
      btnStart.disabled = data.status !== 'done';
    }
  });
}

function escHtml(s) {
  return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}

async function loadResults() {
  try {
    const resp = await fetch(`/api/status/${sessionId}`);
    const data = await resp.json();
    if (!data.preview) return;

    let html = '<table class="result-table"><thead><tr>';
    const keys = Object.keys(data.preview[0]);
    for (const k of keys) html += `<th>${k}</th>`;
    html += '</tr></thead><tbody>';
    for (const row of data.preview) {
      html += '<tr>';
      for (const k of keys) html += `<td>${row[k] || ''}</td>`;
      html += '</tr>';
    }
    html += '</tbody></table>';
    html += `<p style="margin-top:8px;color:var(--muted);">共 ${data.total} 条记录</p>`;
    resultArea.innerHTML = html;
    resultArea.classList.remove('hidden');
  } catch (e) {}
}

// ---- 浏览器填报 ----
btnSubmit.addEventListener('click', async () => {
  if (!sessionId) return;
  btnSubmit.disabled = true;
  statusBadge.textContent = 'submitting';
  statusBadge.className = 'status-badge submitting';

  try {
    await fetch(`/api/submit/${sessionId}`, { method: 'POST' });
    startStream();
  } catch (e) {
    alert('提交失败: ' + e.message);
    btnSubmit.disabled = false;
  }
});

// ---- 下载 CSV ----
btnDownload.addEventListener('click', () => {
  if (sessionId) window.open(`/api/download/${sessionId}/csv`, '_blank');
});
</script>
</body>
</html>