import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import sharp = require('sharp');
import { listDirTool, readFileTool, writeFileTool } from '../src/modules/netaclaw/tools/builtin/file.js';

describe('file tools', () => {
  const tempDirs: string[] = [];

  afterEach(() => {
    tempDirs.splice(0).forEach(dir => fs.rmSync(dir, { recursive: true, force: true }));
  });

  const createTempDir = () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), 'neta-file-tool-'));
    tempDirs.push(root);
    return root;
  };

  it('reads a whole small file by default', async () => {
    const root = createTempDir();
    const filePath = path.join(root, 'note.txt');
    fs.writeFileSync(filePath, 'alpha\nbeta', 'utf8');

    const result = await readFileTool.execute('read-1', { path: filePath });

    expect((result as any).text).toBe('alpha\nbeta');
  });

  it('reads paths with Pi-compatible Unicode space normalization', async () => {
    const root = createTempDir();
    fs.writeFileSync(path.join(root, 'my file.txt'), 'normalized', 'utf8');

    const result = await readFileTool.execute('read-1', {
      path: 'my\u00A0file.txt',
      _netaRuntime: { sessionCwd: root },
    } as any);

    expect((result as any).text).toBe('normalized');
  });

  it('returns structured image results for image files', async () => {
    const root = createTempDir();
    const filePath = path.join(root, 'pixel.png');
    fs.writeFileSync(
      filePath,
      Buffer.from(
        'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Wn0lVQAAAAASUVORK5CYII=',
        'base64'
      )
    );

    const result = await readFileTool.execute('read-image', { path: filePath });

    expect(result).toEqual(expect.objectContaining({
      type: 'image',
      mimeType: 'image/png',
      width: 1,
      height: 1,
    }));
    expect((result as any).url).toContain('data:image/png;base64,');
    expect((result as any).text).toContain('Dimensions: 1x1');
    expect((result as any).text).toContain('Size:');
  });

  it('resizes oversized image files before returning structured image data', async () => {
    const root = createTempDir();
    const filePath = path.join(root, 'large.png');
    const largeImage = await sharp({
      create: {
        width: 2400,
        height: 1200,
        channels: 3,
        background: { r: 12, g: 34, b: 56 },
      },
    }).png().toBuffer();
    fs.writeFileSync(filePath, largeImage);

    const result = await readFileTool.execute('read-large-image', { path: filePath });

    expect(result).toEqual(expect.objectContaining({
      type: 'image',
      resized: true,
      originalWidth: 2400,
      originalHeight: 1200,
    }));
    expect((result as any).width).toBeLessThanOrEqual(2000);
    expect((result as any).height).toBeLessThanOrEqual(2000);
    expect((result as any).url).toMatch(/^data:image\/(png|jpeg);base64,/);
    expect((result as any).text).toContain('original 2400x1200');
  });

  it('reads a line window with offset and limit', async () => {
    const root = createTempDir();
    const filePath = path.join(root, 'note.txt');
    fs.writeFileSync(filePath, 'line 1\nline 2\nline 3\nline 4', 'utf8');

    const result = await readFileTool.execute('read-2', {
      path: filePath,
      offset: 2,
      limit: 2,
    });

    expect((result as any).text).toContain('line 2\nline 3');
    expect((result as any).text).toContain('offset=4');
  });

  it('truncates large reads with a continuation hint', async () => {
    const root = createTempDir();
    const filePath = path.join(root, 'large.txt');
    const content = Array.from({ length: 2001 }, (_, index) => `line ${index + 1}`).join('\n');
    fs.writeFileSync(filePath, content, 'utf8');

    const result = await readFileTool.execute('read-3', { path: filePath });
    const text = (result as any).text as string;

    expect(text).toContain('line 1');
    expect(text).toContain('line 2000');
    expect(text).not.toContain('line 2001');
    expect(text).toContain('已达到 2000 行限制');
    expect(text).toContain('offset=2001');
  });

  it('serializes write_file mutations through the file mutation queue', async () => {
    const root = createTempDir();
    const filePath = path.join(root, 'nested', 'note.txt');

    await writeFileTool.execute('write-1', { path: filePath, content: 'created' });

    expect(fs.readFileSync(filePath, 'utf8')).toBe('created');
  });

  it('lists runtime session cwd when path is omitted', async () => {
    const root = createTempDir();
    fs.writeFileSync(path.join(root, 'note.txt'), 'created', 'utf8');

    const result = await listDirTool.execute('list-1', {
      _netaRuntime: { sessionCwd: root },
    } as any);

    expect((result as any).text).toContain('note.txt');
  });
});
