/**
 * Minimal gateway REST API client for the TUI and CLI commands.
 */

export interface ModelInfo {
  id: string;
  provider: string;
  name: string;
}

export interface ProviderInfo {
  id: string;
  name: string;
  available: boolean;
  models: ModelInfo[];
}

export interface SessionInfo {
  id: string;
  provider: string;
  modelId: string;
  createdAt: string;
  promptCount: number;
  channels: string[];
  durationMs: number;
}

export interface SessionListResult {
  sessions: SessionInfo[];
  total: number;
}

// ── Agent Config types ──

export interface AgentConfigInfo {
  id: string;
  name: string;
  provider: string;
  model: string;
  status: string;
  projectId: string | null;
  ownerId: string | null;
  systemPrompt: string | null;
  allowedTools: string[] | null;
  skills: string[] | null;
  isSystem: boolean;
  config: Record<string, unknown> | null;
  createdAt: string;
  updatedAt: string;
}

// ── Project types ──

export interface ProjectInfo {
  id: string;
  name: string;
  description: string | null;
  status: string;
  ownerId: string | null;
  createdAt: string;
  updatedAt: string;
}

// ── Mission types ──

export interface MissionInfo {
  id: string;
  name: string;
  description: string | null;
  status: string;
  projectId: string | null;
  userId: string | null;
  phase: string | null;
  milestones: Record<string, unknown>[] | null;
  config: Record<string, unknown> | null;
  createdAt: string;
  updatedAt: string;
}

// ── Mission Task types ──

export interface MissionTaskInfo {
  id: string;
  missionId: string;
  taskId: string | null;
  userId: string;
  status: string;
  description: string | null;
  notes: string | null;
  pr: string | null;
  createdAt: string;
  updatedAt: string;
}

// ── Helpers ──

function headers(sessionCookie: string, gatewayUrl: string) {
  return { Cookie: sessionCookie, Origin: gatewayUrl };
}

function jsonHeaders(sessionCookie: string, gatewayUrl: string) {
  return { ...headers(sessionCookie, gatewayUrl), 'Content-Type': 'application/json' };
}

async function handleResponse<T>(res: Response, errorPrefix: string): Promise<T> {
  if (!res.ok) {
    const body = await res.text().catch(() => '');
    throw new Error(`${errorPrefix} (${res.status}): ${body}`);
  }
  return (await res.json()) as T;
}

// ── Conversation types ──

export interface ConversationInfo {
  id: string;
  title: string | null;
  archived: boolean;
  createdAt: string;
  updatedAt: string;
}

// ── Conversation endpoints ──

export async function createConversation(
  gatewayUrl: string,
  sessionCookie: string,
  data: { title?: string; projectId?: string } = {},
): Promise<ConversationInfo> {
  const res = await fetch(`${gatewayUrl}/api/conversations`, {
    method: 'POST',
    headers: jsonHeaders(sessionCookie, gatewayUrl),
    body: JSON.stringify(data),
  });
  return handleResponse<ConversationInfo>(res, 'Failed to create conversation');
}

// ── Provider / Model endpoints ──

export async function fetchAvailableModels(
  gatewayUrl: string,
  sessionCookie?: string,
): Promise<ModelInfo[]> {
  try {
    const res = await fetch(`${gatewayUrl}/api/providers/models`, {
      headers: {
        ...(sessionCookie ? { Cookie: sessionCookie } : {}),
        Origin: gatewayUrl,
      },
    });
    if (!res.ok) return [];
    const data = (await res.json()) as ModelInfo[];
    return Array.isArray(data) ? data : [];
  } catch {
    return [];
  }
}

export async function fetchProviders(
  gatewayUrl: string,
  sessionCookie?: string,
): Promise<ProviderInfo[]> {
  try {
    const res = await fetch(`${gatewayUrl}/api/providers`, {
      headers: {
        ...(sessionCookie ? { Cookie: sessionCookie } : {}),
        Origin: gatewayUrl,
      },
    });
    if (!res.ok) return [];
    const data = (await res.json()) as ProviderInfo[];
    return Array.isArray(data) ? data : [];
  } catch {
    return [];
  }
}

