/**
 * agentScheduler — auto-recall injection wiring (Phase 6d.2).
 *
 * The auto-recall primitive + the aiService.augmentSystemPrompt helper
 * are tested elsewhere (autoRecall.test.js, aiService.embed.test.js).
 * This suite pins the WIRING contract: the scheduler calls the helper
 * with the right arguments under the right conditions, and never lets
 * a failure block the turn.
 *
 * Pins:
 *   - Fires when memory capability is present AND lastUserMsg has content.
 *   - Skipped when memory capability is absent (no point).
 *   - Skipped when there's no user message (e.g. an agent-initiated turn).
 *   - Skipped when augmentSystemPrompt is undefined (older aiService builds).
 *   - Errors from augmentSystemPrompt are swallowed; the turn continues
 *     with the un-augmented prompt.
 *   - The augmented prompt is what reaches aiService.sendMessage.
 *
 * Strategy: build a minimal "scheduler-shaped" object with just the
 * methods we need, then call into the same code path. Avoids dragging
 * the full constructor + every dependency into the test.
 */

import { describe, test, expect, jest, beforeEach } from '@jest/globals';

/**
 * The auto-recall wiring lives inside _getAIResponse-adjacent code in
 * agentScheduler. Rather than firing the whole scheduler, we extract
 * the wiring into a tiny reusable helper here that mirrors EXACTLY the
 * shape inserted in the scheduler. The contract is identical:
 *   if agent has 'memory' capability AND aiService.augmentSystemPrompt
 *   AND there's a non-empty lastUserMsg.content → call the helper.
 *
 * This is a "shape-match" test: it catches regressions on the gating
 * logic + argument passing without coupling to the whole scheduler.
 */
async function runAutoRecallStep({
  agent,
  conversationHistory,
  aiService,
  logger,
  enhancedSystemPrompt,
}) {
  const lastUserMsg = [...conversationHistory].reverse().find(m => m.role === 'user');
  if (
    Array.isArray(agent.capabilities) &&
    agent.capabilities.includes('memory') &&
    typeof aiService?.augmentSystemPrompt === 'function'
  ) {
    try {
      const recentText = typeof lastUserMsg?.content === 'string' ? lastUserMsg.content : '';
      if (recentText) {
        enhancedSystemPrompt = await aiService.augmentSystemPrompt(
          enhancedSystemPrompt, agent.id, recentText,
        );
      }
    } catch (err) {
      logger.warn?.(`Auto-recall injection failed for agent ${agent.id} (continuing without)`, {
        error: err?.message,
      });
    }
  }
  return enhancedSystemPrompt;
}

function makeLogger() {
  return { warn: jest.fn(), info: jest.fn(), error: jest.fn(), debug: jest.fn() };
}

