// Node.js 22+. One submission per run; a saved job can be resumed without a POST.
import { readFile, writeFile } from 'node:fs/promises';
class WorkflowError extends Error {}
const base = 'https://label-api.gookit.co/api/v1';
const key = process.env.GOOKIT_API_KEY;
const [mode, input, output = 'labels.pdf'] = process.argv.slice(2);
const pause = ms => new Promise(resolve => setTimeout(resolve, ms));
async function api(path, body) {
  return fetch(base + path, {
    method: body ? 'POST' : 'GET',
    headers: { Authorization: `Bearer ${key}`, ...(body ? { 'Content-Type': 'application/json' } : {}) },
    body: body ? JSON.stringify(body) : undefined,
    signal: AbortSignal.timeout(30_000),
  });
}
async function main() {
  if (!key || !input || !['submit', 'resume'].includes(mode)) {
    throw new WorkflowError('Use GOOKIT_API_KEY and: node labels.mjs submit batch.json [output.pdf] OR resume job.json [output.pdf]');
  }
  let jobId;
  if (mode === 'submit') {
    const body = JSON.parse(await readFile(input, 'utf8'));
    const quantities = body.items?.map(item => item.quantity);
    if (!quantities?.length || quantities.some(q => !Number.isSafeInteger(q) || q < 1) || quantities.reduce((a,b) => a+b, 0) > 1000) {
      throw new WorkflowError('Use positive integer quantities totaling at most 1,000 labels.');
    }
    const response = await api('/print', body); // Deliberately no POST retry.
    if (response.status !== 202) throw new WorkflowError(`Submission HTTP ${response.status}; inspect the cause before submitting again.`);
    ({ jobId } = await response.json());
    if (typeof jobId !== 'string' || !jobId) throw new WorkflowError('Accepted response missing jobId; reconcile before another submission.');
    await writeFile(output + '.job.json', JSON.stringify({ jobId }) + '\n', { mode: 0o600 });
    console.log(`Accepted. Resume state saved to ${output}.job.json`);
  } else {
    ({ jobId } = JSON.parse(await readFile(input, 'utf8')));
    if (typeof jobId !== 'string' || !jobId) throw new WorkflowError('Missing saved jobId.');
  }
  const deadline = Date.now() + 120_000;
  while (Date.now() < deadline) {
    const response = await api('/print/' + encodeURIComponent(jobId));
    let waitMs = 2000;
    if (response.status === 429) {
      const seconds = Number(response.headers.get('retry-after') ?? 2);
      if (!Number.isFinite(seconds) || seconds < 0) throw new WorkflowError('Invalid Retry-After; check the saved job later.');
      waitMs = Math.max(1, seconds) * 1000;
    } else {
      if (!response.ok) throw new WorkflowError(`Status HTTP ${response.status}; keep the saved job and investigate.`);
      const job = await response.json();
      if (job.status === 'completed') {
        if (typeof job.url !== 'string' || new URL(job.url).protocol !== 'https:') throw new WorkflowError('Missing HTTPS output URL.');
        const pdf = await fetch(job.url, { signal: AbortSignal.timeout(60_000) }); // No API key on storage requests.
        if (!pdf.ok) throw new WorkflowError(`Download HTTP ${pdf.status}; resume the same job.`);
        const bytes = Buffer.from(await pdf.arrayBuffer());
        if (bytes.subarray(0, 5).toString() !== '%PDF-') throw new WorkflowError('Download was not a PDF.');
        await writeFile(output, bytes);
        console.log(`Saved ${output}`);
        return;
      }
      if (job.status === 'failed') throw new WorkflowError('Job failed; inspect its error through the API before another submission.');
      if (!['pending', 'processing'].includes(job.status)) throw new WorkflowError('Unknown job status; stop and investigate.');
    }
    if (Date.now() + waitMs >= deadline) break;
    await pause(waitMs);
  }
  throw new WorkflowError('Polling deadline reached. Resume the saved job later; do not submit the batch again.');
}
main().catch(error => {
  // Avoid logging fetch exceptions containing a signed download URL or secrets.
  console.error(error instanceof WorkflowError ? error.message : 'Stopped. Check the HTTP response or saved job locally. An interrupted submission may already have been accepted; do not blindly repeat it.');
  process.exitCode = 1;
});
