/**
 * Tests for updateTask's status-transition handler.
 *
 * Covers the in_progress entry handler that:
 *   - Auto-loads `[memory:mem-...]` referenced memory into result.attachedMemory
 *   - Emits result.memoryNudge when no memory is attached
 *   - Surfaces a not-found error when the referenced memory was deleted
 *   - Does nothing on non-transitioning updates (already in_progress)
 *   - Does nothing for transitions to other statuses (e.g. blocked)
 */

import { jest, describe, test, expect, beforeEach } from '@jest/globals';
import { createMockLogger } from '../../__test-utils__/mockFactories.js';

const mockLoadMemories = jest.fn();
const mockInitialize = jest.fn().mockResolvedValue(undefined);
const mockAddMemory = jest.fn();
jest.unstable_mockModule('../../services/memoryService.js', () => ({
  getMemoryService: jest.fn(() => ({
    initialize: mockInitialize,
    addMemory: mockAddMemory,
    loadMemories: mockLoadMemories,
  })),
}));

let uuidCounter = 0;
jest.unstable_mockModule('uuid', () => ({
  v4: jest.fn(() => `mock-uuid-${++uuidCounter}`),
}));

const { default: TaskManagerTool } = await import('../taskManagerTool.js');

function makeTool() {
  const tool = new TaskManagerTool({ description: 'test' });
  tool.logger = createMockLogger();
  return tool;
}

function makeContext({ taskList } = {}) {
  const agent = {
    id: 'agent-1',
    name: 'Trans Test',
    capabilities: ['memory', 'tools'],
    taskList: taskList || { tasks: [], lastUpdated: new Date().toISOString() },
  };
  return {
    agent,
    context: {
      agentId: 'agent-1',
      agentName: 'Trans Test',
      agent,
      agentPool: {
        getAgent: jest.fn().mockResolvedValue(agent),
        persistAgentState: jest.fn().mockResolvedValue(undefined),
      },
      projectDir: '/tmp/test',
    },
  };
}

function makeTask(overrides = {}) {
  return {
    id: 'task-1',
    title: 'Demo task',
    description: 'No reference here',
    status: 'pending',
    priority: 'medium',
    createdAt: new Date().toISOString(),
    updatedAt: new Date().toISOString(),
    ...overrides,
  };
}

beforeEach(() => {
  uuidCounter = 0;
  mockLoadMemories.mockReset();
  mockInitialize.mockReset();
  mockInitialize.mockResolvedValue(undefined);
  mockAddMemory.mockReset();
});

