/**
 * Tests for agent turn CRUD query helpers.
 *
 * Each test initializes an in-memory PGlite instance, creates the schema,
 * exercises the query function, and tears down the database.
 */

import { describe, it, expect, beforeAll, beforeEach, afterAll } from 'bun:test';
import { setupTestDb, cleanTestDb, teardownTestDb } from '../../helpers/db';
import { registerAgent } from '@myco/db/queries/agents.js';
import { insertRun } from '@myco/db/queries/runs.js';
import { insertTurn, listTurns, updateTurn } from '@myco/db/queries/turns.js';
import type { TurnInsert } from '@myco/db/queries/turns.js';
import { ALL_PROJECTS_SCOPE } from '@myco/grove/ids.js';

/** Epoch seconds helper. */
const epochNow = () => Math.floor(Date.now() / 1000);

/** Shared agent and run IDs used across tests. */
const TEST_AGENT_ID = 'agent-turns-test';
const TEST_RUN_ID = 'run-turns-test';

/** Factory for minimal valid turn data. */
function makeTurn(overrides: Partial<TurnInsert> = {}): TurnInsert {
  return {
    run_id: TEST_RUN_ID,
    agent_id: TEST_AGENT_ID,
    turn_number: 1,
    tool_name: 'vault_search',
    ...overrides,
  };
}

describe('turn query helpers', () => {
  beforeAll(() => { setupTestDb(); });
  afterAll(() => { teardownTestDb(); });
  beforeEach(async () => {
    cleanTestDb();
    // Insert FK targets
    registerAgent({
      id: TEST_AGENT_ID,
      name: 'Test Agent',
      created_at: epochNow(),
    });
    insertRun({
      id: TEST_RUN_ID,
      agent_id: TEST_AGENT_ID,
      started_at: epochNow(),
    });
  });

  // ---------------------------------------------------------------------------
  // insertTurn
  // ---------------------------------------------------------------------------

  describe('insertTurn', () => {
    it('inserts a turn and returns it with auto-generated id', async () => {
      const now = epochNow();
      const data = makeTurn({
        tool_input: '{"query":"recent sessions"}',
        tool_output_summary: 'Found 5 sessions',
        started_at: now - 2,
        completed_at: now,
      });
      const row = insertTurn(data);

      expect(typeof row.id).toBe('number');
      expect(row.run_id).toBe(TEST_RUN_ID);
      expect(row.agent_id).toBe(TEST_AGENT_ID);
      expect(row.turn_number).toBe(1);
      expect(row.tool_name).toBe('vault_search');
      expect(row.tool_input).toBe('{"query":"recent sessions"}');
      expect(row.tool_output_summary).toBe('Found 5 sessions');
      expect(row.started_at).toBe(now - 2);
      expect(row.completed_at).toBe(now);
    });

    it('inserts a turn with minimal fields', async () => {
      const data = makeTurn();
      const row = insertTurn(data);

      expect(row.tool_input).toBeNull();
      expect(row.tool_output_summary).toBeNull();
      expect(row.started_at).toBeNull();
      expect(row.completed_at).toBeNull();
    });
  });

  // ---------------------------------------------------------------------------
  // listTurns
  // ---------------------------------------------------------------------------

  describe('listTurns', () => {
    it('returns turns for a run ordered by turn_number ASC', async () => {
      insertTurn(makeTurn({ turn_number: 3, tool_name: 'vault_report' }));
      insertTurn(makeTurn({ turn_number: 1, tool_name: 'vault_search' }));
      insertTurn(makeTurn({ turn_number: 2, tool_name: 'vault_read' }));

      const rows = listTurns(TEST_RUN_ID, { scope: ALL_PROJECTS_SCOPE });
      expect(rows).toHaveLength(3);
      expect(rows[0].turn_number).toBe(1);
      expect(rows[0].tool_name).toBe('vault_search');
      expect(rows[1].turn_number).toBe(2);
      expect(rows[1].tool_name).toBe('vault_read');
      expect(rows[2].turn_number).toBe(3);
      expect(rows[2].tool_name).toBe('vault_report');
    });

    it('returns empty array for run with no turns', async () => {
      const rows = listTurns('no-such-run', { scope: ALL_PROJECTS_SCOPE });
      expect(rows).toEqual([]);
    });
  });

  // ---------------------------------------------------------------------------
  // updateTurn
  // ---------------------------------------------------------------------------

  describe('updateTurn', () => {
    it('updates tool_output_summary and completed_at after insert', async () => {
      const row = insertTurn(makeTurn({
        tool_input: '{"query":"spores"}',
        started_at: epochNow() - 5,
      }));
      const completedAt = epochNow();

      const updated = updateTurn(row.id, {
        tool_output_summary: 'Found 3 spores',
        completed_at: completedAt,
      });

      expect(updated).not.toBeNull();
      expect(updated!.tool_output_summary).toBe('Found 3 spores');
      expect(updated!.completed_at).toBe(completedAt);
    });

    it('returns null when the turn does not exist', async () => {
      const updated = updateTurn(999999, {
        tool_output_summary: 'Nope',
        completed_at: epochNow(),
      });

      expect(updated).toBeNull();
    });
  });
});
