import {
  runTurn
} from "./chunk-CRCDYAJC.js";
import {
  enqueueTurn
} from "./chunk-ULPKWCA5.js";
import {
  installOtlpExporter
} from "./chunk-PZG4I7YQ.js";
import {
  redactAppendInput
} from "./chunk-D4IST25M.js";
import {
  createApiKey,
  listApiKeys
} from "./chunk-UW7HVMAP.js";
import {
  initSentry
} from "./chunk-3MQ2FWXS.js";
import {
  installShutdownHandlers
} from "./chunk-UQDDNCZI.js";
import {
  runSweep
} from "./chunk-RYU7LXJY.js";
import {
  fillWarmPools,
  reconcileDockerOrphanSandboxes,
  reconcileOrphanSandboxes
} from "./chunk-T3WHRKGD.js";
import {
  appendEvent,
  installPayloadRedactor
} from "./chunk-4NRHKTBW.js";
import {
  setSessionSandbox
} from "./chunk-SEGKFEG5.js";
import {
  readEnvValue,
  upsertEnvLine
} from "./chunk-YPXI7Q2M.js";
import {
  getLastUnprocessedUserMessage
} from "./chunk-LH3NEWMT.js";
import {
  getBySession,
  register
} from "./chunk-EFOIR7R3.js";
import {
  tryResolveProvider
} from "./chunk-ZKZME3DE.js";
import {
  getEnvironment
} from "./chunk-GTYDSYTR.js";
import {
  getConfig
} from "./chunk-2MAQ6JFL.js";
import {
  init_clock,
  nowMs
} from "./chunk-HFDLUBWN.js";
import {
  getDb,
  init_client
} from "./chunk-TLEKKGH7.js";
import {
  getRuntime
} from "./chunk-UYTSKFGK.js";

