/**
 * `forge jobs` — list / inspect / fire / toggle / delete Jobs.
 * Talks to Forge web (default 8403) via the same MW_URL base as the rest
 * of the CLI. Auth is required (admin token) — surface 401 from the API.
 */

interface Job {
  id: string;
  name: string;
  enabled: boolean;
  schedule_interval_minutes: number;
  source_connector: string;
  source_tool: string;
  dispatch_type: 'pipeline' | 'chat';
  last_run_at: string | null;
  next_run_at: string | null;
}

interface JobRun {
  id: string;
  job_id: string;
  started_at: string;
  finished_at: string | null;
  status: 'running' | 'ok' | 'error';
  items_seen: number;
  items_new: number;
  items_dispatched: number;
  error: string | null;
  trigger: 'schedule' | 'manual';
}

const isTty = !!process.stdout.isTTY;
const dim = (s: string) => isTty ? `\x1b[2m${s}\x1b[0m` : s;
const red = (s: string) => isTty ? `\x1b[31m${s}\x1b[0m` : s;
const grn = (s: string) => isTty ? `\x1b[32m${s}\x1b[0m` : s;
const cyan = (s: string) => isTty ? `\x1b[36m${s}\x1b[0m` : s;

function getBase(): string {
  const portArgIdx = process.argv.findIndex(a => a === '--port');
  const port = portArgIdx >= 0 ? process.argv[portArgIdx + 1] : undefined;
  return process.env.MW_URL || `http://localhost:${port || '8403'}`;
}

async function api(path: string, init?: RequestInit): Promise<any> {
  const base = getBase();
  let res: Response;
  try { res = await fetch(base + path, init); }
  catch (e) { throw new Error(`forge unreachable on ${base}: ${(e as Error).message}`); }
  if (!res.ok && res.status !== 202) {
    throw new Error(`HTTP ${res.status}: ${(await res.text().catch(() => '')).slice(0, 200)}`);
  }
  return res.status === 204 ? null : res.json();
}

function statusBadge(s: string): string {
  if (s === 'ok') return grn('✓ ok');
  if (s === 'error') return red('✗ error');
  if (s === 'running') return cyan('… running');
  return s;
}

export async function jobsCommand(args: string[]): Promise<void> {
  const sub = args[0] || 'list';
  switch (sub) {
    case 'list':
    case 'ls':
      return cmdList();
    case 'show': return cmdShow(args[1]);
    case 'runs': return cmdRuns(args[1]);
    case 'run':  return cmdRun(args[1], ...args.slice(2));
    case 'dispatches':
    case 'd':    return cmdDispatches(args[1], args[2]);
    case 'enable':
    case 'disable': return cmdToggle(args[1], sub === 'enable');
    case 'rm':   return cmdRm(args[1]);
    case 'reset':return cmdReset(args[1]);
    case '--help':
    case '-h':
    default:
      return printHelp();
  }
}

async function cmdList(): Promise<void> {
  const j = await api('/api/jobs');
  if (!j?.jobs?.length) { console.log('(no jobs)'); return; }
  for (const job of j.jobs as Job[]) {
    const status = job.enabled ? grn('●') : dim('○');
    const next = job.next_run_at ? dim(`next ${job.next_run_at}`) : dim('next: now');
    const last = job.last_run_at ? dim(`last ${job.last_run_at}`) : dim('never run');
    console.log(`${status} ${cyan(job.id)}  ${job.name}`);
    console.log(`   ${dim(`${job.source_connector}.${job.source_tool}`)} → ${job.dispatch_type} · every ${job.schedule_interval_minutes}m`);
    console.log(`   ${last}  ${next}`);
  }
}

async function cmdShow(id?: string): Promise<void> {
  if (!id) { console.error('Usage: forge jobs show <id>'); process.exit(1); }
  const j = await api(`/api/jobs/${encodeURIComponent(id)}`);
  console.log(JSON.stringify(j, null, 2));
}

