#!/usr/bin/env node

/**
 * Coding Agent Skill — entry point.
 *
 * Receives config via WALL_E_SKILL_CONFIG env var.
 * Resumes from WALL_E_CHECKPOINT if present.
 * Outputs CHECKPOINT: lines for crash-safe resumption.
 * Outputs final report to stdout.
 */

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

function formatProgressEvent(event = {}) {
  const subtaskId = event.subtask?.id ?? (Number.isInteger(event.index) ? event.index + 1 : '?');
  const subtaskTitle = event.subtask?.title ?? event.title ?? '';
  switch (event.type) {
    case 'subtask_start':
      return `\n--- Subtask ${subtaskId}: ${subtaskTitle} ---`;
    case 'subtask_done':
      return `  [OK] Subtask ${subtaskId} completed`;
    case 'subtask_retry':
    case 'retry':
      return `  [RETRY ${event.attempt ?? event.retry ?? '?'}] ${(event.error || '').slice(0, 200)}`;
    case 'subtask_failed':
      return `  [FAILED] Subtask ${subtaskId}: ${(event.error || '').slice(0, 200)}`;
    case 'review_concern':
      return `  [CONCERN] ${(event.issues || []).join('; ')}`;
    case 'escalate':
      return `  [ESCALATE] Stuck on subtask ${subtaskId}: ${(event.error || '').slice(0, 200)}`;
    case 'budget_exceeded':
      return `  [BUDGET] Exceeded: ~$${Number(event.spent || 0).toFixed(2)}`;
    default:
      return null;
  }
}

function resumeStartFrom(state) {
  if (Number.isInteger(state?.completed)) return state.completed;
  if (Array.isArray(state?.completed)) return state.completed.length;
  return 0;
}

async function main() {
  // Parse config
  const configStr = process.env.WALL_E_SKILL_CONFIG;
  if (!configStr) {
    throw new Error('Missing WALL_E_SKILL_CONFIG');
  }

  let config;
  try {
    config = JSON.parse(configStr);
  } catch (e) {
    throw new Error(`Invalid WALL_E_SKILL_CONFIG: ${e.message}`);
  }

  const request = config.request;
  const cwd = config.cwd || process.cwd();
  const options = config.options || {};

  if (!request) {
    throw new Error('Missing request in config');
  }

  // Load orchestrator (resolve relative to wall-e root, not skill dir)
  const walleRoot = path.resolve(__dirname, '..', '..', '..');
  const orchestrator = require(path.join(walleRoot, 'coding-orchestrator.js'));

  // Load brain if available
  let brain;
  try {
    brain = require(path.join(walleRoot, 'brain.js'));
  } catch {}

  // Check for checkpoint (resume case)
  let resumeState = null;
  const checkpoint = process.env.WALL_E_CHECKPOINT;
  if (checkpoint && fs.existsSync(checkpoint)) {
    try {
      resumeState = orchestrator.readCheckpoint(checkpoint);
      console.log(`Resuming from checkpoint: ${resumeStartFrom(resumeState)} subtasks completed`);
    } catch (e) {
      console.error('Failed to read checkpoint:', e.message);
    }
  }

  // Progress callback — logs to stdout
  function onProgress(event) {
    const line = formatProgressEvent(event);
    if (line) console.log(line);
  }

  try {
    // Plan (skip if resuming with existing plan)
    let planData;
    if (resumeState && resumeState.plan) {
      console.log('Using plan from checkpoint');
      const mergedConfig = { ...orchestrator.getDefaultConfig(), ...options };
      const { assembleContext } = require(path.join(walleRoot, 'coding-context.js'));
      const { createCodingCapabilities } = require(path.join(walleRoot, 'coding', 'capability-broker.js'));
      const capabilities = createCodingCapabilities({
        cwd,
        brain,
        mcpClient: Object.prototype.hasOwnProperty.call(options, 'mcpClient') ? options.mcpClient : undefined,
        skillRunner: Object.prototype.hasOwnProperty.call(options, 'skillRunner') ? options.skillRunner : undefined,
        enableMcpTools: options.enableMcpTools !== false,
        enableSkillTools: options.enableSkillTools !== false,
      });
      const context = await assembleContext(request, cwd, { brain, mcpClient: capabilities.mcpClient });
      planData = { plan: resumeState.plan, context, config: { ...mergedConfig, _brain: brain }, capabilities };
    } else {
      console.log('Planning...');
      planData = await orchestrator.plan(request, cwd, { ...options, brain });
      console.log(`Plan: ${planData.plan.subtasks.length} subtasks, branch: ${planData.plan.branch_name}`);

      // Guardrail check
      if (planData.plan.subtasks.length > planData.config.guardrail_threshold) {
        console.log(`\n[GUARDRAIL] Plan has ${planData.plan.subtasks.length} subtasks (threshold: ${planData.config.guardrail_threshold})`);
        console.log('Subtasks:');
        planData.plan.subtasks.forEach(s => console.log(`  ${s.id}. ${s.title}`));
        console.log('\nPausing for approval. Re-run to continue.');
        const statePath = path.join(cwd, '.wall-e-coding-state.json');
        orchestrator.writeCheckpoint(statePath, {
          completed: [], plan: planData.plan,
          cumulative_context: '', branch: planData.plan.branch_name, session_ids: {},
        });
        console.log(`CHECKPOINT: ${statePath}`);
        return;
      }
    }

    // Execute
    console.log('\nExecuting...');
    const executeResult = await orchestrator.execute(planData, {
      cwd,
      onProgress,
      startFrom: resumeStartFrom(resumeState),
    });

    if (!executeResult.success) {
      throw new Error(`Execution failed: ${executeResult.error}`);
    }

    // Complete
    console.log('\nCompleting...');
    const report = await orchestrator.complete(request, planData, executeResult, {
      cwd, brain, onProgress,
    });

    console.log('\n' + orchestrator.formatReport(report));
  } catch (e) {
    // Re-throw so module callers (skill-planner) see the failure without
    // the daemon dying. CLI wrapper at the bottom maps this to exit 1.
    throw new Error(`Coding agent error: ${e.message}`);
  }
}

// Export for skill-planner; also run directly when invoked as CLI script
module.exports = { run: main, formatProgressEvent, resumeStartFrom };

if (require.main === module) {
  main().catch(err => {
    console.error('[coding-agent] Error:', err.message);
    process.exit(1);
  });
}
