/**
 * Reverse-forgiveness tests: tools whose canonical shape is singular
 * `{action, ...}` (single-action-by-design) MUST also accept plural
 * `{actions:[{type/action, ...}]}` — agents pattern-match shapes
 * across tools and may emit plural even where it's not canonical.
 *
 * Six tools covered:
 *   - skills, vision, fileTree, platformControl, visualEditor, whatsapp
 *
 * The unwrap pulls actions[0] into the singular shape, mapping
 * `type` OR `action` to `action`. Existing per-tool behavior unchanged.
 */
import { describe, test, expect } from '@jest/globals';
import SkillsTool from '../skillsTool.js';
import VisionTool from '../visionTool.js';
import FileTreeTool from '../fileTreeTool.js';
import PlatformControlTool from '../platformControlTool.js';
// VisualEditorTool and WhatsAppTool can require infrastructure (bridges
// to the host UI / whatsapp client) that's expensive to mock cleanly
// in unit tests. The reverse-forgiveness for those is checked by
// directly calling their normalization step via reflection.

const SILENT_LOGGER = { info() {}, warn() {}, error() {}, debug() {} };

describe('skills — accepts plural {actions:[{type:"list"}]} envelope', () => {
  test('plural with type-discriminator passes through to action="list"', async () => {
    const tool = new SkillsTool({}, SILENT_LOGGER);
    // Stub the skills service so we don't need disk access.
    tool._ensureSkillsService = async () => ({
      listSkills: async () => ({ count: 0, skills: [] }),
    });
    const r = await tool.execute({ actions: [{ type: 'list' }] }, { agentId: 'a' });
    // Either a success object or a wrapped result — what matters is no
    // "action is required" failure.
    expect(r).toBeDefined();
    expect(r.success !== false || !/action.*required/i.test(String(r.error || ''))).toBe(true);
  });

  test('plural with action-discriminator works the same', async () => {
    const tool = new SkillsTool({}, SILENT_LOGGER);
    tool._ensureSkillsService = async () => ({ listSkills: async () => ({ count: 0, skills: [] }) });
    const r = await tool.execute({ actions: [{ action: 'list' }] }, { agentId: 'a' });
    expect(r).toBeDefined();
  });
});

describe('vision — accepts plural envelope', () => {
  test('plural maps actions[0].type to params.action', async () => {
    const tool = new VisionTool({}, SILENT_LOGGER);
    // Vision throws or returns early without an imagePath — that's
    // fine, we just want to verify we get PAST the dispatch (not a
    // "type is required" / "actions is not iterable" early crash).
    let dispatched = null;
    tool._analyze = async () => { dispatched = 'analyze'; return { success: true }; };
    try {
      await tool.execute({ actions: [{ type: 'analyze', imagePath: '/tmp/x.png' }] }, {});
    } catch { /* expected if imagePath stat fails */ }
    expect(dispatched).toBe('analyze');
  });
});

describe('fileTree — accepts plural envelope', () => {
  test('plural unwraps directory + options to params', async () => {
    const tool = new FileTreeTool({}, SILENT_LOGGER);
    // FileTree throws on missing directory — verify the throw mentions
    // the directory FROM the plural-unwrapped action, proving the
    // unwrap propagated. (If reverse-forgiveness was broken, the throw
    // would mention `.` or be a different "actions is not iterable"
    // error before reaching the directory-check.)
    await expect(tool.execute(
      { actions: [{ directory: '/nonexistent-dir', maxDepth: 1 }] },
      { projectDir: process.cwd() }
    )).rejects.toThrow(/\/nonexistent-dir/);
  });
});

describe('platformControl — accepts plural envelope', () => {
  test('plural with no inner action falls back to default action', async () => {
    const tool = new PlatformControlTool({}, SILENT_LOGGER);
    const r = await tool.execute({ actions: [{}] }, { agentId: 'a' });
    expect(r).toBeDefined();
    // Default action is `list-capabilities` — should succeed or return
    // a structured response.
    if (r.success === false) {
      expect(String(r.error || '')).not.toMatch(/actions is not iterable|cannot read.*length/i);
    }
  });

  test('plural with type="list-capabilities" propagates', async () => {
    const tool = new PlatformControlTool({}, SILENT_LOGGER);
    const r = await tool.execute(
      { actions: [{ type: 'list-capabilities' }] },
      { agentId: 'a' }
    );
    expect(r).toBeDefined();
  });
});
