/**
 * Job types — see docs/Jobs-Design.md.
 */

export type DispatchType = 'pipeline' | 'chat';
export type JobRunStatus = 'running' | 'ok' | 'error';
export type JobRunTrigger = 'schedule' | 'manual';
export type JobDispatchStatus = 'dispatched' | 'error';

export interface PipelineDispatchParams {
  workflow_name: string;
  project_path: string;
  project_name: string;
  /** Per-item template; string values render `{{item.<path>}}` substitutions. */
  input_template: Record<string, string>;
}

export interface ChatDispatchParams {
  /** Agent profile id (settings.agents); falls back to settings.chatAgent. */
  agent_profile?: string;
  session_title_template?: string;
  message_template: string;
  /** Append every item to one session vs fresh session per item. */
  reuse_session?: boolean;
  /**
   * Mode:
   *   'per-item' (default) — one chat message per new item; message_template
   *                          uses {{item.X}} placeholders.
   *   'summary'            — ALL new items in this tick are bundled into a
   *                          single message; message_template gets
   *                          {{items}} (JSON array) + {{items_text}} (one
   *                          per line) + {{count}}. Lets the LLM analyze
   *                          + summarize whatever you want.
   */
  mode?: 'per-item' | 'summary';
  /**
   * Where the message lands:
   *   'main' (default for summary) — append into the user's persistent main
   *                                   chat session. Triggers the agent
   *                                   loop in the user's active sidepanel.
   *   'new'  (default for per-item) — fresh temp session each tick (or
   *                                   reused if reuse_session=true).
   */
  target?: 'main' | 'new';
}

export type DispatchParams = PipelineDispatchParams | ChatDispatchParams;

export interface Job {
  id: string;
  name: string;
  enabled: boolean;
  schedule_interval_minutes: number;

  source_connector: string;
  source_tool: string;
  source_input: Record<string, unknown>;

  items_path: string;          // '' = whole result if array
  dedup_field: string;

  dispatch_type: DispatchType;
  dispatch_params: DispatchParams;

  /**
   * Skill names (from forge-skills registry) to make available to the
   * task spawned by this job's dispatch. Forwarded into the task's
   * `--append-system-prompt` so Claude knows which skills to invoke
   * for this run. Missing skills are auto-installed at dispatch time
   * (see lib/jobs/dispatcher.ts).
   */
  skills: string[];

  /**
   * Trigger model:
   *   'period' — fire every `schedule_interval_minutes`
   *   'once'   — fire once at `schedule_at` (ISO), then auto-disable
   *   'cron'   — fire on each match of `schedule_cron` (5-field cron, server local TZ)
   *   'manual' — never auto-fire; only the Fire / Force button (POST /api/jobs/[id]/fire) starts a run
   */
  schedule_kind: 'period' | 'once' | 'cron' | 'manual';
  /** ISO timestamp; only used when schedule_kind === 'once'. */
  schedule_at: string | null;
  /** Cron expression (5 fields); only used when schedule_kind === 'cron'. */
  schedule_cron: string | null;

  /** Per-tick dispatch budget. Default 5, hard ceiling 10 in the
   *  scheduler. Source can return 200 items; only this many spawn
   *  pipelines per tick — the rest stay un-dedup-marked and roll
   *  over to the next tick. Protects disk/RAM from fan-out blow-up. */
  max_per_tick: number;

  /** How this Job paces pipeline dispatch:
   *
   *   'sequential' (default) — at most ONE pipeline from this Job runs
   *                  at a time. Each tick checks whether the previously
   *                  dispatched pipeline has reached a terminal state;
   *                  if not, the entire tick is skipped (item stays
   *                  un-dedup-marked, rolls over to next tick). Avoids
   *                  hammering downstream systems (GitLab rate limits,
   *                  Mantis browser-tab races, resource contention).
   *                  This is the safer default.
   *
   *   'parallel'   — each tick dispatches up to max_per_tick items
   *                  concurrently. Items still hit the global pipeline
   *                  cap, but no per-Job throttle beyond that. Use
   *                  only when downstream is known to tolerate burst. */
  concurrency_mode: 'parallel' | 'sequential';

  /** What to do when an item's pipeline ends in 'failed' state:
   *    'continue' (default) — proceed to next item; each item is
   *                independent so one failure doesn't poison the batch.
   *    'stop'     — halt drain (clears next_run_at). User must Force-
   *                run again to resume. Use when a failure likely means
   *                something systemic broke (auth lost, repo gone). */
  on_failure: 'continue' | 'stop';

  last_run_at: string | null;
  next_run_at: string | null;
  created_at: string;
  updated_at: string;
}

export interface JobRun {
  id: string;
  job_id: string;
  started_at: string;
  finished_at: string | null;
  status: JobRunStatus;
  items_seen: number;
  items_new: number;
  items_dispatched: number;
  error: string | null;
  trigger: JobRunTrigger;
  /** Diagnostic note set by the scheduler — backfill summary, missing-path hint, etc. */
  notes: string | null;
  /** Plain-text execution log for this tick (timestamped lines). */
  log: string | null;
}

export interface JobDispatch {
  id: string;
  job_run_id: string;
  item_key: string;
  item_preview: string | null;
  dispatch_type: DispatchType;
  dispatch_target_id: string | null;
  status: JobDispatchStatus;
  error: string | null;
  created_at: string;
}

export interface CreateJobInput {
  name: string;
  enabled?: boolean;
  schedule_interval_minutes?: number;

  source_connector: string;
  source_tool: string;
  source_input?: Record<string, unknown>;

  items_path?: string;
  dedup_field: string;

  dispatch_type: DispatchType;
  dispatch_params: DispatchParams;

  /** Skill names to forward into the dispatched task. See Job.skills. */
  skills?: string[];

  /** Default 'period'. */
  schedule_kind?: 'period' | 'once' | 'cron' | 'manual';
  /** ISO timestamp, required when schedule_kind === 'once'. */
  schedule_at?: string | null;
  /** Cron expression, required when schedule_kind === 'cron'. */
  schedule_cron?: string | null;

  /** Per-tick dispatch budget (default 5, capped 1-10 in scheduler). */
  max_per_tick?: number;

  /** 'parallel' (default) | 'sequential'. See Job.concurrency_mode. */
  concurrency_mode?: 'parallel' | 'sequential';

  /** 'continue' (default) | 'stop'. See Job.on_failure. */
  on_failure?: 'continue' | 'stop';

  /** Default true: first tick records existing items as seen without dispatching. */
  mark_existing_as_seen?: boolean;
}
