import { describe, it, expect } from 'vitest';
import type { ToolContext } from '../tools/index.js';
import type { DispatchOutcome } from './tool-dispatcher.js';
import {
  buildBrowserWaitResult,
  buildToolApprovalWaitResult,
  buildPackageApprovalWaitResult,
  buildSubtaskWaitResult,
  buildCompleteAutoParkResult,
} from './pending-states.js';

const ctx = (over: Partial<ToolContext> = {}): ToolContext =>
  ({ workspacePath: '/ws', editAllowed: true, ...over } as ToolContext);

describe('buildBrowserWaitResult', () => {
  it('shapes a WAITING_HUMAN_BROWSER result from a waiting_human dispatch', () => {
    const dispatch = { status: 'waiting_human', output: 'login please', waitReason: 'browser_login', sessionId: 'sess-1' } as Extract<DispatchOutcome, { status: 'waiting_human' }>;
    const r = buildBrowserWaitResult(dispatch, ['BrowseWeb'], 'm1');
    expect(r.next).toBe('WAITING_HUMAN_BROWSER');
    expect(r.output).toBe('login please');
    expect(r.waitReason).toBe('browser_login');
    expect(r.browserSessionId).toBe('sess-1');
    expect(r.toolsUsed).toEqual(['BrowseWeb']);
  });
});

describe('buildToolApprovalWaitResult', () => {
  it('parks for approval and clears the flag when set', () => {
    const c = ctx({ pendingToolApproval: { toolName: 'Bash' } });
    const r = buildToolApprovalWaitResult(c, [], 'm1');
    expect(r?.next).toBe('WAITING_HUMAN_TOOL_REQUEST');
    expect(r?.waitReason).toBe('tool_request');
    expect(String(r?.output)).toContain('Bash');
    expect(c.pendingToolApproval).toBeNull(); // flag consumed
  });
  it('returns null when no approval is pending', () => {
    expect(buildToolApprovalWaitResult(ctx(), [], 'm1')).toBeNull();
  });
});

describe('buildPackageApprovalWaitResult', () => {
  it('parks for approval and clears the flag when set', () => {
    const c = ctx({ pendingPackageApproval: { spec: 'requests==2.32.3' } });
    const r = buildPackageApprovalWaitResult(c, [], 'm1');
    expect(r?.next).toBe('WAITING_HUMAN_PACKAGE_REQUEST');
    expect(r?.waitReason).toBe('package_request');
    expect(String(r?.output)).toContain('requests==2.32.3');
    expect(c.pendingPackageApproval).toBeNull(); // flag consumed
  });
  it('returns null when no package approval is pending', () => {
    expect(buildPackageApprovalWaitResult(ctx(), [], 'm1')).toBeNull();
  });
});

describe('buildSubtaskWaitResult', () => {
  it('parks as WAIT_SUBTASKS (resume same movement) and clears the flag', () => {
    const c = ctx({ pendingSubtaskWait: true });
    const r = buildSubtaskWaitResult(c, [], 'step-2');
    expect(r?.next).toBe('WAIT_SUBTASKS');
    expect(r?.resumeMovement).toBe('step-2');
    expect(c.pendingSubtaskWait).toBe(false);
  });
  it('returns null when no subtask wait is pending', () => {
    expect(buildSubtaskWaitResult(ctx(), [], 'm1')).toBeNull();
  });
});

describe('buildCompleteAutoParkResult', () => {
  it('parks a success completion when there are unwaited pending children', async () => {
    const c = ctx({ hasPendingSubtasks: async () => true });
    const r = await buildCompleteAutoParkResult({ status: 'success', result: 'R' }, c, [], 'm1');
    expect(r?.next).toBe('WAIT_SUBTASKS');
    expect(r?.output).toBe('R');
    expect(r?.resumeMovement).toBe('m1');
  });
  it('falls back to a default message when result is absent', async () => {
    const c = ctx({ hasPendingSubtasks: async () => true });
    const r = await buildCompleteAutoParkResult({ status: 'success' }, c, [], 'm1');
    expect(String(r?.output)).toContain('サブタスク');
  });
  it('returns null when no children are pending', async () => {
    const c = ctx({ hasPendingSubtasks: async () => false });
    expect(await buildCompleteAutoParkResult({ status: 'success' }, c, [], 'm1')).toBeNull();
  });
  it('returns null when hasPendingSubtasks is not provided', async () => {
    expect(await buildCompleteAutoParkResult({ status: 'success' }, ctx(), [], 'm1')).toBeNull();
  });
  it('returns null for non-success completions', async () => {
    const c = ctx({ hasPendingSubtasks: async () => true });
    expect(await buildCompleteAutoParkResult({ status: 'aborted' }, c, [], 'm1')).toBeNull();
  });
  it('fails open (no park) when the pending-children check throws', async () => {
    const c = ctx({ hasPendingSubtasks: async () => { throw new Error('db down'); } });
    expect(await buildCompleteAutoParkResult({ status: 'success' }, c, [], 'm1')).toBeNull();
  });
});