// ── Session endpoints ──

export async function fetchSessions(
  gatewayUrl: string,
  sessionCookie: string,
): Promise<SessionListResult> {
  const res = await fetch(`${gatewayUrl}/api/sessions`, {
    headers: headers(sessionCookie, gatewayUrl),
  });
  return handleResponse<SessionListResult>(res, 'Failed to list sessions');
}

export async function deleteSession(
  gatewayUrl: string,
  sessionCookie: string,
  sessionId: string,
): Promise<void> {
  const res = await fetch(`${gatewayUrl}/api/sessions/${encodeURIComponent(sessionId)}`, {
    method: 'DELETE',
    headers: headers(sessionCookie, gatewayUrl),
  });
  if (!res.ok && res.status !== 204) {
    const body = await res.text().catch(() => '');
    throw new Error(`Failed to destroy session (${res.status}): ${body}`);
  }
}

// ── Agent Config endpoints ──

export async function fetchAgentConfigs(
  gatewayUrl: string,
  sessionCookie: string,
): Promise<AgentConfigInfo[]> {
  const res = await fetch(`${gatewayUrl}/api/agents`, {
    headers: headers(sessionCookie, gatewayUrl),
  });
  return handleResponse<AgentConfigInfo[]>(res, 'Failed to list agents');
}

export async function fetchAgentConfig(
  gatewayUrl: string,
  sessionCookie: string,
  id: string,
): Promise<AgentConfigInfo> {
  const res = await fetch(`${gatewayUrl}/api/agents/${encodeURIComponent(id)}`, {
    headers: headers(sessionCookie, gatewayUrl),
  });
  return handleResponse<AgentConfigInfo>(res, 'Failed to get agent');
}

export async function createAgentConfig(
  gatewayUrl: string,
  sessionCookie: string,
  data: {
    name: string;
    provider: string;
    model: string;
    projectId?: string;
    systemPrompt?: string;
    allowedTools?: string[];
    skills?: string[];
    config?: Record<string, unknown>;
  },
): Promise<AgentConfigInfo> {
  const res = await fetch(`${gatewayUrl}/api/agents`, {
    method: 'POST',
    headers: jsonHeaders(sessionCookie, gatewayUrl),
    body: JSON.stringify(data),
  });
  return handleResponse<AgentConfigInfo>(res, 'Failed to create agent');
}

export async function updateAgentConfig(
  gatewayUrl: string,
  sessionCookie: string,
  id: string,
  data: Record<string, unknown>,
): Promise<AgentConfigInfo> {
  const res = await fetch(`${gatewayUrl}/api/agents/${encodeURIComponent(id)}`, {
    method: 'PATCH',
    headers: jsonHeaders(sessionCookie, gatewayUrl),
    body: JSON.stringify(data),
  });
  return handleResponse<AgentConfigInfo>(res, 'Failed to update agent');
}

export async function deleteAgentConfig(
  gatewayUrl: string,
  sessionCookie: string,
  id: string,
): Promise<void> {
  const res = await fetch(`${gatewayUrl}/api/agents/${encodeURIComponent(id)}`, {
    method: 'DELETE',
    headers: headers(sessionCookie, gatewayUrl),
  });
  if (!res.ok && res.status !== 204) {
    const body = await res.text().catch(() => '');
    throw new Error(`Failed to delete agent (${res.status}): ${body}`);
  }
}

// ── Project endpoints ──

export async function fetchProjects(
  gatewayUrl: string,
  sessionCookie: string,
): Promise<ProjectInfo[]> {
  const res = await fetch(`${gatewayUrl}/api/projects`, {
    headers: headers(sessionCookie, gatewayUrl),
  });
  return handleResponse<ProjectInfo[]>(res, 'Failed to list projects');
}

// ── Mission endpoints ──

