const fs = require('fs');
const path = require('path');

/**
 * Local cron adapter.
 *
 * Generates `scripts/routines/<name>.sh` (a self-contained wrapper) and prints
 * the exact crontab line the user must install. Does NOT touch the user's
 * crontab directly — that would be too invasive.
 *
 * Requires the `claude` CLI to be available on the user's PATH.
 */
class CronAdapter {
  constructor(cwd = process.cwd()) {
    this.cwd = cwd;
  }

  get name() { return 'cron'; }
  get label() { return 'Local cron (shell wrapper + crontab line)'; }
  get description() {
    return 'Generates a shell wrapper. You add a single line to your crontab. Best for self-hosted runs.';
  }

  scriptDir() {
    return path.join(this.cwd, 'scripts', 'routines');
  }

  scriptPath(name) {
    return path.join(this.scriptDir(), `${name}.sh`);
  }

  install(spec) {
    if (!fs.existsSync(this.scriptDir())) {
      fs.mkdirSync(this.scriptDir(), { recursive: true });
    }
    const sh = this._renderShellWrapper(spec);
    fs.writeFileSync(this.scriptPath(spec.name), sh);
    fs.chmodSync(this.scriptPath(spec.name), 0o755);

    const absPath = path.resolve(this.cwd, this.scriptPath(spec.name));
    const crontabLine = `${spec.schedule.cron}  ${absPath} >> ${path.dirname(absPath)}/${spec.name}.log 2>&1`;

    return {
      backend: this.name,
      artifacts: [path.relative(this.cwd, this.scriptPath(spec.name))],
      followup: [
        `Add this line to your crontab (\`crontab -e\`):`,
        ``,
        `  ${crontabLine}`,
        ``,
        `Make sure the \`claude\` CLI is on PATH for the cron user and that ANTHROPIC_API_KEY is exported in the script env (edit the wrapper to suit).`,
        `Manual test: ${absPath}`
      ],
      crontab_line: crontabLine
    };
  }

  uninstall(name) {
    const p = this.scriptPath(name);
    if (fs.existsSync(p)) fs.unlinkSync(p);
    return {
      backend: this.name,
      followup: [
        `Remove the crontab line that referenced scripts/routines/${name}.sh.`,
        `Run \`crontab -e\` and delete the matching entry.`
      ]
    };
  }

  detect(name) {
    return fs.existsSync(this.scriptPath(name));
  }

