import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { fileReadTool } from './file-read.js';
import { fileWriteTool } from './file-write.js';
import { fileEditTool } from './file-edit.js';
import { fileListTool } from './file-list.js';
import { mkdtempSync, writeFileSync, readFileSync, rmSync, mkdirSync } from 'fs';
import { join } from 'path';
import { tmpdir } from 'os';

let testDir: string;

beforeEach(() => {
  testDir = mkdtempSync(join(tmpdir(), 'flynn-file-test-'));
});

afterEach(() => {
  rmSync(testDir, { recursive: true });
});

describe('file.read', () => {
  it('reads a file', async () => {
    writeFileSync(join(testDir, 'hello.txt'), 'hello world');
    const result = await fileReadTool.execute({ path: join(testDir, 'hello.txt') });
    expect(result.success).toBe(true);
    expect(result.output).toBe('hello world');
  });

  it('reads with offset and limit', async () => {
    writeFileSync(join(testDir, 'lines.txt'), 'line1\nline2\nline3\nline4\n');
    const result = await fileReadTool.execute({ path: join(testDir, 'lines.txt'), offset: 1, limit: 2 });
    expect(result.success).toBe(true);
    expect(result.output).toBe('line2\nline3');
  });

  it('returns error for missing file', async () => {
    const result = await fileReadTool.execute({ path: join(testDir, 'nope.txt') });
    expect(result.success).toBe(false);
    expect(result.error).toBeTruthy();
  });
});

describe('file.write', () => {
  it('writes a new file', async () => {
    const filePath = join(testDir, 'new.txt');
    const result = await fileWriteTool.execute({ path: filePath, content: 'new content' });
    expect(result.success).toBe(true);
    expect(readFileSync(filePath, 'utf-8')).toBe('new content');
  });

  it('creates intermediate directories', async () => {
    const filePath = join(testDir, 'sub', 'dir', 'file.txt');
    const result = await fileWriteTool.execute({ path: filePath, content: 'deep' });
    expect(result.success).toBe(true);
    expect(readFileSync(filePath, 'utf-8')).toBe('deep');
  });
});

describe('file.edit', () => {
  it('replaces a string in a file', async () => {
    const filePath = join(testDir, 'edit.txt');
    writeFileSync(filePath, 'hello world');
    const result = await fileEditTool.execute({
      path: filePath,
      old_string: 'world',
      new_string: 'flynn',
    });
    expect(result.success).toBe(true);
    expect(readFileSync(filePath, 'utf-8')).toBe('hello flynn');
  });

  it('fails if old_string not found', async () => {
    const filePath = join(testDir, 'edit2.txt');
    writeFileSync(filePath, 'hello world');
    const result = await fileEditTool.execute({
      path: filePath,
      old_string: 'xyz',
      new_string: 'abc',
    });
    expect(result.success).toBe(false);
    expect(result.error).toContain('not found');
  });

  it('fails if old_string matches multiple times without replace_all', async () => {
    const filePath = join(testDir, 'edit3.txt');
    writeFileSync(filePath, 'aaa bbb aaa');
    const result = await fileEditTool.execute({
      path: filePath,
      old_string: 'aaa',
      new_string: 'ccc',
    });
    expect(result.success).toBe(false);
    expect(result.error).toContain('multiple');
  });

  it('replaces all when replace_all is true', async () => {
    const filePath = join(testDir, 'edit4.txt');
    writeFileSync(filePath, 'aaa bbb aaa');
    const result = await fileEditTool.execute({
      path: filePath,
      old_string: 'aaa',
      new_string: 'ccc',
      replace_all: true,
    });
    expect(result.success).toBe(true);
    expect(readFileSync(filePath, 'utf-8')).toBe('ccc bbb ccc');
  });
});

describe('file.list', () => {
  it('lists files in a directory', async () => {
    writeFileSync(join(testDir, 'a.txt'), '');
    writeFileSync(join(testDir, 'b.ts'), '');
    mkdirSync(join(testDir, 'sub'));
    writeFileSync(join(testDir, 'sub', 'c.txt'), '');

    const result = await fileListTool.execute({ path: testDir });
    expect(result.success).toBe(true);
    expect(result.output).toContain('a.txt');
    expect(result.output).toContain('b.ts');
    expect(result.output).toContain('sub');
  });

  it('filters with glob pattern', async () => {
    writeFileSync(join(testDir, 'a.txt'), '');
    writeFileSync(join(testDir, 'b.ts'), '');

    const result = await fileListTool.execute({ path: testDir, pattern: '*.ts' });
    expect(result.success).toBe(true);
    expect(result.output).toContain('b.ts');
    expect(result.output).not.toContain('a.txt');
  });
});