export async function fetchMissions(
  gatewayUrl: string,
  sessionCookie: string,
): Promise<MissionInfo[]> {
  const res = await fetch(`${gatewayUrl}/api/missions`, {
    headers: headers(sessionCookie, gatewayUrl),
  });
  return handleResponse<MissionInfo[]>(res, 'Failed to list missions');
}

export async function fetchMission(
  gatewayUrl: string,
  sessionCookie: string,
  id: string,
): Promise<MissionInfo> {
  const res = await fetch(`${gatewayUrl}/api/missions/${encodeURIComponent(id)}`, {
    headers: headers(sessionCookie, gatewayUrl),
  });
  return handleResponse<MissionInfo>(res, 'Failed to get mission');
}

export async function createMission(
  gatewayUrl: string,
  sessionCookie: string,
  data: {
    name: string;
    description?: string;
    projectId?: string;
    status?: string;
    phase?: string;
    milestones?: Record<string, unknown>[];
    config?: Record<string, unknown>;
  },
): Promise<MissionInfo> {
  const res = await fetch(`${gatewayUrl}/api/missions`, {
    method: 'POST',
    headers: jsonHeaders(sessionCookie, gatewayUrl),
    body: JSON.stringify(data),
  });
  return handleResponse<MissionInfo>(res, 'Failed to create mission');
}

export async function updateMission(
  gatewayUrl: string,
  sessionCookie: string,
  id: string,
  data: Record<string, unknown>,
): Promise<MissionInfo> {
  const res = await fetch(`${gatewayUrl}/api/missions/${encodeURIComponent(id)}`, {
    method: 'PATCH',
    headers: jsonHeaders(sessionCookie, gatewayUrl),
    body: JSON.stringify(data),
  });
  return handleResponse<MissionInfo>(res, 'Failed to update mission');
}

export async function deleteMission(
  gatewayUrl: string,
  sessionCookie: string,
  id: string,
): Promise<void> {
  const res = await fetch(`${gatewayUrl}/api/missions/${encodeURIComponent(id)}`, {
    method: 'DELETE',
    headers: headers(sessionCookie, gatewayUrl),
  });
  if (!res.ok && res.status !== 204) {
    const body = await res.text().catch(() => '');
    throw new Error(`Failed to delete mission (${res.status}): ${body}`);
  }
}

// ── Authenticated interaction runtime endpoints ──

export interface InteractionRequest {
  agentName: string;
  correlationId?: string;
}

function interactionHeaders(sessionCookie: string, gatewayUrl: string, correlationId?: string) {
  return {
    ...jsonHeaders(sessionCookie, gatewayUrl),
    ...(correlationId ? { 'X-Correlation-Id': correlationId } : {}),
  };
}

function interactionPath(agentName: string, suffix: string): string {
  return `/api/interaction/${encodeURIComponent(agentName)}${suffix}`;
}

export async function fetchInteractionSessions(
  gatewayUrl: string,
  sessionCookie: string,
  request: InteractionRequest & { providerId: string },
): Promise<unknown[]> {
  const params = new URLSearchParams({ provider: request.providerId });
  const res = await fetch(
    `${gatewayUrl}${interactionPath(request.agentName, `/sessions?${params}`)}`,
    {
      headers: interactionHeaders(sessionCookie, gatewayUrl, request.correlationId),
    },
  );
  return handleResponse<unknown[]>(res, 'Failed to list interaction sessions');
}

export async function fetchInteractionTree(
  gatewayUrl: string,
  sessionCookie: string,
  request: InteractionRequest & { providerId: string },
): Promise<unknown[]> {
  const params = new URLSearchParams({ provider: request.providerId });
  const res = await fetch(`${gatewayUrl}${interactionPath(request.agentName, `/tree?${params}`)}`, {
    headers: interactionHeaders(sessionCookie, gatewayUrl, request.correlationId),
  });
  return handleResponse<unknown[]>(res, 'Failed to get interaction session tree');
}

