import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { createReadRawFileTool } from '../../../../src/context/ingest/tools/read-raw-file.tool.js';

describe('read_raw_file tool', () => {
  let stagedDir: string;

  beforeEach(async () => {
    stagedDir = await mkdtemp(join(tmpdir(), 'readraw-'));
    await mkdir(join(stagedDir, 'views'), { recursive: true });
    await writeFile(join(stagedDir, 'views', 'a.yml'), 'line1\nline2\nline3\n', 'utf-8');
    await writeFile(join(stagedDir, 'peer.yml'), 'secret', 'utf-8');
  });

  afterEach(async () => rm(stagedDir, { recursive: true, force: true }));

  it('returns content for an allowed path', async () => {
    const tool = createReadRawFileTool({ stagedDir, allowedPaths: new Set(['views/a.yml']) });
    const result = await (tool.execute as (...args: unknown[]) => unknown)(
      { path: 'views/a.yml' },
      { toolCallId: 't1', messages: [] },
    );
    expect(result).toContain('line1');
    expect(result).toContain('line2');
  });

  it('refuses to return oversized files and directs callers to read spans', async () => {
    await writeFile(join(stagedDir, 'views', 'huge.yml'), `${'x'.repeat(160_000)}\n`, 'utf-8');
    const tool = createReadRawFileTool({ stagedDir, allowedPaths: new Set(['views/huge.yml']) });
    const result = await (tool.execute as (...args: unknown[]) => unknown)(
      { path: 'views/huge.yml' },
      { toolCallId: 't1', messages: [] },
    );

    expect(result).toMatch(/too large/i);
    expect(result).toMatch(/read_raw_span/i);
    expect(String(result).length).toBeLessThan(1000);
  });

  it('rejects a path not in the allow-list', async () => {
    const tool = createReadRawFileTool({ stagedDir, allowedPaths: new Set(['views/a.yml']) });
    const result = await (tool.execute as (...args: unknown[]) => unknown)(
      { path: 'peer.yml' },
      { toolCallId: 't1', messages: [] },
    );
    expect(result).toMatch(/not accessible/i);
    expect(result).not.toContain('secret');
  });

  it('rejects directory traversal attempts', async () => {
    const tool = createReadRawFileTool({ stagedDir, allowedPaths: new Set(['views/a.yml']) });
    const result = await (tool.execute as (...args: unknown[]) => unknown)(
      { path: '../outside.yml' },
      { toolCallId: 't1', messages: [] },
    );
    expect(result).toMatch(/not accessible/i);
  });

  it('returns a clear error when the file is missing despite being allowed', async () => {
    const tool = createReadRawFileTool({ stagedDir, allowedPaths: new Set(['views/missing.yml']) });
    const result = await (tool.execute as (...args: unknown[]) => unknown)(
      { path: 'views/missing.yml' },
      { toolCallId: 't1', messages: [] },
    );
    expect(result).toMatch(/not found/i);
  });
});
