/**
 * agentCommunicationTool tests for find-agent + backfill-agents-index.
 *
 * Pins:
 *   - find-agent with embeddings disabled → clean error
 *   - find-agent with empty index → clean hint to backfill first
 *   - find-agent excludes requester unless includeSelf=true
 *   - topK + hybrid forwarded; indexer error surfaces in result
 *   - backfill walks agentPool.listActiveAgents + calls indexer.backfill
 *   - backfill reports skipped + failed counts honestly
 *   - missing agentPool → clean error
 *
 * Mocks: stub AgentIndexer at the module boundary (the indexer's
 * internals have their own dedicated suite); fake agentPool with the
 * single method this tool uses.
 */

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

const mockIndexerInstance = {
  isEnabled: true,
  indexOne:  jest.fn().mockResolvedValue({ indexed: true }),
  deleteOne: jest.fn().mockResolvedValue({ deleted: true }),
  backfill:  jest.fn().mockResolvedValue({ ok: true, indexed: 3, skipped: 0, failed: 0 }),
  search:    jest.fn().mockResolvedValue({ enabled: true, hits: [] }),
  stats:     jest.fn().mockResolvedValue({ count: 3, enabled: true }),
};
const MockAgentIndexer = jest.fn(() => mockIndexerInstance);
jest.unstable_mockModule('../../services/embeddings/indexers/agentIndexer.js', () => ({
  AgentIndexer: MockAgentIndexer,
  agentToText: () => '',
}));

const { default: AgentCommunicationTool } = await import('../agentCommunicationTool.js');

function makeTool({ embeddingsEnabled = true, agents = [] } = {}) {
  const tool = new AgentCommunicationTool();
  mockIndexerInstance.isEnabled = embeddingsEnabled;
  mockIndexerInstance.search.mockClear();
  mockIndexerInstance.backfill.mockClear();
  mockIndexerInstance.stats.mockClear();
  MockAgentIndexer.mockClear();

  tool.aiService = { getEmbeddingService: () => ({ isEnabled: embeddingsEnabled }) };
  tool._agentIndexer = null;

  const agentPool = {
    listActiveAgents: jest.fn(async () => agents),
  };
  return { tool, agentPool };
}

const sampleAgents = [
  { id: 'agent-dba', name: 'Database Specialist', systemPrompt: 'SQL migrations expert', capabilities: ['terminal'], skills: ['migrations'] },
  { id: 'agent-fe',  name: 'Frontend Dev',        systemPrompt: 'React + CSS',            capabilities: ['filesystem'], skills: ['react'] },
];