export async function enrollInteractionSession(
  gatewayUrl: string,
  sessionCookie: string,
  request: InteractionRequest & {
    sessionId: string;
    providerId: string;
    runtimeSessionId: string;
  },
): Promise<{ status: string; sessionId: string }> {
  const res = await fetch(
    `${gatewayUrl}${interactionPath(request.agentName, `/sessions/${encodeURIComponent(request.sessionId)}/enroll`)}`,
    {
      method: 'POST',
      headers: interactionHeaders(sessionCookie, gatewayUrl, request.correlationId),
      body: JSON.stringify({
        providerId: request.providerId,
        runtimeSessionId: request.runtimeSessionId,
      }),
    },
  );
  return handleResponse<{ status: string; sessionId: string }>(
    res,
    'Failed to enroll interaction session',
  );
}

export async function attachInteractionSession(
  gatewayUrl: string,
  sessionCookie: string,
  request: InteractionRequest & { sessionId: string; mode?: 'read' | 'control' },
): Promise<unknown> {
  const res = await fetch(
    `${gatewayUrl}${interactionPath(request.agentName, `/sessions/${encodeURIComponent(request.sessionId)}/attach`)}`,
    {
      method: 'POST',
      headers: interactionHeaders(sessionCookie, gatewayUrl, request.correlationId),
      body: JSON.stringify({ mode: request.mode ?? 'read' }),
    },
  );
  return handleResponse<unknown>(res, 'Failed to attach interaction session');
}

export async function* streamInteractionSession(
  gatewayUrl: string,
  sessionCookie: string,
  request: InteractionRequest & { sessionId: string; cursor?: string },
): AsyncIterable<unknown> {
  const params = request.cursor?.trim()
    ? `?${new URLSearchParams({ cursor: request.cursor.trim() }).toString()}`
    : '';
  const res = await fetch(
    `${gatewayUrl}${interactionPath(request.agentName, `/sessions/${encodeURIComponent(request.sessionId)}/stream${params}`)}`,
    { headers: interactionHeaders(sessionCookie, gatewayUrl, request.correlationId) },
  );
  if (!res.ok || !res.body) {
    const body = await res.text().catch(() => '');
    throw new Error(`Failed to stream interaction session (${res.status}): ${body}`);
  }

  const reader = res.body.getReader();
  const decoder = new TextDecoder();
  let pending = '';
  try {
    for (;;) {
      const chunk = await reader.read();
      if (chunk.done) break;
      pending += decoder.decode(chunk.value, { stream: true });
      for (;;) {
        const separator = pending.indexOf('\n\n');
        if (separator < 0) break;
        const frame = pending.slice(0, separator);
        pending = pending.slice(separator + 2);
        const data = frame
          .split('\n')
          .find((line: string): boolean => line.startsWith('data:'))
          ?.slice('data:'.length)
          .trim();
        if (data) yield JSON.parse(data) as unknown;
      }
    }
  } finally {
    reader.releaseLock();
  }
}

export async function sendInteractionMessage(
  gatewayUrl: string,
  sessionCookie: string,
  request: InteractionRequest & { sessionId: string; content: string; idempotencyKey: string },
): Promise<{ status: string; sessionId: string }> {
  const res = await fetch(
    `${gatewayUrl}${interactionPath(request.agentName, `/sessions/${encodeURIComponent(request.sessionId)}/send`)}`,
    {
      method: 'POST',
      headers: interactionHeaders(sessionCookie, gatewayUrl, request.correlationId),
      body: JSON.stringify({ content: request.content, idempotencyKey: request.idempotencyKey }),
    },
  );
  return handleResponse<{ status: string; sessionId: string }>(
    res,
    'Failed to send interaction message',
  );
}

