/* ═══════════════════════════════════════════════════════
   14  RAG 관리 (KB Admin)
   ══════════════════════════════════════════════════════ */

let kbToken = sessionStorage.getItem('kbToken') || '';
let kbExpiresAt = sessionStorage.getItem('kbExpiresAt') || '';
let kbCollections = [];
let kbPollTimer = null;

function kbHeaders(extra) {
  const h = { ...(extra || {}) };
  if (kbToken) h['X-Kb-Token'] = kbToken;
  return h;
}

async function kbFetch(method, path, body, opt) {
  const init = { method, headers: kbHeaders({ 'Content-Type': 'application/json' }), ...(opt || {}) };
  if (body !== undefined && body !== null) init.body = JSON.stringify(body);
  const res = await fetch(path, init);
  let data = null;
  try { data = await res.json(); } catch { /* ignore */ }
  return { ok: res.ok, status: res.status, data };
}

// ── 탭 진입 시 세션 검증 ─────────────────────────────
paneInit.kbadmin = async function() {
  if (kbToken) {
    const r = await kbFetch('GET', '/api/kb/auth/status');
    if (r.ok && r.data && r.data.valid) {
      kbShowMain();
    } else {
      kbShowLogin('세션이 만료되었습니다. 다시 로그인하세요.');
    }
  } else {
    kbShowLogin('');
  }
};

function kbShowLogin(msg) {
  kbToken = ''; kbExpiresAt = '';
  sessionStorage.removeItem('kbToken'); sessionStorage.removeItem('kbExpiresAt');
  document.getElementById('kb-login-card').classList.remove('hidden');
  document.getElementById('kb-main').classList.add('hidden');
  const m = document.getElementById('kb-login-msg');
  if (m) m.textContent = msg || '';
  kbStopPoll();
}

function kbShowMain() {
  document.getElementById('kb-login-card').classList.add('hidden');
  document.getElementById('kb-main').classList.remove('hidden');
  kbUpdateSessionInfo();
  kbLoadCollections().then(() => kbRefresh());
  kbStartPoll();
}

function kbUpdateSessionInfo() {
  const el = document.getElementById('kb-session-info');
  if (!el) return;
  if (kbExpiresAt) {
    const t = new Date(kbExpiresAt);
    el.textContent = `세션 만료: ${t.toLocaleTimeString('ko-KR')}`;
  } else {
    el.textContent = '세션: --:--';
  }
}

async function kbLogin() {
  const pw = document.getElementById('kb-pw').value;
  const msg = document.getElementById('kb-login-msg');
  if (!pw) { msg.textContent = '비밀번호를 입력하세요.'; return; }
  msg.textContent = '로그인 중...';
  const r = await fetch('/api/kb/auth/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ password: pw })
  });
  const data = await r.json().catch(() => ({}));
  if (!r.ok || !data.success) {
    msg.textContent = '❌ ' + (data.error || '로그인 실패');
    return;
  }
  kbToken = data.token;
  kbExpiresAt = data.expiresAt;
  sessionStorage.setItem('kbToken', kbToken);
  sessionStorage.setItem('kbExpiresAt', kbExpiresAt || '');
  document.getElementById('kb-pw').value = '';
  msg.textContent = '';
  kbShowMain();
}

async function kbLogout() {
  if (kbToken) await kbFetch('POST', '/api/kb/auth/logout');
  kbShowLogin('로그아웃되었습니다.');
}

// ── 컬렉션 ─────────────────────────────────────────────
async function kbLoadCollections() {
  const r = await kbFetch('GET', '/api/kb/collections');
  if (!r.ok || !r.data || !r.data.success) return;
  kbCollections = r.data.items || [];
  const fSel = document.getElementById('kb-f-coll');
  const uSel = document.getElementById('kb-up-coll');
  fSel.innerHTML = '<option value="">전체</option>' +
    kbCollections.map(c => `<option value="${c.key}">${c.name}</option>`).join('');
  uSel.innerHTML = '<option value="">-- 선택 --</option>' +
    kbCollections.map(c => `<option value="${c.key}">${c.name}</option>`).join('');
}

// ── 목록 ───────────────────────────────────────────────
async function kbRefresh() {
  const coll = document.getElementById('kb-f-coll').value;
  const status = document.getElementById('kb-f-status').value;
  const q = document.getElementById('kb-f-q').value.trim();
  const qs = new URLSearchParams();
  if (coll) qs.set('collection', coll);
  if (status) qs.set('status', status);
  if (q) qs.set('q', q);
  qs.set('pageSize', '200');

  const r = await kbFetch('GET', '/api/kb/documents?' + qs.toString());
  if (!r.ok || !r.data || !r.data.success) {
    document.getElementById('kb-doc-table').innerHTML = '<div class="placeholder">조회 실패</div>';
    return;
  }
  kbRenderDocs(r.data.items, r.data.total);
}

