import { createExecuteSkillTool } from '../src/modules/netaclaw/tools/builtin/execute_skill';

describe('execute_skill process event bridge', () => {
  it('bridges skill process events to tool updates', async () => {
    let capturedParams: any;
    const executor = {
      execute: jest.fn(async (params: any) => {
        capturedParams = params;
        params.onProcessEvent?.({
          source: 'skill',
          targetType: 'skill',
          stage: 'frames',
          status: 'running',
          message: 'frames extracted',
          current: 1,
          total: 2,
          payload: { frameCount: 10 },
        });
        return { success: true, output: { ok: true }, duration: 1 };
      }),
    };
    const updates: any[] = [];

    const tool = createExecuteSkillTool(executor as any);
    const result = await tool.execute(
      'call-1',
      { name: 'demo', input: { taskId: 't1' } },
      event => updates.push(event),
    );

    expect(capturedParams).toEqual(expect.objectContaining({
      skillName: 'demo',
      input: { taskId: 't1' },
      toolCallId: 'call-1',
    }));
    expect(updates).toEqual([
      expect.objectContaining({
        source: 'skill',
        targetType: 'skill',
        stage: 'frames',
        status: 'running',
        message: 'frames extracted',
        current: 1,
        total: 2,
        payload: {
          frameCount: 10,
          skillName: 'demo',
        },
      }),
    ]);
    expect(result).toEqual({
      type: 'text',
      text: JSON.stringify({ ok: true }, null, 2),
    });
  });

  it('does not fail execute_skill when process update observer throws', async () => {
    const executor = {
      execute: jest.fn(async (params: any) => {
        params.onProcessEvent?.({
          source: 'skill',
          targetType: 'skill',
          status: 'running',
          message: 'still observational',
        });
        return { success: true, output: { ok: true }, duration: 1 };
      }),
    };

    const tool = createExecuteSkillTool(executor as any);
    const result = await tool.execute(
      'call-1',
      { name: 'demo', input: {} },
      () => {
        throw new Error('observer failed');
      },
    );

    expect(result).toEqual({
      type: 'text',
      text: JSON.stringify({ ok: true }, null, 2),
    });
  });

  it('returns structured images when skill output includes bestFrameImages', async () => {
    const output = {
      success: true,
      taskId: 'task_1',
      summary: { mergedDamageCount: 1, bestFrameCount: 1, candidateDamageCount: 2, reviewImageCount: 1 },
      vehicleInfo: { frontPlate: '粤B12345' },
      candidates: [
        { id: 'cand_001', part: '前保险杠', type: '划痕' },
      ],
      damages: [
        { id: 'damage_001', part: '前保险杠', type: '划痕', severity: '轻微' },
      ],
      uncertainDamages: [
        { id: 'damage_002', part: '右前门', type: '凹陷', severity: '轻微' },
      ],
      bestFrameImages: [
        {
          url: '/workspace/vehicle-damage-inspection/task_1/source/best_01_damage_001_2_00s.jpg',
          mimeType: 'image/jpeg',
          label: '前保险杠划痕 2s',
        },
      ],
      reviewImages: [
        {
          url: '/workspace/vehicle-damage-inspection/task_1/source/review_zoom_damage_001_2_00s.jpg',
          mimeType: 'image/jpeg',
          label: '模型不确定，放大后复核：前保险杠划痕',
          purpose: '模型不确定，放大后复核',
          reason: '放大后仍不够清晰，建议人工复核',
        },
      ],
    };
    const executor = {
      execute: jest.fn(async () => ({ success: true, output, duration: 1 })),
    };

    const tool = createExecuteSkillTool(executor as any);
    const result = await tool.execute('call-1', { name: 'vehicle-damage-inspection', input: {} });

    expect(result).toEqual({
      type: 'images',
      text: [
        'Skill vehicle-damage-inspection 执行完成。',
        '请只依据 finalResult.damages、finalResult.uncertainDamages、finalResult.bestFrameImages、finalResult.reviewImages 和 finalResult.summary 回答用户。',
        'damages 是已确认旧伤；uncertainDamages 是放大后仍无法明确确认的疑似旧伤，需要提示人工复核，不能说成已确认旧伤。',
        'reviewImages 是模型不清楚时生成的放大复核图；如存在，请说明“模型不清楚，已放大后判断”，并把图片返回给前端展示。',
        'candidates 是已复核前的中间候选，不代表最终旧伤，不要把 candidates 当成检测结论。',
        JSON.stringify({
          success: true,
          taskId: 'task_1',
          finalResult: {
            summary: { mergedDamageCount: 1, bestFrameCount: 1, candidateDamageCount: 2, reviewImageCount: 1 },
            vehicleInfo: { frontPlate: '粤B12345' },
            damages: [
              { id: 'damage_001', part: '前保险杠', type: '划痕', severity: '轻微' },
            ],
            uncertainDamages: [
              { id: 'damage_002', part: '右前门', type: '凹陷', severity: '轻微' },
            ],
            bestFrameImages: output.bestFrameImages,
            reviewImages: output.reviewImages,
          },
          artifacts: undefined,
        }, null, 2),
      ].join('\n'),
      images: [
        {
          url: '/workspace/vehicle-damage-inspection/task_1/source/best_01_damage_001_2_00s.jpg',
          mimeType: 'image/jpeg',
          label: '前保险杠划痕 2s',
        },
        {
          url: '/workspace/vehicle-damage-inspection/task_1/source/review_zoom_damage_001_2_00s.jpg',
          mimeType: 'image/jpeg',
          label: '模型不确定，放大后复核：前保险杠划痕',
          purpose: '模型不确定，放大后复核',
          reason: '放大后仍不够清晰，建议人工复核',
        },
      ],
    });
  });

  it('returns final-only text when reviewed skill output has rejected all candidates', async () => {
    const output = {
      success: true,
      taskId: 'task_empty',
      summary: { candidateDamageCount: 1, mergedDamageCount: 0, bestFrameCount: 0, reviewImageCount: 0 },
      candidates: [
        { id: 'cand_001', part: '右前翼子板', type: '凹陷' },
      ],
      damages: [],
      uncertainDamages: [],
      bestFrameImages: [],
      reviewImages: [],
    };
    const executor = {
      execute: jest.fn(async () => ({ success: true, output, duration: 1 })),
    };

    const tool = createExecuteSkillTool(executor as any);
    const result = await tool.execute('call-1', { name: 'vehicle-damage-inspection', input: {} });

    expect(result).toEqual({
      type: 'text',
      text: [
        'Skill vehicle-damage-inspection 执行完成。',
        '请只依据 finalResult.damages、finalResult.uncertainDamages、finalResult.bestFrameImages、finalResult.reviewImages 和 finalResult.summary 回答用户。',
        'damages 是已确认旧伤；uncertainDamages 是放大后仍无法明确确认的疑似旧伤，需要提示人工复核，不能说成已确认旧伤。',
        'reviewImages 是模型不清楚时生成的放大复核图；如存在，请说明“模型不清楚，已放大后判断”，并把图片返回给前端展示。',
        'candidates 是已复核前的中间候选，不代表最终旧伤，不要把 candidates 当成检测结论。',
        JSON.stringify({
          success: true,
          taskId: 'task_empty',
          finalResult: {
            summary: { candidateDamageCount: 1, mergedDamageCount: 0, bestFrameCount: 0, reviewImageCount: 0 },
            vehicleInfo: undefined,
            damages: [],
            uncertainDamages: [],
            bestFrameImages: [],
            reviewImages: [],
          },
          artifacts: undefined,
        }, null, 2),
      ].join('\n'),
    });
    expect((result as any).text).not.toContain('右前翼子板');
  });
});