  _renderShellWrapper(spec) {
    const outputPath = (spec.output && spec.output.path) || `docs/reports/{{YYYYMMDD}}-${spec.name}.md`;
    const commitEnabled = spec.output && spec.output.commit && spec.output.commit.enabled;
    const commitPrefix = (spec.output && spec.output.commit && spec.output.commit.prefix) || `[${spec.name.toUpperCase()}]`;
    const promptHeredoc = spec.prompt;
    // review_engine (since v4.18.0): cron is the ONLY backend that supports `codex`. When set, the
    // wrapper drives the OpenAI Codex companion and falls back to `claude --agent` if it is absent.
    const engine = String(spec.review_engine || 'claude').toLowerCase();
    const promptCodex = spec.prompt_codex || spec.prompt;
    const repoRoot = '"$(cd "$(dirname "$0")/../.." && pwd)"';

    return `#!/usr/bin/env bash
# Generated by BALDART (v${require('../../../package.json').version}) — npx baldart routines
# Routine: ${spec.name}
# ${spec.description}
#
# Schedule: ${spec.schedule.cron}   (${spec.schedule.cadence_label || 'scheduled'} ${spec.schedule.timezone || 'UTC'})
#
# Add to crontab (\`crontab -e\`):
#   ${spec.schedule.cron}  ${'$(pwd)'}/scripts/routines/${spec.name}.sh >> ${'$(pwd)'}/scripts/routines/${spec.name}.log 2>&1

set -euo pipefail

# Resolve repo root and move into it so relative paths in the prompt work.
REPO_ROOT=${repoRoot}
cd "$REPO_ROOT"

# Required: \`claude\` CLI on PATH and ANTHROPIC_API_KEY set.
if ! command -v claude >/dev/null 2>&1; then
  echo "ERROR: claude CLI not found on PATH" >&2
  exit 1
fi
${engine === 'codex'
  ? `# review_engine: codex — Codex authenticates via the plugin + ~/.codex (NOT ANTHROPIC_API_KEY).
# The key is only needed if the run falls back to claude, so this is a soft warning, not a hard stop.
if [ -z "\${ANTHROPIC_API_KEY:-}" ]; then
  echo "NOTE: ANTHROPIC_API_KEY unset — fine for the Codex engine, but the claude fallback would fail." >&2
fi`
  : `: "\${ANTHROPIC_API_KEY:?ANTHROPIC_API_KEY must be set (export it from the cron env or your shell profile)}"`}

# Marks this as an unattended (no-human) run. The /baldart-update skill (and
# any other autonomy-aware skill) auto-detects this and switches to
# non-interactive mode — no chat confirmations, safe guardrails (STOP on
# custom divergence / conflicts, never forces a destructive merge). Exported
# so the \`claude\` process and every child Bash it spawns inherit it.
export BALDART_AUTONOMOUS=1

DATE=$(date -u +%Y%m%d)
OUT_PATH="${outputPath}"
OUT_PATH="\${OUT_PATH//\\{\\{YYYYMMDD\\}\\}/$DATE}"
mkdir -p "$(dirname "$OUT_PATH")"

# Claude engine (also the fallback for the Codex engine).
run_claude() {
  claude --print --output-format json --mode bypassPermissions \\
    --agent ${spec.agent} \\
    > /tmp/baldart-${spec.name}.json <<'PROMPT'
${promptHeredoc}
PROMPT
  # Persist the output if the agent did not already write to disk
  if [ ! -s "$OUT_PATH" ]; then
    jq -r '.result // .' /tmp/baldart-${spec.name}.json > "$OUT_PATH" 2>/dev/null \\
      || cp /tmp/baldart-${spec.name}.json "$OUT_PATH"
  fi
}
${engine === 'codex'
  ? `# review_engine: codex — resolve the OpenAI Codex companion (glob decides existence, not us).
CODEX_SCRIPT="$(ls -d ~/.claude/plugins/marketplaces/openai-codex/plugins/codex/scripts/codex-companion.mjs ~/.claude/plugins/cache/openai-codex/codex/*/scripts/codex-companion.mjs 2>/dev/null | sort -V | tail -1)"
if [ -n "$CODEX_SCRIPT" ] && command -v node >/dev/null 2>&1; then
  echo "Codex engine: $CODEX_SCRIPT"
  node "$CODEX_SCRIPT" task "$(cat <<'CODEXPROMPT'
${promptCodex}
CODEXPROMPT
)" --wait > "$OUT_PATH" 2>/tmp/baldart-${spec.name}-codex.err || true
  if [ ! -s "$OUT_PATH" ]; then
    echo "WARN: Codex produced no output (see /tmp/baldart-${spec.name}-codex.err) — falling back to claude" >&2
    run_claude
  fi
else
  echo "WARN: CODEX_NOT_FOUND — codex plugin / ~/.codex not present for this user, or node missing — falling back to claude" >&2
  run_claude
fi`
  : `run_claude`}
${commitEnabled ? `
# Auto-commit the report and any inline fixes
if git diff --quiet && git diff --staged --quiet; then
  echo "No changes to commit."
else
  git add -A
  git commit -m "${commitPrefix} routine $(date -u +%Y-%m-%d)"
  # Push only when on a branch with a configured upstream
  if git rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' >/dev/null 2>&1; then
    git push
  fi
fi
` : ''}
echo "OK: ${spec.name} finished at $(date -u)"
`;
  }
}

module.exports = CronAdapter;
