/**
 * POST /api/jobs/<id>/run — manually fire one tick.
 *
 * Inserts the run row synchronously, returns its id (202), and runs the
 * actual work in the background. Poll GET /api/jobs/<id>/runs/<run_id>
 * (or /api/jobs/<id>) to observe completion.
 *
 * Query params:
 *   reset_dedup=1   wipe job_seen before running. Useful for "force re-dispatch
 *                   the same items I tested with last time" while iterating
 *                   on a pipeline. Recorded as note on the run for audit.
 */

import { NextResponse } from 'next/server';
import { prepareRun, executeRun, isJobBusy } from '@/lib/jobs/scheduler';
import { resetDedup } from '@/lib/jobs/store';

export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
  const { id } = await params;
  const url = new URL(req.url);
  const shouldReset = url.searchParams.get('reset_dedup') === '1';

  // Refuse to fire if the Job is mid-tick or, for sequential mode,
  // has a previously-dispatched pipeline still running. Without this
  // double-clicking Force run was queuing identical concurrent ticks
  // that all dispatched the same items in parallel — defeating the
  // sequential safety. The check runs reconcile internally so stale
  // zombies don't block legitimate manual fires.
  const busy = isJobBusy(id);
  if (busy.busy) {
    return NextResponse.json({
      error: `Job is busy: ${busy.reason}. Wait for it to finish before firing again.`,
      busy: true,
      reason: busy.reason,
    }, { status: 409 });
  }

  let removedDedupKeys = 0;
  if (shouldReset) {
    try { removedDedupKeys = resetDedup(id); }
    catch (e) {
      return NextResponse.json({ error: 'reset_dedup failed: ' + (e as Error).message }, { status: 500 });
    }
  }
  let prepared;
  try {
    prepared = prepareRun(id, 'manual');
  } catch (e) {
    return NextResponse.json({ error: (e as Error).message }, { status: 404 });
  }
  // Pass trigger='manual' so executeRun's on_failure=stop gate
  // recognises this as a user-initiated resume and lets it through.
  // Without this the run is treated as scheduled and halts on the
  // last failure forever.
  void executeRun(prepared.job, prepared.runId, 'manual').catch((err) => {
    console.error('[jobs] manual run crashed', err);
  });
  return NextResponse.json({
    accepted: true,
    run_id: prepared.runId,
    dedup_reset: shouldReset,
    removed_dedup_keys: shouldReset ? removedDedupKeys : undefined,
  }, { status: 202 });
}