function kbRenderDocs(items, total) {
  const stats = document.getElementById('kb-doc-stats');
  stats.textContent = `총 ${total}건`;
  const tbl = document.getElementById('kb-doc-table');
  if (!items || items.length === 0) {
    tbl.innerHTML = '<div class="placeholder">문서 없음</div>';
    return;
  }
  const collMap = Object.fromEntries(kbCollections.map(c => [c.key, c.name]));
  const rows = items.map(d => {
    const tags = (d.tags || []).map(t => `<span class="kb-tag">${t}</span>`).join(' ');
    const dt = d.uploadedAt ? new Date(d.uploadedAt).toLocaleString('ko-KR') : '';
    const size = d.fileSize ? kbFmtSize(d.fileSize) : '';
    return `<tr>
      <td class="mono">${kbShortId(d.id)}</td>
      <td>${kbEscape(d.title)}</td>
      <td>${collMap[d.collection] || d.collection}</td>
      <td>${tags}</td>
      <td class="mono">${size}</td>
      <td><span class="kb-status kb-st-${d.status}">${d.status}</span>${d.errorMessage ? `<div class="kb-err" title="${kbEscape(d.errorMessage)}">${kbEscape(d.errorMessage.slice(0,60))}…</div>`:''}</td>
      <td class="mono">${d.chunkCount || 0}</td>
      <td class="mono">${dt}</td>
      <td>
        <button class="btn-b btn-sm" onclick="kbDownload('${d.id}')">⬇</button>
        ${(d.chunkCount || 0) > 0 ? `<button class="btn-b btn-sm" onclick="kbShowChunks('${d.id}','${kbEscape(d.title)}')">🔍</button>` : ''}
        <button class="btn-b btn-sm" onclick="kbReindex('${d.id}')">↻</button>
        <button class="btn-b btn-sm" onclick="kbDisable('${d.id}')">🚫</button>
        <button class="btn-b btn-sm" onclick="kbDelete('${d.id}','${kbEscape(d.title)}')">✖</button>
      </td>
    </tr>`;
  }).join('');
  tbl.innerHTML = `<table class="tbl kb-doc-tbl"><thead><tr>
    <th>ID</th><th>제목</th><th>컬렉션</th><th>태그</th><th>크기</th>
    <th>상태</th><th>청크</th><th>업로드</th><th>액션</th>
    </tr></thead><tbody>${rows}</tbody></table>`;
}

function kbShortId(id) { return (id || '').replace(/-/g, '').slice(0, 8); }
function kbEscape(s) { return String(s == null ? '' : s).replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c]); }
function kbFmtSize(n) {
  if (n < 1024) return n + 'B';
  if (n < 1024 * 1024) return (n / 1024).toFixed(1) + 'K';
  if (n < 1024 * 1024 * 1024) return (n / 1024 / 1024).toFixed(1) + 'M';
  return (n / 1024 / 1024 / 1024).toFixed(2) + 'G';
}

// ── 업로드 모달 ────────────────────────────────────────
function kbUploadOpen() {
  document.getElementById('kb-up-msg').textContent = '';
  document.getElementById('kb-up-title').value = '';
  document.getElementById('kb-up-tags').value = '';
  document.getElementById('kb-up-file').value = '';
  document.getElementById('kb-upload-modal').classList.remove('hidden');
}
function kbUploadClose() {
  document.getElementById('kb-upload-modal').classList.add('hidden');
}
async function kbUploadSubmit() {
  const coll = document.getElementById('kb-up-coll').value;
  const title = document.getElementById('kb-up-title').value.trim();
  const tags = document.getElementById('kb-up-tags').value.trim();
  const fileInput = document.getElementById('kb-up-file');
  const msg = document.getElementById('kb-up-msg');
  if (!coll) { msg.textContent = '❌ 컬렉션을 선택하세요.'; return; }
  if (!fileInput.files || fileInput.files.length === 0) { msg.textContent = '❌ 파일을 선택하세요.'; return; }
  const fd = new FormData();
  fd.append('file', fileInput.files[0]);
  fd.append('collectionKey', coll);
  if (title) fd.append('title', title);
  if (tags) fd.append('tags', tags);
  msg.textContent = '업로드 중...';
  const r = await fetch('/api/kb/upload', { method: 'POST', headers: kbHeaders(), body: fd });
  const data = await r.json().catch(() => ({}));
  if (!r.ok || !data.success) {
    msg.textContent = '❌ ' + (data.error || ('HTTP ' + r.status));
    return;
  }
  msg.textContent = '✅ 업로드 완료. 인덱싱 진행 중...';
  setTimeout(() => kbUploadClose(), 600);
  kbRefresh();
}

