import {
  ContainerGone
} from "./chunk-CTVBTCTF.js";
import {
  getConfig
} from "./chunk-2MAQ6JFL.js";
import {
  ApiError
} from "./chunk-EZYKRG4W.js";

// src/containers/client.ts
function authHeaders(tokenOverride) {
  const token = tokenOverride ?? getConfig().spriteToken;
  if (!token) {
    throw new ApiError(
      500,
      "server_error",
      "SPRITE_TOKEN required \u2014 add to vault or .env"
    );
  }
  return { Authorization: `Bearer ${token}` };
}
function api() {
  return getConfig().spriteApi.replace(/\/+$/, "");
}
function signalFor(timeoutMs) {
  const t = timeoutMs ?? getConfig().spriteTimeoutMs;
  if (!t) return void 0;
  return AbortSignal.timeout(t);
}
async function handleJson(res, op) {
  if (!res.ok) {
    const text = await res.text().catch(() => "");
    throw new ApiError(
      res.status >= 500 ? 502 : 500,
      "server_error",
      `sprites.dev ${op} failed (${res.status}): ${text.slice(0, 500)}`
    );
  }
  return await res.json();
}
async function consumeNdjsonProgress(res, op) {
  if (!res.ok) {
    const text = await res.text().catch(() => "");
    throw new ApiError(
      res.status >= 500 ? 502 : 500,
      "server_error",
      `sprites.dev ${op} failed (${res.status}): ${text.slice(0, 500)}`
    );
  }
  if (!res.body) return;
  const reader = res.body.getReader();
  const decoder = new TextDecoder();
  let buf = "";
  for (; ; ) {
    const { done, value } = await reader.read();
    if (done) break;
    buf += decoder.decode(value, { stream: true });
    let nl = buf.indexOf("\n");
    while (nl !== -1) {
      const line = buf.slice(0, nl).trim();
      buf = buf.slice(nl + 1);
      nl = buf.indexOf("\n");
      if (!line) continue;
      try {
        const evt = JSON.parse(line);
        if (evt.type === "error") {
          throw new ApiError(502, "server_error", `${op}: ${evt.message || evt.error || "error"}`);
        }
      } catch (err) {
        if (err instanceof ApiError) throw err;
      }
    }
  }
}
async function createSprite(input) {
  const res = await fetch(`${api()}/v1/sprites`, {
    method: "POST",
    headers: { ...authHeaders(input.tokenOverride), "content-type": "application/json" },
    body: JSON.stringify({ name: input.name, wait_for_capacity: input.wait_for_capacity, url_settings: input.url_settings }),
    signal: signalFor()
  });
  return handleJson(res, "createSprite");
}
async function getSprite(name, tokenOverride) {
  const res = await fetch(
    `${api()}/v1/sprites/${encodeURIComponent(name)}`,
    { headers: authHeaders(tokenOverride), signal: signalFor() }
  );
  if (res.status === 404) return null;
  return handleJson(res, "getSprite");
}
async function deleteSprite(name, tokenOverride) {
  const res = await fetch(
    `${api()}/v1/sprites/${encodeURIComponent(name)}`,
    { method: "DELETE", headers: authHeaders(tokenOverride), signal: signalFor() }
  );
  if (!res.ok && res.status !== 404) {
    const text = await res.text().catch(() => "");
    throw new ApiError(502, "server_error", `deleteSprite: ${res.status} ${text.slice(0, 200)}`);
  }
}
async function listSprites(opts = {}) {
  const params = new URLSearchParams();
  if (opts.prefix) params.set("prefix", opts.prefix);
  if (opts.max_results != null) params.set("max_results", String(opts.max_results));
  if (opts.continuation_token) params.set("continuation_token", opts.continuation_token);
  const res = await fetch(
    `${api()}/v1/sprites?${params.toString()}`,
    { headers: authHeaders(), signal: signalFor() }
  );
  return handleJson(res, "listSprites");
}
async function createCheckpoint(name, comment) {
  const res = await fetch(
    `${api()}/v1/sprites/${encodeURIComponent(name)}/checkpoint`,
    {
      method: "POST",
      headers: { ...authHeaders(), "content-type": "application/json" },
      body: JSON.stringify({ comment: comment ?? null }),
      signal: signalFor(getConfig().agentTimeoutMs)
    }
  );
  if (!res.ok || !res.body) {
    const text = await res.text().catch(() => "");
    throw new ApiError(502, "server_error", `createCheckpoint: ${res.status} ${text.slice(0, 200)}`);
  }
  const reader = res.body.getReader();
  const decoder = new TextDecoder();
  let buf = "";
  let id = null;
  for (; ; ) {
    const { done, value } = await reader.read();
    if (done) break;
    buf += decoder.decode(value, { stream: true });
    let nl = buf.indexOf("\n");
    while (nl !== -1) {
      const line = buf.slice(0, nl).trim();
      buf = buf.slice(nl + 1);
      nl = buf.indexOf("\n");
      if (!line) continue;
      try {
        const evt = JSON.parse(line);
        if (evt.type === "error") {
          throw new ApiError(502, "server_error", `createCheckpoint: ${evt.data || evt.message || "error"}`);
        }
        if (evt.checkpoint_id || evt.id) {
          id = evt.checkpoint_id || evt.id || null;
        }
        if (!id && evt.type === "info" && evt.data) {
          const idMatch = evt.data.match(/^\s*ID:\s*(.+)$/);
          if (idMatch) id = idMatch[1].trim();
        }
        if (!id && evt.type === "complete" && evt.data) {
          const ckptMatch = evt.data.match(/Checkpoint\s+(\S+)/);
          if (ckptMatch) id = ckptMatch[1];
        }
      } catch (err) {
        if (err instanceof ApiError) throw err;
      }
    }
  }
  if (!id) {
    throw new ApiError(502, "server_error", "createCheckpoint: no checkpoint id in progress stream");
  }
  return { id };
}
async function restoreCheckpoint(name, checkpointId) {
  const res = await fetch(
    `${api()}/v1/sprites/${encodeURIComponent(name)}/checkpoints/${encodeURIComponent(checkpointId)}/restore`,
    {
      method: "POST",
      headers: authHeaders(),
      signal: signalFor(getConfig().agentTimeoutMs)
    }
  );
  await consumeNdjsonProgress(res, "restoreCheckpoint");
}
async function httpExec(name, argv, opts = {}) {
  const params = new URLSearchParams();
  for (const c of argv) params.append("cmd", c);
  if (opts.stdin != null) params.set("stdin", "true");
  const res = await fetch(
    `${api()}/v1/sprites/${encodeURIComponent(name)}/exec?${params.toString()}`,
    {
      method: "POST",
      headers: authHeaders(opts.tokenOverride),
      body: opts.stdin ?? void 0,
      signal: signalFor(opts.timeoutMs ?? getConfig().agentTimeoutMs)
    }
  );
  if (!res.ok) {
    const text2 = await res.text().catch(() => "");
    if (res.status === 404) {
      throw new ContainerGone(name, `httpExec: 404 ${text2.slice(0, 300)}`);
    }
    throw new ApiError(502, "server_error", `httpExec: ${res.status} ${text2.slice(0, 300)}`);
  }
  const ct = res.headers.get("content-type") || "";
  if (ct.includes("application/json")) {
    return await res.json();
  }
  const text = await res.text();
  return { stdout: text, stderr: "", exit_code: 0 };
}
async function killExecSession(spriteName, execSessionId) {
  const res = await fetch(
    `${api()}/v1/sprites/${encodeURIComponent(spriteName)}/exec/${encodeURIComponent(execSessionId)}/kill`,
    { method: "POST", headers: authHeaders(), signal: signalFor() }
  );
  if (!res.ok && res.status !== 404) {
    const text = await res.text().catch(() => "");
    throw new ApiError(502, "server_error", `killExecSession: ${res.status} ${text.slice(0, 200)}`);
  }
  await consumeNdjsonProgress(res, "killExecSession").catch(() => {
  });
}

export {
  createSprite,
  getSprite,
  deleteSprite,
  listSprites,
  createCheckpoint,
  restoreCheckpoint,
  httpExec,
  killExecSession
};