describe('agentCommunicationTool — find-agent', () => {
  test('disabled embeddings → clean error, no indexer search', async () => {
    const { tool } = makeTool({ embeddingsEnabled: false });
    const r = await tool.execute(
      { action: 'find-agent', query: 'database expert' },
      { agentId: 'agent-self' },
    );
    expect(r.success).toBe(false);
    expect(r.error).toMatch(/not enabled/i);
    expect(mockIndexerInstance.search).not.toHaveBeenCalled();
  });

  test('empty index → clean hint to run backfill first', async () => {
    const { tool } = makeTool();
    mockIndexerInstance.stats.mockResolvedValue({ count: 0, enabled: true });
    const r = await tool.execute(
      { action: 'find-agent', query: 'database expert' },
      { agentId: 'agent-self' },
    );
    expect(r.success).toBe(false);
    expect(r.error).toMatch(/backfill/);
    expect(mockIndexerInstance.search).not.toHaveBeenCalled();
  });

  test('missing query → clean error', async () => {
    const { tool } = makeTool();
    const r = await tool.execute(
      { action: 'find-agent' },
      { agentId: 'agent-self' },
    );
    expect(r.success).toBe(false);
    expect(r.error).toMatch(/query/i);
  });

  test('happy path → returns hits with name/type/capabilities/skills', async () => {
    const { tool } = makeTool();
    mockIndexerInstance.stats.mockResolvedValue({ count: 3, enabled: true });
    mockIndexerInstance.search.mockResolvedValue({
      enabled: true,
      hits: [
        {
          id: 'agent-dba', score: 0.9,
          agentId: 'agent-dba', name: 'Database Specialist', type: 'data-engineer',
          capabilities: ['terminal'], skills: ['migrations'],
          promptSnippet: 'SQL migrations expert',
          metadata: {},
        },
      ],
    });
    const r = await tool.execute(
      { action: 'find-agent', query: 'database migration', topK: 3 },
      { agentId: 'agent-self' },
    );
    expect(r.success).toBe(true);
    expect(r.action).toBe('find-agent');
    expect(r.mode).toBe('hybrid');
    expect(r.hits[0]).toMatchObject({
      agentId: 'agent-dba',
      name: 'Database Specialist',
    });
    expect(mockIndexerInstance.search).toHaveBeenCalledWith(
      'database migration',
      { topK: 3, hybrid: true, excludeIds: ['agent-self'] },
    );
  });

  test('includeSelf=true keeps requester in results', async () => {
    const { tool } = makeTool();
    mockIndexerInstance.stats.mockResolvedValue({ count: 1, enabled: true });
    mockIndexerInstance.search.mockResolvedValue({ enabled: true, hits: [] });
    await tool.execute(
      { action: 'find-agent', query: 'q', includeSelf: true },
      { agentId: 'agent-self' },
    );
    expect(mockIndexerInstance.search).toHaveBeenCalledWith(
      'q',
      expect.objectContaining({ excludeIds: [] }),
    );
  });

  test('hybrid:false forwarded', async () => {
    const { tool } = makeTool();
    mockIndexerInstance.stats.mockResolvedValue({ count: 1, enabled: true });
    mockIndexerInstance.search.mockResolvedValue({ enabled: true, hits: [] });
    await tool.execute(
      { action: 'find-agent', query: 'q', hybrid: false },
      { agentId: 'agent-self' },
    );
    expect(mockIndexerInstance.search).toHaveBeenCalledWith(
      'q',
      expect.objectContaining({ hybrid: false }),
    );
  });

  test('indexer error surfaces in result', async () => {
    const { tool } = makeTool();
    mockIndexerInstance.stats.mockResolvedValue({ count: 3, enabled: true });
    mockIndexerInstance.search.mockResolvedValue({ enabled: true, hits: [], error: 'rate-limited' });
    const r = await tool.execute(
      { action: 'find-agent', query: 'q' },
      { agentId: 'agent-self' },
    );
    expect(r.success).toBe(false);
    expect(r.error).toBe('rate-limited');
  });
});

describe('agentCommunicationTool — backfill-agents-index', () => {
  test('disabled → clean error, no indexer call', async () => {
    const { tool, agentPool } = makeTool({ embeddingsEnabled: false, agents: sampleAgents });
    const r = await tool.execute(
      { action: 'backfill-agents-index' },
      { agentId: 'agent-self', agentPool },
    );
    expect(r.success).toBe(false);
    expect(r.message).toMatch(/not enabled/i);
    expect(mockIndexerInstance.backfill).not.toHaveBeenCalled();
  });

  test('walks listActiveAgents + calls indexer.backfill', async () => {
    const { tool, agentPool } = makeTool({ agents: sampleAgents });
    const r = await tool.execute(
      { action: 'backfill-agents-index' },
      { agentId: 'agent-self', agentPool },
    );
    expect(agentPool.listActiveAgents).toHaveBeenCalledTimes(1);
    expect(mockIndexerInstance.backfill).toHaveBeenCalledWith(sampleAgents);
    expect(r.success).toBe(true);
    expect(r.indexed).toBe(3);
  });

  test('missing agentPool → clean error', async () => {
    const { tool } = makeTool();
    const r = await tool.execute(
      { action: 'backfill-agents-index' },
      { agentId: 'agent-self' },
    );
    expect(r.success).toBe(false);
    expect(r.error).toMatch(/agentPool/);
  });

  test('reports skipped + failed counts cleanly', async () => {
    const { tool, agentPool } = makeTool({ agents: sampleAgents });
    mockIndexerInstance.backfill.mockResolvedValue({
      ok: true, indexed: 1, skipped: 1, failed: 0,
    });
    const r = await tool.execute(
      { action: 'backfill-agents-index' },
      { agentId: 'agent-self', agentPool },
    );
    expect(r.indexed).toBe(1);
    expect(r.skipped).toBe(1);
    expect(r.message).toMatch(/skipped 1/);
  });

  test('indexer failure surfaces cleanly', async () => {
    const { tool, agentPool } = makeTool({ agents: sampleAgents });
    mockIndexerInstance.backfill.mockResolvedValue({
      ok: false, indexed: 0, skipped: 0, failed: 2, error: 'rate-limited',
    });
    const r = await tool.execute(
      { action: 'backfill-agents-index' },
      { agentId: 'agent-self', agentPool },
    );
    expect(r.success).toBe(false);
    expect(r.failed).toBe(2);
    expect(r.error).toBe('rate-limited');
  });
});