// ── 청크 미리보기 ─────────────────────────────────────
async function kbShowChunks(id, title) {
  const modal = document.getElementById('kb-chunk-modal');
  const titleEl = document.getElementById('kb-chunk-title');
  const body = document.getElementById('kb-chunk-body');
  titleEl.textContent = `🔍 청크 미리보기 — ${title || ''}`;
  body.innerHTML = '<div class="placeholder">불러오는 중...</div>';
  modal.classList.remove('hidden');

  const r = await kbFetch('GET', '/api/kb/documents/' + id + '/chunks?limit=200');
  if (!r.ok || !r.data || !r.data.success) {
    body.innerHTML = '<div class="placeholder">조회 실패: ' + ((r.data && r.data.error) || r.status) + '</div>';
    return;
  }
  kbRenderChunks(r.data.chunks || []);
}

function kbChunkCloseModal() {
  document.getElementById('kb-chunk-modal').classList.add('hidden');
}

function kbRenderChunks(chunks) {
  const body = document.getElementById('kb-chunk-body');
  if (!chunks.length) {
    body.innerHTML = '<div class="placeholder">청크 없음</div>';
    return;
  }
  body.innerHTML = `<div class="kb-chunk-stat">총 ${chunks.length}개 청크</div>` +
    chunks.map((c, i) => {
      const kind = c.chunk_kind || 'text';
      const loc = c.locator
        || [c.sheet, c.page ? 'p' + c.page : '', c.heading_path].filter(Boolean).join(' · ');
      const text = c.text || '';
      const preview = text.length > 240 ? text.slice(0, 240) + '…' : text;
      return `<div class="kb-chunk-card">
        <div class="kb-chunk-head" onclick="this.parentElement.classList.toggle('open')">
          <span class="kb-chunk-badge kb-chunk-kind-${kbEscape(kind)}">${kbEscape(kind)}</span>
          <span class="kb-chunk-locator">${kbEscape(loc) || '#' + (i + 1)}</span>
          <span class="kb-chunk-len">${text.length}자</span>
        </div>
        <div class="kb-chunk-preview">${kbEscape(preview)}</div>
        <div class="kb-chunk-full"><pre>${kbEscape(text)}</pre></div>
      </div>`;
    }).join('');
}

// ── 액션 ───────────────────────────────────────────────
function kbDownload(id) {
  window.open('/api/kb/download/' + id, '_blank');
}

async function kbReindex(id) {
  if (!confirm('재인덱싱하시겠습니까? (Qdrant 기존 청크 삭제 후 다시 처리)')) return;
  const r = await kbFetch('POST', '/api/kb/documents/' + id + '/reindex');
  if (!r.ok) alert('실패: ' + (r.data && r.data.error ? r.data.error : r.status));
  kbRefresh();
}

async function kbDisable(id) {
  if (!confirm('이 문서를 비활성화하시겠습니까?')) return;
  const r = await kbFetch('POST', '/api/kb/documents/' + id + '/disable');
  if (!r.ok) alert('실패');
  kbRefresh();
}

async function kbDelete(id, title) {
  if (!confirm(`삭제하시겠습니까?\n${title}\n(Qdrant 청크와 원본 파일도 함께 삭제됩니다)`)) return;
  const r = await kbFetch('DELETE', '/api/kb/documents/' + id);
  if (!r.ok) alert('실패');
  kbRefresh();
}

async function kbBulkDisable() {
  const title = prompt('일괄 비활성화할 제목을 정확히 입력하세요:');
  if (!title) return;
  const r = await kbFetch('POST', '/api/kb/documents/bulk-disable', { title });
  if (r.ok && r.data && r.data.success) alert(`${r.data.affected}건 비활성화 완료`);
  else alert('실패');
  kbRefresh();
}

