// Upload API — direct file ingestion for the org console.
//
// Wraps the backend's generic upload endpoint:
//   POST /ingestion/file          multipart {file} → JobResponse {id, status, ...}
//   GET  /ingestion/jobs/{id}     → JobResponse (status: pending|running|done|failed)
//
// The backend whitelists documents + tables (and native db files) and caps a
// single file at 100MB; the same rules are mirrored here so bad picks are
// rejected before any bytes leave the browser. Keep PARCLE_UPLOAD_EXTENSIONS in
// sync with parcle-knowledge's core/ingestion/routing.py (whitelist minus images).

const PARCLE_UPLOAD_MAX_BYTES = 100 * 1024 * 1024;
const PARCLE_UPLOAD_EXTENSIONS = [
  // documents
  '.pdf', '.docx', '.pptx', '.txt', '.md', '.markdown', '.html', '.htm', '.json', '.xml', '.msg',
  // tables (LLM-classified: real datasets become SQL-queryable, the rest are documents)
  '.csv', '.tsv', '.xlsx', '.xlsm',
  // native databases (queryable as-is)
  '.sqlite', '.sqlite3', '.duckdb',
];

function parcleUploadAccept() {
  return PARCLE_UPLOAD_EXTENSIONS.join(',');
}

function parcleUploadExt(name) {
  const m = /\.[^.\/\\]+$/.exec(String(name || '').toLowerCase());
  return m ? m[0] : '';
}

// null when the file is acceptable, else a human-readable reason.
function parcleUploadCheck(file) {
  const ext = parcleUploadExt(file && file.name);
  if (!PARCLE_UPLOAD_EXTENSIONS.includes(ext)) {
    return `Unsupported type ${ext || '(no extension)'}`;
  }
  if (file.size > PARCLE_UPLOAD_MAX_BYTES) {
    return `Over the ${Math.round(PARCLE_UPLOAD_MAX_BYTES / (1024 * 1024))}MB limit`;
  }
  return null;
}

// Multipart with only the auth header — the browser must set Content-Type
// itself to get the boundary.
async function parcleUploadFile(path, file, filename) {
  const form = new FormData();
  form.append('file', file, filename || file.name || 'upload');
  const res = await fetch(parcleApiBase() + path, {
    method: 'POST',
    headers: (typeof parcleAuthHeaders === 'function') ? parcleAuthHeaders() : {},
    body: form,
  });
  if (res.status === 401 && typeof parcleNotifyUnauthorized === 'function') parcleNotifyUnauthorized();
  if (!res.ok) {
    let detail = '';
    try { detail = (await res.json()).detail || ''; } catch (e) {}
    throw new Error(detail || `Upload failed (${res.status})`);
  }
  try { return await res.json(); } catch (e) { return {}; }
}

async function parcleUploadApiDelete(path) {
  const auth = (typeof parcleAuthHeaders === 'function') ? parcleAuthHeaders() : {};
  const res = await fetch(parcleApiBase() + path, { method: 'DELETE', headers: auth });
  if (res.status === 401 && typeof parcleNotifyUnauthorized === 'function') parcleNotifyUnauthorized();
  if (!res.ok) {
    let detail = '';
    try { detail = (await res.json()).detail || ''; } catch (e) {}
    throw new Error(detail || `Delete failed (${res.status})`);
  }
  try { return await res.json(); } catch (e) { return {}; }
}

async function parcleUploadApiGet(path) {
  const auth = (typeof parcleAuthHeaders === 'function') ? parcleAuthHeaders() : {};
  const res = await fetch(parcleApiBase() + path, { headers: auth });
  if (res.status === 401 && typeof parcleNotifyUnauthorized === 'function') parcleNotifyUnauthorized();
  if (!res.ok) {
    let detail = '';
    try { detail = (await res.json()).detail || ''; } catch (e) {}
    throw new Error(detail || `GET ${path} → ${res.status}`);
  }
  return await res.json();
}

const ParcleUploadAPI = {
  ingestFile(file, filename) {
    return parcleUploadFile('/ingestion/file', file, filename);
  },
  job(jobId) {
    return parcleUploadApiGet(`/ingestion/jobs/${encodeURIComponent(jobId)}`);
  },
  // Files uploaded directly (connector content excluded) → {files, total, queryable}.
  // limit=0 returns the counts only, which is all the Connectors card polls for.
  files(limit) {
    const q = (limit === undefined) ? '' : `?limit=${encodeURIComponent(limit)}`;
    return parcleUploadApiGet(`/ingestion/files${q}`);
  },
  // Permanently removes the file and everything ingested from it (backend
  // DELETE /ingestion/files/{id}). Connector-synced content answers 404 —
  // this only ever touches what files() lists.
  deleteFile(fileId) {
    return parcleUploadApiDelete(`/ingestion/files/${encodeURIComponent(fileId)}`);
  },
};

// Broadcast by UploadDrawer whenever its queue changes, so the Connectors-page
// card can show live progress while the drawer is closed.
const PARCLE_UPLOAD_EVENT = 'parcle:upload-changed';

function parcleAnnounceUpload(detail) {
  try { window.dispatchEvent(new CustomEvent(PARCLE_UPLOAD_EVENT, { detail })); }
  catch (e) { /* very old browser — the card falls back to its own poll */ }
}

Object.assign(window, {
  PARCLE_UPLOAD_MAX_BYTES,
  PARCLE_UPLOAD_EXTENSIONS,
  PARCLE_UPLOAD_EVENT,
  parcleUploadAccept,
  parcleUploadExt,
  parcleUploadCheck,
  parcleUploadFile,
  parcleAnnounceUpload,
  ParcleUploadAPI,
});
