import {
  init_ids,
  newId
} from "./chunk-F4WUVOLE.js";
import {
  init_clock,
  nowMs,
  toIso
} from "./chunk-HFDLUBWN.js";
import {
  getDrizzle,
  init_drizzle
} from "./chunk-RII4M3J2.js";
import {
  schema_exports
} from "./chunk-DDJOPRB7.js";

// src/db/work.ts
init_drizzle();
init_ids();
init_clock();
import { eq, and, sql } from "drizzle-orm";
var LEASE_TTL_MS = 6e4;
function hydrate(row) {
  const metadata = row.metadata_json ? JSON.parse(row.metadata_json) : {};
  return {
    type: "work",
    id: row.id,
    environment_id: row.environment_id,
    state: row.state,
    data: { type: "session", id: row.session_id },
    metadata,
    worker_id: row.worker_id ?? null,
    created_at: toIso(row.created_at),
    acknowledged_at: row.acknowledged_at ? toIso(row.acknowledged_at) : null,
    started_at: row.started_at ? toIso(row.started_at) : null,
    latest_heartbeat_at: row.latest_heartbeat_at ? toIso(row.latest_heartbeat_at) : null,
    stop_requested_at: row.stop_requested_at ? toIso(row.stop_requested_at) : null,
    stopped_at: row.stopped_at ? toIso(row.stopped_at) : null
  };
}
function createWorkItem(envId, sessionId, opts) {
  const db = getDrizzle();
  const id = newId("work");
  const now = nowMs();
  db.insert(schema_exports.workItems).values({
    id,
    environment_id: envId,
    session_id: sessionId,
    state: "queued",
    inputs_json: opts?.inputsJson ?? null,
    metadata_json: "{}",
    tenant_id: opts?.tenantId ?? null,
    created_at: now
  }).run();
  return getWorkItem(id);
}
function getWorkItem(id) {
  const db = getDrizzle();
  const row = db.select().from(schema_exports.workItems).where(eq(schema_exports.workItems.id, id)).get();
  return row ? hydrate(row) : void 0;
}
function getWorkItemInputs(id) {
  const db = getDrizzle();
  const row = db.select({ inputs_json: schema_exports.workItems.inputs_json }).from(schema_exports.workItems).where(eq(schema_exports.workItems.id, id)).get();
  return row?.inputs_json ?? null;
}
function listWorkItems(envId, opts) {
  const db = getDrizzle();
  const limit = Math.min(Math.max(opts?.limit ?? 20, 1), 100);
  const conditions = [
    eq(schema_exports.workItems.environment_id, envId)
  ];
  if (opts?.state) {
    conditions.push(eq(schema_exports.workItems.state, opts.state));
  }
  if (opts?.cursor) {
    conditions.push(sql`${schema_exports.workItems.created_at} < (SELECT created_at FROM work_items WHERE id = ${opts.cursor})`);
  }
  const rows = db.select().from(schema_exports.workItems).where(and(...conditions)).orderBy(sql`${schema_exports.workItems.created_at} DESC`).limit(limit).all();
  return rows.map(hydrate);
}
function pollWorkItem(envId, workerId) {
  const db = getDrizzle();
  const now = nowMs();
  db.run(
    sql`UPDATE work_items
        SET state = 'queued', worker_id = NULL, lease_expires_at = NULL
        WHERE state IN ('pending', 'active')
          AND lease_expires_at IS NOT NULL
          AND lease_expires_at < ${now}`
  );
  const leaseExpires = now + LEASE_TTL_MS;
  const wid = workerId ?? null;
  const result = db.run(
    sql`UPDATE work_items
        SET state = 'pending',
            worker_id = ${wid},
            lease_expires_at = ${leaseExpires}
        WHERE id = (
          SELECT id FROM work_items
          WHERE environment_id = ${envId} AND state = 'queued'
          ORDER BY created_at ASC LIMIT 1
        )
        AND state = 'queued'`
  );
  if (result.changes === 0) return null;
  const rows = db.all(
    sql`SELECT * FROM work_items
        WHERE environment_id = ${envId}
          AND state = 'pending'
          AND lease_expires_at = ${leaseExpires}
          AND worker_id IS ${wid}
        ORDER BY created_at ASC LIMIT 1`
  );
  if (rows.length === 0) return null;
  return hydrate(rows[0]);
}
function ackWorkItem(id, workerId) {
  const db = getDrizzle();
  const now = nowMs();
  const leaseExpires = now + LEASE_TTL_MS;
  const conditions = [eq(schema_exports.workItems.id, id), eq(schema_exports.workItems.state, "pending")];
  const res = db.update(schema_exports.workItems).set({
    state: "active",
    worker_id: workerId ?? void 0,
    acknowledged_at: now,
    started_at: now,
    lease_expires_at: leaseExpires
  }).where(and(...conditions)).run();
  if (res.changes === 0) return void 0;
  return getWorkItem(id);
}
function heartbeatWorkItem(id) {
  const db = getDrizzle();
  const item = getWorkItem(id);
  if (!item) return void 0;
  const now = nowMs();
  if (item.state === "completed" || item.state === "failed") {
    return {
      type: "work_heartbeat",
      last_heartbeat: toIso(now),
      lease_extended: false,
      state: item.state,
      ttl_seconds: 0
    };
  }
  const leaseExpires = now + LEASE_TTL_MS;
  db.update(schema_exports.workItems).set({
    latest_heartbeat_at: now,
    lease_expires_at: leaseExpires
  }).where(eq(schema_exports.workItems.id, id)).run();
  return {
    type: "work_heartbeat",
    last_heartbeat: toIso(now),
    lease_extended: true,
    state: item.state,
    ttl_seconds: LEASE_TTL_MS / 1e3
  };
}
function completeWorkItem(id, state) {
  const db = getDrizzle();
  const now = nowMs();
  const res = db.update(schema_exports.workItems).set({
    state,
    stopped_at: now,
    lease_expires_at: null
  }).where(eq(schema_exports.workItems.id, id)).run();
  if (res.changes === 0) return void 0;
  return getWorkItem(id);
}
function stopWorkItem(id, force) {
  const db = getDrizzle();
  const now = nowMs();
  if (force) {
    const res = db.update(schema_exports.workItems).set({
      state: "failed",
      stop_requested_at: now,
      stopped_at: now,
      lease_expires_at: null
    }).where(eq(schema_exports.workItems.id, id)).run();
    if (res.changes === 0) return void 0;
  } else {
    const res = db.update(schema_exports.workItems).set({ stop_requested_at: now }).where(eq(schema_exports.workItems.id, id)).run();
    if (res.changes === 0) return void 0;
  }
  return getWorkItem(id);
}
function updateWorkItemMetadata(id, metadata) {
  const db = getDrizzle();
  const item = getWorkItem(id);
  if (!item) return void 0;
  const merged = { ...item.metadata };
  for (const [key, value] of Object.entries(metadata)) {
    if (value === null) {
      delete merged[key];
    } else {
      merged[key] = value;
    }
  }
  db.update(schema_exports.workItems).set({ metadata_json: JSON.stringify(merged) }).where(eq(schema_exports.workItems.id, id)).run();
  return getWorkItem(id);
}
function getWorkQueueStats(envId) {
  const db = getDrizzle();
  const rows = db.all(
    sql`SELECT state, COUNT(*) as cnt
        FROM work_items
        WHERE environment_id = ${envId}
        GROUP BY state`
  );
  const counts = {};
  for (const row of rows) {
    counts[row.state] = row.cnt;
  }
  const depth = counts["queued"] ?? 0;
  const pending = (counts["pending"] ?? 0) + (counts["active"] ?? 0);
  const oldestRow = db.all(
    sql`SELECT MIN(created_at) as oldest
        FROM work_items
        WHERE environment_id = ${envId} AND state = 'queued'`
  );
  const oldestQueuedAt = oldestRow[0]?.oldest ? toIso(oldestRow[0].oldest) : null;
  const workerRows = db.all(
    sql`SELECT COUNT(DISTINCT worker_id) as cnt
        FROM work_items
        WHERE environment_id = ${envId}
          AND state IN ('pending', 'active')
          AND worker_id IS NOT NULL`
  );
  const workersPollling = workerRows[0]?.cnt ?? 0;
  return {
    type: "work_queue_stats",
    depth,
    pending,
    workers_polling: workersPollling > 0 ? workersPollling : null,
    oldest_queued_at: oldestQueuedAt
  };
}

export {
  createWorkItem,
  getWorkItem,
  getWorkItemInputs,
  listWorkItems,
  pollWorkItem,
  ackWorkItem,
  heartbeatWorkItem,
  completeWorkItem,
  stopWorkItem,
  updateWorkItemMetadata,
  getWorkQueueStats
};