describe('TaskManagerTool — updateTask in_progress transition handler', () => {
  test('auto-loads attached memory when task description has [memory:mem-...] marker', async () => {
    mockLoadMemories.mockResolvedValueOnce([
      { id: 'mem-abc', title: 'Task plan', content: 'step 1: ...' },
    ]);
    const tool = makeTool();
    const { agent, context } = makeContext();
    agent.taskList.tasks = [makeTask({ description: '[memory:mem-abc] short summary' })];

    const res = await tool.execute(
      { action: 'update', taskId: 'task-1', status: 'in_progress' },
      context
    );

    expect(res.success).toBe(true);
    expect(res.result.attachedMemory).toBeDefined();
    expect(res.result.attachedMemory.id).toBe('mem-abc');
    expect(res.result.attachedMemory.content).toBe('step 1: ...');
    expect(res.result.attachedMemory._hint).toMatch(/memory append/);
    expect(res.result.memoryNudge).toBeUndefined();
  });

  test('LOXIA_MEMORY_NUDGE_VARIANT=descriptive falls back to the older wording (for A/B validation)', async () => {
    const prev = process.env.LOXIA_MEMORY_NUDGE_VARIANT;
    process.env.LOXIA_MEMORY_NUDGE_VARIANT = 'descriptive';
    try {
      const tool = makeTool();
      const { agent, context } = makeContext();
      agent.taskList.tasks = [makeTask({ description: 'Plain task with no marker' })];

      const res = await tool.execute(
        { action: 'update', taskId: 'task-1', status: 'in_progress' },
        context
      );

      expect(res.success).toBe(true);
      // Descriptive wording, NOT the imperative "VERY NEXT" phrasing.
      expect(res.result.memoryNudge).toMatch(/Scan the.*AGENT MEMORY/);
      expect(res.result.memoryNudge).not.toMatch(/VERY NEXT/);
    } finally {
      if (prev === undefined) delete process.env.LOXIA_MEMORY_NUDGE_VARIANT;
      else process.env.LOXIA_MEMORY_NUDGE_VARIANT = prev;
    }
  });

  test('LOXIA_MEMORY_NUDGE_VARIANT unset OR =directive emits the directive wording', async () => {
    const prev = process.env.LOXIA_MEMORY_NUDGE_VARIANT;
    delete process.env.LOXIA_MEMORY_NUDGE_VARIANT;
    try {
      const tool = makeTool();
      const { agent, context } = makeContext();
      agent.taskList.tasks = [makeTask({ description: 'Plain task with no marker' })];

      const res = await tool.execute(
        { action: 'update', taskId: 'task-1', status: 'in_progress' },
        context
      );

      expect(res.result.memoryNudge).toMatch(/VERY NEXT tool call/);
      expect(res.result.memoryNudge).toMatch(/memory list/);
    } finally {
      if (prev === undefined) delete process.env.LOXIA_MEMORY_NUDGE_VARIANT;
      else process.env.LOXIA_MEMORY_NUDGE_VARIANT = prev;
    }
  });

  test('emits memoryNudge when transitioning to in_progress without any [memory:] marker', async () => {
    const tool = makeTool();
    const { agent, context } = makeContext();
    agent.taskList.tasks = [makeTask({ description: 'Plain task with no marker' })];

    const res = await tool.execute(
      { action: 'update', taskId: 'task-1', status: 'in_progress' },
      context
    );

    expect(res.success).toBe(true);
    expect(res.result.attachedMemory).toBeUndefined();
    // Directive wording (post-optimization, 2026-05-19): the
    // descriptive earlier nudge ("Scan the catalog...") scored 22% in
    // the live multi-turn memory bench. Switching to "Your VERY NEXT
    // tool call MUST be `memory list`" doubled it to 44%. See
    // scripts/memory-bench/.
    expect(res.result.memoryNudge).toMatch(/memory list/);
    expect(res.result.memoryNudge).toMatch(/VERY NEXT tool call/);
    expect(res.result.memoryNudge).toMatch(/memory add/);
    expect(mockLoadMemories).not.toHaveBeenCalled();
  });

  test('surfaces error when [memory:] marker points at a deleted memory', async () => {
    mockLoadMemories.mockResolvedValueOnce([]); // memory not found
    const tool = makeTool();
    const { agent, context } = makeContext();
    agent.taskList.tasks = [makeTask({ description: '[memory:mem-ghost] gone' })];

    const res = await tool.execute(
      { action: 'update', taskId: 'task-1', status: 'in_progress' },
      context
    );

    expect(res.success).toBe(true);
    expect(res.result.attachedMemory).toBeDefined();
    expect(res.result.attachedMemory.id).toBe('mem-ghost');
    expect(res.result.attachedMemory.error).toMatch(/not found/i);
    expect(res.result.attachedMemory.content).toBeUndefined();
  });

  test('does NOT trigger handler when task was already in_progress (no transition)', async () => {
    const tool = makeTool();
    const { agent, context } = makeContext();
    agent.taskList.tasks = [makeTask({ status: 'in_progress', description: '[memory:mem-xyz] x' })];

    const res = await tool.execute(
      { action: 'update', taskId: 'task-1', status: 'in_progress' },
      context
    );

    expect(res.success).toBe(true);
    expect(res.result.attachedMemory).toBeUndefined();
    expect(res.result.memoryNudge).toBeUndefined();
    expect(mockLoadMemories).not.toHaveBeenCalled();
  });

  test('does NOT trigger handler for transitions to non-in_progress statuses', async () => {
    const tool = makeTool();
    const { agent, context } = makeContext();
    agent.taskList.tasks = [makeTask({ status: 'pending', description: '[memory:mem-xyz] x' })];

    const res = await tool.execute(
      { action: 'update', taskId: 'task-1', status: 'blocked' },
      context
    );

    expect(res.success).toBe(true);
    expect(res.result.attachedMemory).toBeUndefined();
    expect(res.result.memoryNudge).toBeUndefined();
    expect(mockLoadMemories).not.toHaveBeenCalled();
  });

  test('handler errors are swallowed (does not break update)', async () => {
    mockLoadMemories.mockRejectedValueOnce(new Error('disk on fire'));
    const tool = makeTool();
    const { agent, context } = makeContext();
    agent.taskList.tasks = [makeTask({ description: '[memory:mem-abc] x' })];

    const res = await tool.execute(
      { action: 'update', taskId: 'task-1', status: 'in_progress' },
      context
    );

    expect(res.success).toBe(true);
    expect(res.result.attachedMemory).toBeUndefined();
    expect(res.result.memoryNudge).toBeUndefined();
  });
});