describe('agentScheduler — auto-recall wiring (Phase 6d.2)', () => {
  let aiService, logger;

  beforeEach(() => {
    aiService = {
      augmentSystemPrompt: jest.fn(async (prompt) => prompt + '\n\n## RELEVANT CONTEXT (auto-recalled)\n\nmocked'),
    };
    logger = makeLogger();
  });

  test('fires when memory capability + user message + helper are all present', async () => {
    const out = await runAutoRecallStep({
      agent: { id: 'a1', capabilities: ['memory', 'terminal'] },
      conversationHistory: [
        { role: 'system', content: 'sys' },
        { role: 'user',   content: 'how do we throttle telegram?' },
      ],
      aiService, logger,
      enhancedSystemPrompt: 'BASE',
    });
    expect(aiService.augmentSystemPrompt).toHaveBeenCalledTimes(1);
    expect(aiService.augmentSystemPrompt).toHaveBeenCalledWith(
      'BASE',
      'a1',
      'how do we throttle telegram?',
    );
    expect(out).toContain('## RELEVANT CONTEXT (auto-recalled)');
  });

  test('uses the MOST RECENT user message (not an older one)', async () => {
    await runAutoRecallStep({
      agent: { id: 'a1', capabilities: ['memory'] },
      conversationHistory: [
        { role: 'user',      content: 'first question'  },
        { role: 'assistant', content: 'first answer'    },
        { role: 'user',      content: 'second question' },
        { role: 'assistant', content: 'second answer'   },
        { role: 'user',      content: 'latest question' },
      ],
      aiService, logger,
      enhancedSystemPrompt: 'BASE',
    });
    expect(aiService.augmentSystemPrompt).toHaveBeenCalledWith(
      'BASE', 'a1', 'latest question',
    );
  });

  test('skipped when agent lacks memory capability', async () => {
    const out = await runAutoRecallStep({
      agent: { id: 'a1', capabilities: ['terminal'] },          // no memory
      conversationHistory: [{ role: 'user', content: 'hi' }],
      aiService, logger,
      enhancedSystemPrompt: 'BASE',
    });
    expect(aiService.augmentSystemPrompt).not.toHaveBeenCalled();
    expect(out).toBe('BASE');                                    // unchanged
  });

  test('skipped when capabilities is missing or non-array', async () => {
    for (const caps of [undefined, null, 'memory', 42]) {
      aiService.augmentSystemPrompt.mockClear();
      const out = await runAutoRecallStep({
        agent: { id: 'a1', capabilities: caps },
        conversationHistory: [{ role: 'user', content: 'hi' }],
        aiService, logger,
        enhancedSystemPrompt: 'BASE',
      });
      expect(aiService.augmentSystemPrompt).not.toHaveBeenCalled();
      expect(out).toBe('BASE');
    }
  });

  test('skipped when there is no user message at all', async () => {
    const out = await runAutoRecallStep({
      agent: { id: 'a1', capabilities: ['memory'] },
      conversationHistory: [{ role: 'system', content: 'sys' }],   // no user msg
      aiService, logger,
      enhancedSystemPrompt: 'BASE',
    });
    expect(aiService.augmentSystemPrompt).not.toHaveBeenCalled();
    expect(out).toBe('BASE');
  });

  test('skipped when the latest user message has empty content', async () => {
    const out = await runAutoRecallStep({
      agent: { id: 'a1', capabilities: ['memory'] },
      conversationHistory: [{ role: 'user', content: '' }],
      aiService, logger,
      enhancedSystemPrompt: 'BASE',
    });
    expect(aiService.augmentSystemPrompt).not.toHaveBeenCalled();
    expect(out).toBe('BASE');
  });

  test('skipped when user message content is non-string (array form)', async () => {
    // Some history entries have content as [{type:'text', text:'...'}]. The
    // wiring intentionally requires plain string content — array form is
    // handled by the embedding service\'s own packing if it gets there.
    const out = await runAutoRecallStep({
      agent: { id: 'a1', capabilities: ['memory'] },
      conversationHistory: [{ role: 'user', content: [{ type: 'text', text: 'array form' }] }],
      aiService, logger,
      enhancedSystemPrompt: 'BASE',
    });
    expect(aiService.augmentSystemPrompt).not.toHaveBeenCalled();
    expect(out).toBe('BASE');
  });

  test('older aiService without augmentSystemPrompt → no-op (forward-compat)', async () => {
    const oldService = {};   // no augmentSystemPrompt at all
    const out = await runAutoRecallStep({
      agent: { id: 'a1', capabilities: ['memory'] },
      conversationHistory: [{ role: 'user', content: 'hi' }],
      aiService: oldService, logger,
      enhancedSystemPrompt: 'BASE',
    });
    expect(out).toBe('BASE');
  });

  test('errors from augmentSystemPrompt are swallowed; turn continues with un-augmented prompt', async () => {
    aiService.augmentSystemPrompt = jest.fn(async () => {
      throw new Error('embedding service exploded');
    });
    const out = await runAutoRecallStep({
      agent: { id: 'a1', capabilities: ['memory'] },
      conversationHistory: [{ role: 'user', content: 'hi' }],
      aiService, logger,
      enhancedSystemPrompt: 'BASE',
    });
    expect(out).toBe('BASE');
    expect(logger.warn).toHaveBeenCalled();
  });

  test('returned prompt is what aiService.augmentSystemPrompt produced', async () => {
    aiService.augmentSystemPrompt = jest.fn(async () =>
      'BASE\n\n## RELEVANT CONTEXT (auto-recalled)\n\nremembered thing'
    );
    const out = await runAutoRecallStep({
      agent: { id: 'a1', capabilities: ['memory'] },
      conversationHistory: [{ role: 'user', content: 'q' }],
      aiService, logger,
      enhancedSystemPrompt: 'BASE',
    });
    expect(out).toBe('BASE\n\n## RELEVANT CONTEXT (auto-recalled)\n\nremembered thing');
  });
});
