/**
 * Claude Agent
 * Handles: claude-query, claude-task-query
 *
 * These are STREAMING actions — they use ctx.stream() to send chunks
 * and return a Promise that resolves when the process completes.
 *
 * Three modes:
 * - sdk (connect.js default) — full SDK streaming with rich messages (tool_use, text, result)
 * - simple --print (relay-client.js) — basic stdout streaming
 *
 * The caller chooses the mode via ctx.streamMode ('structured' | 'simple').
 * 'structured' now maps to SDK mode for rich message forwarding.
 */
import { query } from '@anthropic-ai/claude-agent-sdk';
import { spawn } from 'child_process';
import os from 'os';

export default {
  name: 'claude',
  actions: {
    'claude-query': async (params, ctx) => {
      const { command, options } = params;
      const mode = ctx.streamMode || 'structured';

      if (mode === 'simple') {
        const resolveBinary = ctx.resolveBinary || ((name) => name);
        return runSimpleClaudeQuery(command, options, ctx, resolveBinary);
      }
      return runSDKClaudeQuery(command, options, ctx);
    },

    'claude-task-query': async (params, ctx) => {
      const { command, options } = params;
      return runSDKClaudeTaskQuery(command, options, ctx);
    },
  },
};

/**
 * Build SDK options from relay command options.
 * Maps the relay format to SDK Options type.
 */
function buildSDKOptions(options = {}) {
  const sdkOptions = {
    cwd: options.projectPath || os.homedir(),
    systemPrompt: { type: 'preset', preset: 'claude_code' },
    settingSources: ['project', 'user', 'local'],
  };

  if (options.sessionId) {
    sdkOptions.resume = options.sessionId;
  }

  if (options.model) {
    sdkOptions.model = options.model;
  }

  if (options.maxBudgetUsd) {
    sdkOptions.maxBudgetUsd = options.maxBudgetUsd;
  }

  if (options.permissionMode && options.permissionMode !== 'default') {
    sdkOptions.permissionMode = options.permissionMode;
  }

  if (options.allowedTools && options.allowedTools.length > 0) {
    sdkOptions.allowedTools = options.allowedTools;
  }

  if (options.disallowedTools && options.disallowedTools.length > 0) {
    sdkOptions.disallowedTools = options.disallowedTools;
  }

  if (options.maxTurns) {
    sdkOptions.maxTurns = options.maxTurns;
  }

  // Pass through tools preset to make all Claude Code tools available
  sdkOptions.tools = { type: 'preset', preset: 'claude_code' };

  return sdkOptions;
}

/**
 * SDK-based structured streaming mode.
 * Forwards rich SDK messages (tool_use, tool_result, text, system, result)
 * through ctx.stream() for the relay to forward to the frontend.
 */
async function runSDKClaudeQuery(command, options, ctx) {
  const sdkOptions = buildSDKOptions(options);

  // Set stream-close timeout for interactive tools
  const prevStreamTimeout = process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT;
  process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT = '300000';

  let queryInstance;
  try {
    queryInstance = query({
      prompt: command || '',
      options: sdkOptions,
    });
  } finally {
    // Restore immediately — Query constructor already captured the value
    if (prevStreamTimeout !== undefined) {
      process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT = prevStreamTimeout;
    } else {
      delete process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT;
    }
  }

  // Track the query instance for abort support (uses .interrupt() instead of .kill())
  if (ctx.trackProcess) {
    ctx.trackProcess(ctx.requestId, { instance: queryInstance, action: 'claude-query' });
  }

  let capturedSessionId = null;

  try {
    for await (const message of queryInstance) {
      // Capture session ID from first message that has one
      if (message.session_id && !capturedSessionId) {
        capturedSessionId = message.session_id;
      }

      // Forward the full SDK message — the backend routeViaRelay() will
      // detect 'claude-sdk-message' type and forward directly to frontend,
      // matching the same format as queryClaudeSDK() in claude-sdk.js
      ctx.stream({
        type: 'claude-sdk-message',
        data: message,
        sessionId: capturedSessionId,
      });
    }

    return { exitCode: 0, sessionId: capturedSessionId };
  } catch (error) {
    ctx.stream({ type: 'claude-error', content: error.message || 'SDK query failed' });
    return { exitCode: 1, sessionId: capturedSessionId };
  } finally {
    if (ctx.untrackProcess) ctx.untrackProcess(ctx.requestId);
  }
}

/**
 * SDK-based sub-agent for read-only research tasks.
 * Restricts tools to read-only operations.
 */
async function runSDKClaudeTaskQuery(command, options, ctx) {
  const sdkOptions = buildSDKOptions(options);

  // Restrict to read-only tools
  sdkOptions.allowedTools = ['View', 'Glob', 'Grep', 'LS', 'Read'];

  let queryInstance;
  try {
    queryInstance = query({
      prompt: command || '',
      options: sdkOptions,
    });
  } catch (error) {
    ctx.stream({ type: 'claude-error', content: error.message || 'SDK task query failed' });
    return { exitCode: 1 };
  }

  if (ctx.trackProcess) {
    ctx.trackProcess(ctx.requestId, { instance: queryInstance, action: 'claude-task-query' });
  }

  let capturedSessionId = null;

  try {
    for await (const message of queryInstance) {
      if (message.session_id && !capturedSessionId) {
        capturedSessionId = message.session_id;
      }

      ctx.stream({
        type: 'claude-sdk-message',
        data: message,
        sessionId: capturedSessionId,
      });
    }

    return { exitCode: 0, sessionId: capturedSessionId };
  } catch (error) {
    ctx.stream({ type: 'claude-error', content: error.message || 'SDK task query failed' });
    return { exitCode: 1, sessionId: capturedSessionId };
  } finally {
    if (ctx.untrackProcess) ctx.untrackProcess(ctx.requestId);
  }
}

/**
 * Simple streaming mode (--print).
 * Just pipes stdout/stderr chunks. Kept for backward compat with relay-client.js.
 */
function runSimpleClaudeQuery(command, options, ctx, resolveBinary) {
  return new Promise((resolve) => {
    const args = ['--print'];
    if (options?.sessionId) args.push('--continue', options.sessionId);

    const proc = spawn(resolveBinary('claude'), [...args, command || ''], {
      shell: true,
      cwd: options?.projectPath || os.homedir(),
      env: process.env,
    });

    if (ctx.trackProcess) ctx.trackProcess(ctx.requestId, { proc, action: 'claude-query' });

    proc.stdout.on('data', (chunk) => {
      ctx.stream({ type: 'claude-response', content: chunk.toString() });
    });

    proc.stderr.on('data', (chunk) => {
      ctx.stream({ type: 'claude-error', content: chunk.toString() });
    });

    proc.on('close', (code) => {
      if (ctx.untrackProcess) ctx.untrackProcess(ctx.requestId);
      resolve({ exitCode: code });
    });

    proc.on('error', () => {
      if (ctx.untrackProcess) ctx.untrackProcess(ctx.requestId);
      resolve({ exitCode: 1 });
    });
  });
}
