'use strict';
/**
 * Boot phase — one-shot LLM turn at daemon startup that reads BOOT.md.
 *
 * Port of OpenClaw's gateway/boot.ts (lines 36-100). The user puts a
 * markdown file at ~/.walle/BOOT.md describing things the agent should
 * verify / announce / message every time it starts. On daemon boot
 * (before scheduler.start()), Wall-E reads the file, builds a focused
 * boot prompt, and fires ONE chat turn. Successful run is signaled by
 * the model replying with the BOOT_OK token.
 *
 * Status returned (matches OpenClaw's BootRunResult shape):
 *   { status: 'missing' }                — no BOOT.md
 *   { status: 'empty' }                  — file present but blank/scaffolding
 *   { status: 'ran', latencyMs, content }— LLM call succeeded
 *   { status: 'failed', reason }         — LLM call threw / no provider
 *
 * The daemon NEVER aborts on a failed boot run — boot is best-effort. A
 * failed boot pass writes an entry to telemetry but the rest of the
 * daemon continues normally.
 */

const path = require('node:path');
const os = require('node:os');

const BOOT_OK_TOKEN = 'BOOT_OK';
const BOOT_TIMEOUT_MS = 30_000;

function _resolveBootPath(opts) {
  if (opts?.bootPath) return opts.bootPath;
  if (process.env.WALLE_BOOT_FILE) return process.env.WALLE_BOOT_FILE;
  const dataDir = process.env.WALLE_DATA_DIR || path.join(os.homedir(), '.walle');
  return path.join(dataDir, 'BOOT.md');
}

function _buildBootPrompt(content) {
  return [
    'You are running a one-shot boot check at Wall-E daemon startup.',
    'Follow BOOT.md exactly — it is the workspace contract for what should',
    'happen on every boot. This is NOT a conversation; it runs once on each',
    'restart with no chat history.',
    '',
    'BOOT.md:',
    content,
    '',
    `If BOOT.md asks you to use tools, do so. After all instructed actions are`,
    `complete, reply with exactly: ${BOOT_OK_TOKEN}.`,
    `If BOOT.md has nothing actionable right now, reply with exactly: ${BOOT_OK_TOKEN}.`,
  ].join('\n');
}

/**
 * @param {Object} opts
 * @param {string} [opts.bootPath]   Override path to BOOT.md (for tests).
 * @param {Object} [opts.client]     LLM client (defaults to llm/client default).
 * @param {string} [opts.model]      Model name (defaults to env WALLE_MODEL).
 * @param {Function} [opts.now]      Date.now-style function for tests.
 * @returns {Promise<{status:string,...}>}
 */
async function runBootCheck(opts = {}) {
  const { readContractFile } = require('../utils/heartbeat-content');
  const bootPath = _resolveBootPath(opts);

  const file = readContractFile(bootPath);
  if (file.status === 'missing') return { status: 'missing' };
  if (file.status === 'empty' || file.status === 'effectively-empty') {
    return { status: 'empty', file_status: file.status };
  }
  if (file.status === 'error') {
    return { status: 'failed', reason: 'read-error', error: String(file.error) };
  }

  // Provider precheck — boot needs an LLM. Use the existing helper so
  // we get the same "no provider configured" semantics as the loops.
  const { shouldRunLoop } = require('./loop-precheck');
  const pre = shouldRunLoop({ loopName: 'boot' });
  if (!pre.run) {
    return { status: 'failed', reason: pre.reason || 'precheck-failed' };
  }

  let provider;
  try {
    provider = opts.client || require('../llm/client').getDefaultClient();
  } catch (e) {
    return { status: 'failed', reason: 'no-client', error: e.message };
  }
  const { getDefaultModel, resolveCompatibleModel } = require('../llm/client');
  const model = resolveCompatibleModel(opts.model || process.env.WALLE_MODEL || getDefaultModel(), provider.type);

  const prompt = _buildBootPrompt(file.content);
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), BOOT_TIMEOUT_MS);
  const startedAt = (opts.now || Date.now)();
  try {
    const response = await provider.chat({
      model,
      maxTokens: 1024,
      system: prompt,
      messages: [{ role: 'user', content: 'Run BOOT.md.' }],
      signal: controller.signal,
    });
    const text = (response && response.content) || '';
    const endedAt = (opts.now || Date.now)();
    return {
      status: 'ran',
      latencyMs: endedAt - startedAt,
      acknowledged: text.includes(BOOT_OK_TOKEN),
      reply_preview: text.slice(0, 300),
    };
  } catch (err) {
    return {
      status: 'failed',
      reason: err.name === 'AbortError' ? 'timeout' : 'llm-error',
      error: err.message,
    };
  } finally {
    clearTimeout(timeout);
  }
}

module.exports = {
  runBootCheck,
  BOOT_OK_TOKEN,
  _buildBootPrompt, // exported for tests
};