async function cmdRuns(id?: string): Promise<void> {
  if (!id) { console.error('Usage: forge jobs runs <id>'); process.exit(1); }
  const j = await api(`/api/jobs/${encodeURIComponent(id)}/runs?limit=20`);
  if (!j?.runs?.length) { console.log('(no runs)'); return; }
  for (const r of j.runs as JobRun[]) {
    const dur = r.finished_at && r.started_at
      ? `${Math.round((new Date(r.finished_at).getTime() - new Date(r.started_at).getTime()) / 100) / 10}s`
      : '…';
    console.log(`${cyan(r.id)} ${r.started_at}  ${statusBadge(r.status)}  ${dim(`(${dur})`)}`);
    console.log(`   seen=${r.items_seen} new=${r.items_new} dispatched=${r.items_dispatched}${r.error ? '  ' + red(r.error.slice(0, 100)) : ''}`);
  }
}

async function cmdDispatches(jobId?: string, runId?: string): Promise<void> {
  if (!jobId || !runId) { console.error('Usage: forge jobs dispatches <jobId> <runId>'); process.exit(1); }
  const j = await api(`/api/jobs/${encodeURIComponent(jobId)}/runs/${encodeURIComponent(runId)}`);
  console.log(`run ${cyan(j.run.id)}  ${statusBadge(j.run.status)}  seen=${j.run.items_seen} new=${j.run.items_new} dispatched=${j.run.items_dispatched}`);
  if (j.run.error) console.log(red('  error: ' + j.run.error));
  if (!j.dispatches?.length) { console.log(dim('  (no dispatches)')); return; }
  for (const d of j.dispatches) {
    const label = d.status === 'dispatched' ? grn('✓') : red('✗');
    console.log(`  ${label} ${cyan(d.item_key)} ${dim('→')} ${d.dispatch_type}:${d.dispatch_target_id || '(none)'}`);
    if (d.item_preview) console.log(dim('    ' + (d.item_preview.slice(0, 100))));
    if (d.error) console.log(red('    error: ' + d.error.slice(0, 200)));
  }
}

async function cmdRun(id?: string, ...rest: string[]): Promise<void> {
  if (!id) { console.error('Usage: forge jobs run <id> [--force]'); process.exit(1); }
  const force = rest.includes('--force') || rest.includes('-f');
  const r = await api(`/api/jobs/${encodeURIComponent(id)}/run${force ? '?reset_dedup=1' : ''}`, { method: 'POST' });
  console.log(`accepted — run_id=${r.run_id}` + (r.dedup_reset ? ` (dedup reset, removed ${r.removed_dedup_keys} keys)` : ''));
  console.log(dim(`watch: forge jobs runs ${id}`));
}

async function cmdToggle(id: string | undefined, enabled: boolean): Promise<void> {
  if (!id) { console.error(`Usage: forge jobs ${enabled ? 'enable' : 'disable'} <id>`); process.exit(1); }
  await api(`/api/jobs/${encodeURIComponent(id)}`, {
    method: 'PATCH',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ enabled }),
  });
  console.log(`${enabled ? 'enabled' : 'disabled'} ${id}`);
}

async function cmdRm(id?: string): Promise<void> {
  if (!id) { console.error('Usage: forge jobs rm <id>'); process.exit(1); }
  await api(`/api/jobs/${encodeURIComponent(id)}`, { method: 'DELETE' });
  console.log('deleted');
}

async function cmdReset(id?: string): Promise<void> {
  if (!id) { console.error('Usage: forge jobs reset <id>'); process.exit(1); }
  const r = await api(`/api/jobs/${encodeURIComponent(id)}/reset_dedup`, { method: 'POST' });
  console.log(`dedup wiped — removed ${r.removed} keys`);
}

function printHelp(): void {
  console.log(`forge jobs — scheduled connector polls

Usage:
  forge jobs                       list jobs
  forge jobs show <id>             full JSON
  forge jobs runs <id>             recent runs (summary line per tick)
  forge jobs dispatches <id> <run> per-item dispatches for one run (target ids)
  forge jobs run <id> [--force]    fire now (manual trigger; --force resets dedup first)
  forge jobs enable <id>           re-enable
  forge jobs disable <id>          stop scheduling
  forge jobs reset <id>            wipe dedup state (re-process everything next tick)
  forge jobs rm <id>               delete

To create a job, use the Forge extension Jobs tab or POST /api/jobs directly
(the YAML-vs-form authoring story lives in the extension).`);
}