async function kbPurgeDisabled() {
  const ds = prompt('비활성화 후 며칠 지난 문서를 영구삭제할까요? (공백이면 모든 disabled 삭제)', '90');
  let body = {};
  if (ds && ds.trim()) {
    const n = parseInt(ds, 10);
    if (isNaN(n) || n < 0) { alert('숫자를 입력하세요.'); return; }
    body.olderThanDays = n;
  }
  if (!confirm('정말 영구삭제하시겠습니까? (되돌릴 수 없습니다)')) return;
  const r = await kbFetch('POST', '/api/kb/documents/purge-disabled', body);
  if (r.ok && r.data && r.data.success) alert(`${r.data.deleted}건 영구삭제 완료`);
  else alert('실패');
  kbRefresh();
}

// ── 비밀번호 변경 ──────────────────────────────────────
function kbChangePwOpen() {
  document.getElementById('kb-pw-old').value = '';
  document.getElementById('kb-pw-new').value = '';
  document.getElementById('kb-pw-msg').textContent = '';
  document.getElementById('kb-pw-modal').classList.remove('hidden');
}
function kbChangePwClose() {
  document.getElementById('kb-pw-modal').classList.add('hidden');
}
async function kbChangePwSubmit() {
  const oldPw = document.getElementById('kb-pw-old').value;
  const newPw = document.getElementById('kb-pw-new').value;
  const msg = document.getElementById('kb-pw-msg');
  if (!oldPw || !newPw) { msg.textContent = '❌ 비밀번호를 입력하세요.'; return; }
  if (newPw.length < 6) { msg.textContent = '❌ 새 비밀번호는 6자 이상.'; return; }
  const r = await kbFetch('POST', '/api/kb/auth/change-password', { oldPassword: oldPw, newPassword: newPw });
  if (r.ok && r.data && r.data.success) {
    msg.textContent = '✅ 변경 완료. 다시 로그인해 주세요.';
    setTimeout(() => { kbChangePwClose(); kbLogout(); }, 800);
  } else {
    msg.textContent = '❌ ' + (r.data && r.data.error ? r.data.error : '변경 실패');
  }
}

// ── 진행률 폴링 (활성 ingest가 있으면 1초마다 새로고침) ─
function kbStartPoll() {
  kbStopPoll();
  kbPollTimer = setInterval(async () => {
    if (!kbToken) return;
    if (document.getElementById('pane-kbadmin').classList.contains('active') === false) return;
    const r = await kbFetch('GET', '/api/kb/documents?status=parsing&pageSize=1');
    const r2 = await kbFetch('GET', '/api/kb/documents?status=embedding&pageSize=1');
    const r3 = await kbFetch('GET', '/api/kb/documents?status=pending&pageSize=1');
    const active = [r, r2, r3].some(x => x.ok && x.data && x.data.total > 0);
    if (active) kbRefresh();
  }, 1500);
}
function kbStopPoll() {
  if (kbPollTimer) { clearInterval(kbPollTimer); kbPollTimer = null; }
}

// ── Field Instrument Inference ──────────────────────────────
function kbInferDownload(path) {
  const encoded = encodeURIComponent(path);
  window.open('/api/kb/instruments/download?path=' + encoded, '_blank');
}

async function kbInferStart() {
  const btn = document.getElementById('kb-infer-btn');
  const msg = document.getElementById('kb-infer-msg');
  const result = document.getElementById('kb-infer-result');
  const useLlm = document.getElementById('kb-infer-llm').checked;

  btn.disabled = true;
  btn.textContent = '⏳ 생성 중...';
  msg.textContent = '계기 유추 및 Excel 생성 중 (수십 초 소요)';
  result.classList.add('hidden');

  try {
    const r = await kbFetch('POST', '/api/kb/instruments/infer', {
      useLlm,
      seedDocId: ''
    });

    if (!r.ok || !r.data || !r.data.success) {
      msg.textContent = '❌ ' + (r.data && r.data.error ? r.data.error : 'HTTP ' + r.status);
      return;
    }

    const d = r.data;
    msg.textContent = '✅ 생성 완료. Excel 다운로드 후 수동으로 KB에 업로드하세요.';
    result.classList.remove('hidden');
    result.innerHTML = `
      <div class="kb-infer-stats">
        <span class="kb-stat">계기: ${d.instrumentCount}개</span>
        <span class="kb-stat">동력기기: ${d.powerEquipmentCount || 0}개</span>
        <span class="kb-stat">미매칭: ${d.unmatchedCount}개</span>
      </div>
      <p>${d.message || ''}</p>
      <div class="btn-row">
        <button class="btn-b btn-sm" onclick="kbInferDownload('${d.downloadPath}')">⬇ Excel 다운로드</button>
      </div>
    `;
  } catch (e) {
    msg.textContent = '❌ ' + e.message;
  } finally {
    btn.disabled = false;
    btn.textContent = '⚡ 초안 생성';
  }
}
