/**
 * Typed client for the @void-nav/api orchestrator backend.
 *
 * The orchestrator turns the implementation board into an agentic system: each
 * run hands a kanban card to a `pi` subprocess (running inside an isolated git
 * worktree) and streams its progress back to the UI. The agent drives the board
 * and the documentation through the server's internal endpoints, so the board
 * stays fully interactive and the docs remain the central store of truth.
 *
 * Keep these types in sync with `apps/api/src/types.ts`.
 */

export type RunStatus = 'queued' | 'running' | 'completed' | 'failed' | 'stopped';

export interface AgentRun {
  id: string;
  cardId: string;
  status: RunStatus;
  useWorktree: boolean;
  branch: string | null;
  worktreePath: string | null;
  prompt: string;
  summary: string | null;
  commitSha: string | null;
  error: string | null;
  createdAt: string;
  startedAt: string | null;
  finishedAt: string | null;
}

export type RunEventType =
  | 'status'
  | 'text'
  | 'tool_start'
  | 'tool_end'
  | 'log'
  | 'done'
  | 'error';

export interface RunEvent {
  seq?: number;
  type: RunEventType;
  data: Record<string, unknown>;
  createdAt?: string;
}

const BASE = '/api/orchestrator';

class ApiError extends Error {
  constructor(message: string, readonly status: number) {
    super(message);
    this.name = 'ApiError';
  }
}

async function req<T>(path: string, init?: RequestInit): Promise<T> {
  const res = await fetch(`${BASE}${path}`, {
    ...init,
    headers: { 'Content-Type': 'application/json', ...(init?.headers ?? {}) },
  });
  if (!res.ok) {
    let detail = `${res.status} ${res.statusText}`;
    try {
      const body = await res.json();
      if (body?.error) detail = body.error;
    } catch {
      /* keep status text */
    }
    throw new ApiError(detail, res.status);
  }
  if (res.status === 204) return undefined as T;
  return (await res.json()) as T;
}

export interface StartRunInput {
  cardId: string;
  prompt?: string;
  useWorktree?: boolean;
  cleanupOnFinish?: boolean;
}

export const orchestratorApi = {
  listRuns: (cardId?: string) =>
    req<{ runs: AgentRun[] }>(`/runs${cardId ? `?cardId=${encodeURIComponent(cardId)}` : ''}`),

  getRun: (id: string, since = 0) =>
    req<{ run: AgentRun; events: RunEvent[] }>(`/runs/${id}?since=${since}`),

  startRun: (input: StartRunInput) =>
    req<{ run: AgentRun }>(`/runs`, { method: 'POST', body: JSON.stringify(input) }),

  messageRun: (id: string, text: string, mode: 'steer' | 'followUp') =>
    req<{ ok: boolean }>(`/runs/${id}/message`, {
      method: 'POST',
      body: JSON.stringify({ text, mode }),
    }),

  stopRun: (id: string) =>
    req<{ ok: boolean }>(`/runs/${id}/stop`, { method: 'POST' }),

  deleteRun: (id: string) => req<void>(`/runs/${id}`, { method: 'DELETE' }),
};