// src/init.ts
import path from "path";
init_client();
init_clock();
var g = globalThis;
async function ensureInitialized() {
  if (g.__caInitPromise) return g.__caInitPromise;
  g.__caInitPromise = doInit();
  return g.__caInitPromise;
}
async function doInit() {
  initSentry();
  getDb();
  const { seedDefaultTenant } = await import("./db/tenants.js");
  seedDefaultTenant();
  try {
    const { findDuplicateKeys } = await import("./util/env.js");
    const envPath = path.resolve(process.cwd(), ".env");
    const dupes = findDuplicateKeys(envPath);
    if (dupes.length > 0) {
      console.warn(
        `[init] WARNING: duplicate keys in .env: ${dupes.join(", ")}. dotenv uses the LAST value \u2014 earlier entries are silently ignored. Remove duplicates to prevent confusion.`
      );
    }
  } catch {
  }
  seedDefaultApiKey();
  installPayloadRedactor(redactAppendInput);
  installOtlpExporter();
  const { installFileSyncHook } = await import("./sync/file-sync.js");
  installFileSyncHook();
  const { validateLicense } = await import("./license/index.js");
  validateLicense();
  await validateRateLimitBackend();
  installShutdownHandlers();
  try {
    await recoverStaleSessions();
  } catch (err) {
    console.error("[init] stale session recovery failed:", err);
  }
  try {
    await rebuildContainerPool();
  } catch (err) {
    console.error("[init] container pool rebuild failed:", err);
  }
  const cfg = getConfig();
  if (cfg.spriteToken) {
    reconcileOrphanSandboxes().then((r) => {
      if (r.deleted > 0) {
        console.log(`[init] reconciled ${r.deleted} orphan sandboxes, kept ${r.kept}`);
      }
    }).catch((err) => {
      console.warn("[init] orphan reconcile (sandboxes) failed:", err);
    });
  }
  reconcileDockerOrphanSandboxes().then((r) => {
    if (r.deleted > 0) {
      console.log(`[init] reconciled ${r.deleted} orphan docker containers, kept ${r.kept}`);
    }
  }).catch((err) => {
    console.warn("[init] orphan reconcile (docker) failed:", err);
  });
  import("./lib/model-registry.js").then((m) => m.getModels().catch(() => {
  }));
  fillWarmPools().catch((err) => {
    console.warn("[init] warm pool fill failed:", err);
  });
  if (!g.__caSweeperHandle) {
    const intervalMs = getConfig().sweeperIntervalMs;
    g.__caSweeperHandle = setInterval(() => {
      void runSweep();
    }, intervalMs);
  }
}
function seedDefaultApiKey() {
  try {
    const keys = listApiKeys();
    if (keys.length > 0) return;
    const envPath = path.resolve(process.cwd(), ".env");
    let seedKey = process.env.SEED_API_KEY;
    if (!seedKey) {
      seedKey = readEnvValue(envPath, "SEED_API_KEY");
      if (seedKey) process.env.SEED_API_KEY = seedKey;
    }
    if (seedKey) {
      const { id: id2 } = createApiKey({ name: "default", permissions: ["*"], rawKey: seedKey });
      console.log(`[init] created API key from SEED_API_KEY (id: ${id2})`);
      return;
    }
    const { key, id } = createApiKey({ name: "default", permissions: ["*"] });
    upsertEnvLine(envPath, "SEED_API_KEY", key);
    console.log(`[init] created default API key and wrote to ${envPath}`);
    process.env.SEED_API_KEY = key;
    console.log(`  id:  ${id}`);
    console.log(`  key: (written to .env \u2014 see SEED_API_KEY)`);
  } catch (err) {
    console.error("[init] failed to seed default API key:", err);
  }
}
async function validateRateLimitBackend() {
  const { validateBackend } = await import("./auth/rate_limit.js");
  await validateBackend();
}
async function recoverStaleSessions() {
  const db = getDb();
  const rows = db.prepare(
    `SELECT * FROM sessions WHERE status = 'running' AND archived_at IS NULL`
  ).all();
  if (rows.length === 0) return;
  console.log(`[init] recovering ${rows.length} stale running session(s)`);
  const rt = getRuntime();
  for (const row of rows) {
    try {
      const lastMsg = getLastUnprocessedUserMessage(row.id);
      if (lastMsg) {
        if (row.sandbox_name) {
          const envObj = getEnvironment(row.environment_id);
          const provider = await tryResolveProvider({ envConfigProvider: envObj?.config?.provider });
          if (!provider) continue;
          try {
            const containers = await provider.list({ prefix: row.sandbox_name });
            const alive = containers.some((c) => c.name === row.sandbox_name);
            if (!alive) {
              console.warn(`[init] sandbox ${row.sandbox_name} for session ${row.id} no longer exists, clearing`);
              setSessionSandbox(row.id, null);
            } else {
              register({
                sandboxName: row.sandbox_name,
                envId: row.environment_id,
                sessionId: row.id,
                createdAt: nowMs()
              });
            }
          } catch (err) {
            console.warn(`[init] container health check failed for ${row.sandbox_name}, clearing:`, err);
            setSessionSandbox(row.id, null);
          }
        }
        appendEvent(row.id, {
          type: "session.status_rescheduled",
          payload: {},
          origin: "server",
          processedAt: nowMs()
        });
        db.prepare(
          `UPDATE sessions SET status = 'idle', stop_reason = 'rescheduled', updated_at = ? WHERE id = ?`
        ).run(nowMs(), row.id);
        rt.inFlightRuns.delete(row.id);
        const payload = JSON.parse(lastMsg.payload_json);
        const text = (payload.content ?? []).filter((b) => b.type === "text" && b.text).map((b) => b.text).join("");
        void enqueueTurn(
          row.environment_id,
          () => runTurn(row.id, [{ kind: "text", eventId: lastMsg.id, text }])
        ).catch((err) => {
          console.error(`[init] reschedule turn failed for ${row.id}:`, err);
        });
        console.log(`[init] rescheduled session ${row.id}`);
        continue;
      }
    } catch (err) {
      console.warn(`[init] reschedule attempt failed for ${row.id}, falling back to error:`, err);
    }
    try {
      appendEvent(row.id, {
        type: "session.error",
        payload: {
          error: {
            type: "server_restart",
            message: "server restarted while turn was running"
          }
        },
        origin: "server",
        processedAt: nowMs()
      });
      appendEvent(row.id, {
        type: "session.status_idle",
        payload: { stop_reason: { type: "error" } },
        origin: "server",
        processedAt: nowMs()
      });
      db.prepare(
        `UPDATE sessions SET status = 'idle', stop_reason = 'error', updated_at = ? WHERE id = ?`
      ).run(nowMs(), row.id);
      rt.inFlightRuns.delete(row.id);
    } catch (err) {
      console.error(`[init] failed to recover session ${row.id}:`, err);
    }
  }
}
async function rebuildContainerPool() {
  const db = getDb();
  const rows = db.prepare(
    `SELECT * FROM sessions WHERE sandbox_name IS NOT NULL AND status != 'running' AND archived_at IS NULL`
  ).all();
  if (rows.length === 0) return;
  let recovered = 0;
  let cleared = 0;
  for (const row of rows) {
    if (getBySession(row.id)) continue;
    try {
      const envObj = getEnvironment(row.environment_id);
      const provider = await tryResolveProvider({ envConfigProvider: envObj?.config?.provider });
      if (!provider) continue;
      const containers = await provider.list({ prefix: row.sandbox_name });
      const alive = containers.some((c) => c.name === row.sandbox_name);
      if (alive) {
        register({
          sandboxName: row.sandbox_name,
          envId: row.environment_id,
          sessionId: row.id,
          createdAt: nowMs()
        });
        recovered++;
      } else {
        setSessionSandbox(row.id, null);
        cleared++;
      }
    } catch {
      setSessionSandbox(row.id, null);
      cleared++;
    }
  }
  if (recovered > 0 || cleared > 0) {
    console.log(`[init] pool rebuild: ${recovered} containers recovered, ${cleared} stale references cleared`);
  }
}

export {
  ensureInitialized,
  recoverStaleSessions
};
