'use strict';
/**
 * Heartbeat / boot file content helpers.
 *
 * Port of OpenClaw's isHeartbeatContentEffectivelyEmpty
 * (src/auto-reply/heartbeat.ts:32-60). Used to decide whether a workspace
 * contract file (HEARTBEAT.md or BOOT.md) carries any actionable
 * instructions, so we can skip an LLM call when the file is empty / just
 * scaffolding.
 *
 * A file is "effectively empty" when every non-blank line is one of:
 *   - Whitespace only
 *   - Markdown ATX header (# / ## / ### …) — `^#+(\s|$)`
 *   - Markdown fence marker (``` or ```language) — `^```[A-Za-z0-9_-]*$`
 *   - Empty list-item stub (`- `, `- [ ]`, `* [x]`, `+ ` …)
 *
 * Notes:
 *   - A MISSING file (content === undefined/null) is NOT effectively empty —
 *     callers can decide what to do (OpenClaw lets the LLM still react).
 *   - Strings like `# TODO` (no space after #) are NOT valid ATX headers
 *     and DO count as content.
 *   - Hashtags (`#follow-up`) do NOT count as ATX headers either.
 */

const ATX_HEADER_RE = /^#+(\s|$)/;
const EMPTY_LIST_RE = /^[-*+]\s*(\[[\sXx]?\]\s*)?$/;
const FENCE_MARKER_RE = /^```[A-Za-z0-9_-]*$/;

function isEffectivelyEmpty(content) {
  if (content === undefined || content === null) return false;
  if (typeof content !== 'string') return false;

  const lines = content.split('\n');
  for (const line of lines) {
    const trimmed = line.trim();
    if (!trimmed) continue;
    if (ATX_HEADER_RE.test(trimmed)) continue;
    if (EMPTY_LIST_RE.test(trimmed)) continue;
    if (FENCE_MARKER_RE.test(trimmed)) continue;
    return false; // found a non-empty content line
  }
  return true;
}

/**
 * Read a workspace contract file safely. Returns
 *   { status: 'ok', content }      — file present and has content
 *   { status: 'empty' }            — file present but trim()=='' (whole file blank)
 *   { status: 'effectively-empty' } — file has only headers/whitespace/list-stubs
 *   { status: 'missing' }          — ENOENT
 *   { status: 'error', error }     — other read error
 *
 * Caller decides what to do with each status. The intended split for the
 * heartbeat-style use case:
 *   - ok                  → run the loop (content goes into the prompt)
 *   - empty / effectively-empty → skip the LLM call this tick
 *   - missing             → loop runs as normal (no contract)
 *   - error               → log + run as normal (don't crash on fs hiccup)
 */
function readContractFile(filePath) {
  const fs = require('node:fs');
  let raw;
  try {
    raw = fs.readFileSync(filePath, 'utf8');
  } catch (err) {
    if (err.code === 'ENOENT') return { status: 'missing' };
    return { status: 'error', error: err };
  }
  const trimmedWhole = raw.trim();
  if (!trimmedWhole) return { status: 'empty' };
  if (isEffectivelyEmpty(raw)) return { status: 'effectively-empty' };
  return { status: 'ok', content: trimmedWhole };
}

module.exports = {
  isEffectivelyEmpty,
  readContractFile,
};
