import { describe, it, expect, beforeEach, vi } from 'vitest';

// Mock fs/promises
vi.mock('fs/promises', () => ({
  stat: vi.fn(),
  copyFile: vi.fn().mockResolvedValue(undefined),
  mkdir: vi.fn().mockResolvedValue(undefined),
  readdir: vi.fn().mockResolvedValue([]),
}));

// 可变状态
let mockCheckResults: Array<{
  allowed: boolean;
  action?: string;
  reason?: string;
  needsConfirmation?: boolean;
}> = [];
let checkCallIndex = 0;

// Mock permission manager
vi.mock('../../../../src/permission/index.js', () => ({
  getPermissionManager: () => ({
    checkFilePermission: vi.fn(async () => {
      const result = mockCheckResults[checkCallIndex] || { allowed: true };
      checkCallIndex++;
      return result;
    }),
  }),
}));

// Mock loadDescription
vi.mock('../../../../src/tools/load_description.js', () => ({
  loadDescription: vi.fn(() => '复制文件'),
}));

import { copyFileTool } from '../../../../src/tools/filesystem/copy_file.js';
import * as fs from 'fs/promises';

describe('copyFileTool - 扩展测试', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockCheckResults = [{ allowed: true }, { allowed: true }];
    checkCallIndex = 0;
    // 重置 mock 默认值
    vi.mocked(fs.stat).mockReset();
    vi.mocked(fs.copyFile).mockReset().mockResolvedValue(undefined);
    vi.mocked(fs.mkdir).mockReset().mockResolvedValue(undefined);
    vi.mocked(fs.readdir).mockReset().mockResolvedValue([]);
  });

  describe('递归复制目录', () => {
    it('递归复制包含文件的目录', async () => {
      vi.mocked(fs.stat)
        .mockResolvedValueOnce({ isDirectory: () => true } as any)
        .mockRejectedValueOnce(new Error('ENOENT'))
        .mockResolvedValueOnce({ isDirectory: () => false } as any);

      vi.mocked(fs.readdir).mockResolvedValueOnce(['file1.txt'] as any);

      const result = await copyFileTool.execute({
        source: 'src_dir',
        destination: 'dest_dir',
      });

      expect(result.success).toBe(true);
      expect(fs.mkdir).toHaveBeenCalled();
      expect(fs.copyFile).toHaveBeenCalled();
    });

    it('递归复制包含子目录的目录', async () => {
      // 调用顺序：
      // 1. execute: stat(source) - 检查源是否存在
      // 2. execute: stat(dest) - 检查目标（ENOENT）
      // 3. copyRecursive: stat(source) - 判断是否是目录
      // 4. copyRecursive: stat(source/subdir) - 递归判断子目录
      vi.mocked(fs.stat)
        .mockResolvedValueOnce({ isDirectory: () => true } as any) // source 存在且是目录
        .mockRejectedValueOnce(new Error('ENOENT')) // dest 不存在
        .mockResolvedValueOnce({ isDirectory: () => true } as any) // copyRecursive(source)
        .mockResolvedValueOnce({ isDirectory: () => true } as any); // copyRecursive(source/subdir)

      vi.mocked(fs.readdir)
        .mockResolvedValueOnce(['subdir'] as any) // 第一层目录
        .mockResolvedValueOnce([] as any); // 子目录为空

      const result = await copyFileTool.execute({
        source: 'src_dir',
        destination: 'dest_dir',
      });

      expect(result.success).toBe(true);
      expect(fs.mkdir).toHaveBeenCalled();
    });
  });

  describe('目标位置权限', () => {
    it('目标位置需要确认时返回错误', async () => {
      mockCheckResults = [
        { allowed: true },
        { allowed: false, action: 'ask', needsConfirmation: true, reason: '首次复制到此位置' },
      ];

      vi.mocked(fs.stat).mockResolvedValueOnce({ isDirectory: () => false } as any);

      const result = await copyFileTool.execute({
        source: 'src.txt',
        destination: '/new/location/dest.txt',
      });

      expect(result.success).toBe(false);
      expect(result.error).toContain('需要用户确认');
      expect(result.error).toContain('首次复制到此位置');
    });
  });

  describe('绝对路径处理', () => {
    it('源和目标都是绝对路径', async () => {
      vi.mocked(fs.stat)
        .mockResolvedValueOnce({ isDirectory: () => false } as any)
        .mockRejectedValueOnce(new Error('ENOENT'));

      const result = await copyFileTool.execute({
        source: '/absolute/source/file.txt',
        destination: '/absolute/destination/file.txt',
      });

      expect(result.success).toBe(true);
      expect(result.output).toContain('/absolute/source/file.txt');
      expect(result.output).toContain('/absolute/destination/file.txt');
    });
  });

  describe('权限检查细节', () => {
    it('源文件权限被拒绝（无原因）', async () => {
      mockCheckResults = [
        { allowed: false, action: 'deny' },
      ];

      const result = await copyFileTool.execute({
        source: 'src.txt',
        destination: 'dest.txt',
      });

      expect(result.success).toBe(false);
      expect(result.error).toContain('不允许读取此文件');
    });

    it('源文件需要确认（无原因）', async () => {
      mockCheckResults = [
        { allowed: false, action: 'ask', needsConfirmation: true },
      ];

      const result = await copyFileTool.execute({
        source: 'src.txt',
        destination: 'dest.txt',
      });

      expect(result.success).toBe(false);
      expect(result.error).toContain('需要权限确认');
    });

    it('目标权限被拒绝（无原因）', async () => {
      mockCheckResults = [
        { allowed: true },
        { allowed: false, action: 'deny' },
      ];

      const result = await copyFileTool.execute({
        source: 'src.txt',
        destination: 'dest.txt',
      });

      expect(result.success).toBe(false);
      expect(result.error).toContain('不允许复制到此位置');
    });

    it('目标需要确认（无原因）', async () => {
      mockCheckResults = [
        { allowed: true },
        { allowed: false, action: 'ask', needsConfirmation: true },
      ];

      const result = await copyFileTool.execute({
        source: 'src.txt',
        destination: 'dest.txt',
      });

      expect(result.success).toBe(false);
      expect(result.error).toContain('需要权限确认');
    });
  });
});