export async function stopInteractionSession(
  gatewayUrl: string,
  sessionCookie: string,
  request: InteractionRequest & { sessionId: string; approvalRef: string },
): Promise<{ status: string; sessionId: string }> {
  const res = await fetch(
    `${gatewayUrl}${interactionPath(request.agentName, `/sessions/${encodeURIComponent(request.sessionId)}/stop`)}`,
    {
      method: 'POST',
      headers: interactionHeaders(sessionCookie, gatewayUrl, request.correlationId),
      body: JSON.stringify({ approvalRef: request.approvalRef }),
    },
  );
  return handleResponse<{ status: string; sessionId: string }>(
    res,
    'Failed to stop interaction session',
  );
}

export async function recoverInteractionSession(
  gatewayUrl: string,
  sessionCookie: string,
  request: InteractionRequest & { sessionId: string },
): Promise<{ status: string; sessionId: string }> {
  const res = await fetch(
    `${gatewayUrl}${interactionPath(request.agentName, `/sessions/${encodeURIComponent(request.sessionId)}/recover`)}`,
    {
      method: 'POST',
      headers: interactionHeaders(sessionCookie, gatewayUrl, request.correlationId),
    },
  );
  return handleResponse<{ status: string; sessionId: string }>(
    res,
    'Failed to recover interaction session',
  );
}

export async function fetchInteractionStatus(
  gatewayUrl: string,
  sessionCookie: string,
): Promise<unknown> {
  const res = await fetch(`${gatewayUrl}/api/providers/status`, {
    headers: headers(sessionCookie, gatewayUrl),
  });
  return handleResponse<unknown>(res, 'Failed to get effective interaction policy');
}

export async function fetchInteractionHealth(gatewayUrl: string): Promise<unknown> {
  const res = await fetch(`${gatewayUrl}/health/ready`);
  return handleResponse<unknown>(res, 'Failed to get interaction readiness');
}

// ── Conversation Message types ──

export interface ConversationMessage {
  id: string;
  role: 'user' | 'assistant' | 'system' | 'tool';
  content: string;
  createdAt: string;
}

// ── Conversation Message endpoints ──

export async function fetchConversationMessages(
  gatewayUrl: string,
  sessionCookie: string,
  conversationId: string,
): Promise<ConversationMessage[]> {
  const res = await fetch(
    `${gatewayUrl}/api/conversations/${encodeURIComponent(conversationId)}/messages`,
    {
      headers: headers(sessionCookie, gatewayUrl),
    },
  );
  return handleResponse<ConversationMessage[]>(res, 'Failed to fetch conversation messages');
}

// ── Mission Task endpoints ──

export async function fetchMissionTasks(
  gatewayUrl: string,
  sessionCookie: string,
  missionId: string,
): Promise<MissionTaskInfo[]> {
  const res = await fetch(`${gatewayUrl}/api/missions/${encodeURIComponent(missionId)}/tasks`, {
    headers: headers(sessionCookie, gatewayUrl),
  });
  return handleResponse<MissionTaskInfo[]>(res, 'Failed to list mission tasks');
}

export async function createMissionTask(
  gatewayUrl: string,
  sessionCookie: string,
  missionId: string,
  data: {
    description?: string;
    status?: string;
    notes?: string;
    pr?: string;
    taskId?: string;
  },
): Promise<MissionTaskInfo> {
  const res = await fetch(`${gatewayUrl}/api/missions/${encodeURIComponent(missionId)}/tasks`, {
    method: 'POST',
    headers: jsonHeaders(sessionCookie, gatewayUrl),
    body: JSON.stringify(data),
  });
  return handleResponse<MissionTaskInfo>(res, 'Failed to create mission task');
}

export async function updateMissionTask(
  gatewayUrl: string,
  sessionCookie: string,
  missionId: string,
  taskId: string,
  data: Record<string, unknown>,
): Promise<MissionTaskInfo> {
  const res = await fetch(
    `${gatewayUrl}/api/missions/${encodeURIComponent(missionId)}/tasks/${encodeURIComponent(taskId)}`,
    {
      method: 'PATCH',
      headers: jsonHeaders(sessionCookie, gatewayUrl),
      body: JSON.stringify(data),
    },
  );
  return handleResponse<MissionTaskInfo>(res, 'Failed to update mission task');
}
