import {
  LUMENFLOW_MEMORY_PATHS
} from "./chunk-4N74J3UT.js";
import {
  ErrorCodes,
  createError
} from "./chunk-RXRKBBSM.js";

// ../memory/dist/mem-signal-core.js
import { randomBytes } from "crypto";
import fs from "fs/promises";
import path from "path";
var SIGNAL_FILE_NAME = "signals.jsonl";
var SIGNAL_RECEIPTS_FILE_NAME = "signal-receipts.jsonl";
var WU_ID_PATTERN = /^WU-\d+$/;
var SIGNAL_ID_PREFIX = "sig-";
var SIGNAL_ID_LENGTH = 8;
var ERROR_MESSAGES = {
  MESSAGE_REQUIRED: "message is required and cannot be empty",
  INVALID_WU_ID: "Invalid WU ID format. Expected WU-XXX (e.g., WU-1473)",
  INVALID_SIGNAL_METADATA: "Signal metadata fields must be non-empty strings when provided",
  LEGACY_SIGNAL_RECORD: "Legacy signal record is not supported by the strict signal contract. Migrate records before loading signals.",
  INVALID_SIGNAL_RECORD: "Invalid signal record in signals.jsonl"
};
var DEFAULT_SIGNAL_TYPE = "coordination";
var DEFAULT_SIGNAL_SENDER = "system";
var DEFAULT_SIGNAL_ORIGIN = "local";
function normalizeOptionalString(value, fieldName) {
  if (value === void 0) {
    return void 0;
  }
  const normalized = value.trim();
  if (normalized.length === 0) {
    throw createError(ErrorCodes.VALIDATION_ERROR, `${ERROR_MESSAGES.INVALID_SIGNAL_METADATA}: ${fieldName}`);
  }
  return normalized;
}
function isNonEmptyString(value) {
  return typeof value === "string" && value.trim().length > 0;
}
function parseStrictSignalRecord(line, lineNumber) {
  let parsed;
  try {
    parsed = JSON.parse(line);
  } catch {
    throw createError(ErrorCodes.VALIDATION_ERROR, `${ERROR_MESSAGES.INVALID_SIGNAL_RECORD} at line ${lineNumber}`);
  }
  if (typeof parsed !== "object" || parsed === null) {
    throw createError(ErrorCodes.VALIDATION_ERROR, `${ERROR_MESSAGES.INVALID_SIGNAL_RECORD} at line ${lineNumber}`);
  }
  const candidate = parsed;
  if (!isNonEmptyString(candidate.id) || !isNonEmptyString(candidate.message) || !isNonEmptyString(candidate.created_at) || typeof candidate.read !== "boolean") {
    throw createError(ErrorCodes.VALIDATION_ERROR, `${ERROR_MESSAGES.INVALID_SIGNAL_RECORD} at line ${lineNumber}`);
  }
  if (!isNonEmptyString(candidate.type) || !isNonEmptyString(candidate.sender) || !isNonEmptyString(candidate.origin) || !isNonEmptyString(candidate.remote_id)) {
    throw createError(ErrorCodes.VALIDATION_ERROR, `${ERROR_MESSAGES.LEGACY_SIGNAL_RECORD} (line ${lineNumber})`);
  }
  return {
    ...candidate,
    id: candidate.id,
    message: candidate.message,
    created_at: candidate.created_at,
    read: candidate.read,
    type: candidate.type,
    sender: candidate.sender,
    origin: candidate.origin,
    remote_id: candidate.remote_id
  };
}
function generateSignalId() {
  const bytes = randomBytes(4);
  const hex = bytes.toString("hex").slice(0, SIGNAL_ID_LENGTH);
  return `${SIGNAL_ID_PREFIX}${hex}`;
}
function getMemoryDir(baseDir) {
  return path.join(baseDir, LUMENFLOW_MEMORY_PATHS.MEMORY_DIR);
}
function getSignalsPath(baseDir) {
  return path.join(getMemoryDir(baseDir), SIGNAL_FILE_NAME);
}
function getReceiptsPath(baseDir) {
  return path.join(getMemoryDir(baseDir), SIGNAL_RECEIPTS_FILE_NAME);
}
async function loadReceiptIds(baseDir) {
  const receiptsPath = getReceiptsPath(baseDir);
  let content;
  try {
    content = await fs.readFile(receiptsPath, { encoding: "utf-8" });
  } catch (err) {
    const error = err;
    if (error.code === "ENOENT") {
      return /* @__PURE__ */ new Set();
    }
    throw error;
  }
  const lines = content.split("\n").filter((line) => line.trim());
  const ids = /* @__PURE__ */ new Set();
  for (const line of lines) {
    const receipt = JSON.parse(line);
    ids.add(receipt.signal_id);
  }
  return ids;
}
function isValidWuId(wuId) {
  return WU_ID_PATTERN.test(wuId);
}
async function createSignal(baseDir, options) {
  const { message, wuId, lane, type, sender, target_agent, origin, remote_id } = options;
  if (!message || typeof message !== "string" || message.trim().length === 0) {
    throw createError(ErrorCodes.VALIDATION_ERROR, ERROR_MESSAGES.MESSAGE_REQUIRED);
  }
  if (wuId !== void 0 && !isValidWuId(wuId)) {
    throw createError(ErrorCodes.INVALID_WU_ID, ERROR_MESSAGES.INVALID_WU_ID);
  }
  const normalizedType = normalizeOptionalString(type, "type") ?? DEFAULT_SIGNAL_TYPE;
  const normalizedSender = normalizeOptionalString(sender, "sender") ?? DEFAULT_SIGNAL_SENDER;
  const normalizedTargetAgent = normalizeOptionalString(target_agent, "target_agent");
  const normalizedOrigin = normalizeOptionalString(origin, "origin") ?? DEFAULT_SIGNAL_ORIGIN;
  const normalizedRemoteId = normalizeOptionalString(remote_id, "remote_id");
  const signal = {
    id: generateSignalId(),
    message: message.trim(),
    created_at: (/* @__PURE__ */ new Date()).toISOString(),
    read: false,
    type: normalizedType,
    sender: normalizedSender,
    origin: normalizedOrigin,
    remote_id: ""
  };
  signal.remote_id = normalizedRemoteId ?? signal.id;
  if (wuId) {
    signal.wu_id = wuId;
  }
  if (lane) {
    signal.lane = lane;
  }
  if (normalizedTargetAgent) {
    signal.target_agent = normalizedTargetAgent;
  }
  const memoryDir = getMemoryDir(baseDir);
  await fs.mkdir(memoryDir, { recursive: true });
  const signalsPath = getSignalsPath(baseDir);
  const line = `${JSON.stringify(signal)}
`;
  await fs.appendFile(signalsPath, line, "utf-8");
  return {
    success: true,
    signal
  };
}
async function loadSignals(baseDir, options = {}) {
  const { wuId, lane, unreadOnly, since } = options;
  const signalsPath = getSignalsPath(baseDir);
  let content;
  try {
    content = await fs.readFile(signalsPath, { encoding: "utf-8" });
  } catch (err) {
    const error = err;
    if (error.code === "ENOENT") {
      return [];
    }
    throw error;
  }
  const lines = content.split("\n").filter((line) => line.trim());
  const signals = lines.map((line, index) => parseStrictSignalRecord(line, index + 1));
  const receiptIds = await loadReceiptIds(baseDir);
  for (const signal of signals) {
    if (!signal.read && receiptIds.has(signal.id)) {
      signal.read = true;
    }
  }
  let filtered = signals;
  if (wuId) {
    filtered = filtered.filter((sig) => sig.wu_id === wuId);
  }
  if (lane) {
    filtered = filtered.filter((sig) => sig.lane === lane);
  }
  if (unreadOnly) {
    filtered = filtered.filter((sig) => sig.read === false);
  }
  if (since) {
    const sinceTime = since instanceof Date ? since : new Date(since);
    filtered = filtered.filter((sig) => new Date(sig.created_at) > sinceTime);
  }
  return filtered;
}
async function markSignalsAsRead(baseDir, signalIds) {
  const signalsPath = getSignalsPath(baseDir);
  const idSet = new Set(signalIds);
  let signalContent;
  try {
    signalContent = await fs.readFile(signalsPath, { encoding: "utf-8" });
  } catch (err) {
    const error = err;
    if (error.code === "ENOENT") {
      return { markedCount: 0 };
    }
    throw error;
  }
  const lines = signalContent.split("\n").filter((line) => line.trim());
  const inlineReadIds = /* @__PURE__ */ new Set();
  const existingIds = /* @__PURE__ */ new Set();
  for (const line of lines) {
    const signal = JSON.parse(line);
    existingIds.add(signal.id);
    if (signal.read) {
      inlineReadIds.add(signal.id);
    }
  }
  const existingReceiptIds = await loadReceiptIds(baseDir);
  const now = (/* @__PURE__ */ new Date()).toISOString();
  const newReceipts = [];
  for (const id of idSet) {
    if (existingIds.has(id) && !inlineReadIds.has(id) && !existingReceiptIds.has(id)) {
      newReceipts.push({ signal_id: id, read_at: now });
    }
  }
  if (newReceipts.length > 0) {
    const memoryDir = getMemoryDir(baseDir);
    await fs.mkdir(memoryDir, { recursive: true });
    const receiptsPath = getReceiptsPath(baseDir);
    const receiptLines = newReceipts.map((r) => JSON.stringify(r)).join("\n") + "\n";
    await fs.appendFile(receiptsPath, receiptLines, "utf-8");
  }
  return { markedCount: newReceipts.length };
}

export {
  SIGNAL_FILE_NAME,
  SIGNAL_RECEIPTS_FILE_NAME,
  createSignal,
  loadSignals,
  markSignalsAsRead
};
