/**
 * Agent API — exposes the active agent harness to workspace code.
 *
 * Single endpoint: POST /api/agent/query
 *
 * If `systemPromptPath` is provided → read it from workspace, use as systemPrompt.
 * If omitted → harness picks its own default coding-agent prompt
 *   (Claude → `claude_code` preset; Codex → its built-in agent prompt).
 *
 * Supports session persistence via `sessionId` (pass the one returned from a
 * previous call). Provider-specific: Claude uses SDK session_id, Codex uses
 * threadId — but the value is opaque to the caller.
 *
 * Auth: per-session secret injected into the workspace backend as BLOBY_AGENT_SECRET.
 * Safety: localhost-only, path traversal prevention, concurrency + rate limits, timeouts.
 */

import fs from 'fs';
import path from 'path';
import { WORKSPACE_DIR } from '../shared/paths.js';
import { runAgentQuery } from './bloby-agent.js';

// ── Types ──────────────────────────────────────────────────────────────────

export interface AgentQueryRequest {
  message: string;
  systemPromptPath?: string;    // relative to workspace, e.g. "skills/my-skill/prompt.txt"
  sessionId?: string;           // resume a previous session
  maxTurns?: number;            // default 25, max 50 (Claude only)
  timeout?: number;             // ms, default 120_000, max 300_000
}

export interface AgentQueryResponse {
  ok: boolean;
  response?: string;
  sessionId?: string;
  toolsUsed?: string[];
  usedFileTools?: boolean;
  error?: string;
}

// ── Concurrency & Rate Limiting ────────────────────────────────────────────

const MAX_CONCURRENT = 3;
const RATE_LIMIT_WINDOW = 60_000; // 1 minute
const RATE_LIMIT_MAX = 10;

let activeQueries = 0;
const requestTimestamps: number[] = [];

function checkRateLimit(): string | null {
  if (activeQueries >= MAX_CONCURRENT) {
    return `Too many concurrent queries (max ${MAX_CONCURRENT}). Try again shortly.`;
  }

  const now = Date.now();
  while (requestTimestamps.length && requestTimestamps[0]! < now - RATE_LIMIT_WINDOW) {
    requestTimestamps.shift();
  }
  if (requestTimestamps.length >= RATE_LIMIT_MAX) {
    return `Rate limit exceeded (max ${RATE_LIMIT_MAX} requests per minute).`;
  }

  return null;
}

// ── Path Validation ────────────────────────────────────────────────────────

function resolveSystemPromptPath(relPath: string): { content: string } | { error: string } {
  const resolved = path.resolve(WORKSPACE_DIR, relPath);
  const workspaceBoundary = WORKSPACE_DIR + path.sep;

  if (!resolved.startsWith(workspaceBoundary) && resolved !== WORKSPACE_DIR) {
    return { error: 'System prompt path must be within the workspace directory.' };
  }

  if (!fs.existsSync(resolved)) {
    return { error: `System prompt file not found: ${relPath}` };
  }

  try {
    const content = fs.readFileSync(resolved, 'utf-8').trim();
    if (!content) return { error: 'System prompt file is empty.' };
    return { content };
  } catch (err: any) {
    return { error: `Failed to read system prompt: ${err.message}` };
  }
}

// ── Main Query Handler ─────────────────────────────────────────────────────

export async function handleAgentQuery(req: AgentQueryRequest): Promise<AgentQueryResponse> {
  if (!req.message || typeof req.message !== 'string') {
    return { ok: false, error: 'Missing or invalid "message" field.' };
  }

  const rateLimitError = checkRateLimit();
  if (rateLimitError) return { ok: false, error: rateLimitError };

  let systemPrompt: string | undefined;
  if (req.systemPromptPath) {
    const result = resolveSystemPromptPath(req.systemPromptPath);
    if ('error' in result) return { ok: false, error: result.error };
    systemPrompt = result.content;
  }

  activeQueries++;
  requestTimestamps.push(Date.now());

  try {
    const result = await runAgentQuery({
      message: req.message,
      systemPrompt,
      sessionId: req.sessionId,
      maxTurns: req.maxTurns,
      timeout: req.timeout,
    });
    return result;
  } finally {
    activeQueries--;
  }
}
