import { describe, it, expect, beforeEach, vi } from 'vitest';
import { BashPermissionChecker } from '../../../src/permission/checkers/bash.js';
import type { PermissionDecision, PermissionContext } from '../../../src/permission/types.js';

// Mock fs 和 path 以避免实际文件操作
vi.mock('fs', () => ({
  existsSync: vi.fn(() => false),
  readFileSync: vi.fn(),
  writeFileSync: vi.fn(),
  mkdirSync: vi.fn(),
}));

describe('BashPermissionChecker - Bash 权限检查器', () => {
  let checker: BashPermissionChecker;
  const testProjectRoot = '/test/project';

  beforeEach(() => {
    checker = new BashPermissionChecker(testProjectRoot);
    vi.clearAllMocks();
  });

  describe('默认配置', () => {
    it('加载默认配置', () => {
      const config = checker.getConfig();

      expect(config.rules).toBeDefined();
      expect(config.rules.length).toBeGreaterThan(0);
      expect(config.default).toBe('ask');
      expect(config.externalDirectory).toBe('ask');
    });

    it('默认规则包含安全命令', () => {
      const config = checker.getConfig();

      // 检查一些默认允许的规则
      const allowRules = config.rules.filter(r => r.action === 'allow');
      const patterns = allowRules.map(r => r.pattern);

      expect(patterns.some(p => p.startsWith('ls'))).toBe(true);
      expect(patterns.some(p => p.startsWith('cat'))).toBe(true);
      expect(patterns.some(p => p.startsWith('git status'))).toBe(true);
    });

    it('默认规则包含危险命令', () => {
      const config = checker.getConfig();

      const denyRules = config.rules.filter(r => r.action === 'deny');
      const patterns = denyRules.map(r => r.pattern);

      expect(patterns.some(p => p.includes('rm -rf'))).toBe(true);
      expect(patterns.some(p => p.includes('sudo'))).toBe(true);
    });
  });

  describe('安全命令检查（默认允许）', () => {
    const safeCommands = [
      'ls -la',
      'cat file.txt',
      'head -n 10 log.txt',
      'tail -f server.log',
      'grep pattern file.txt',
      'find . -name "*.js"',
      'echo hello',
      'pwd',
      'which node',
      'git status',
      'git log --oneline',
      'git diff HEAD',
    ];

    for (const command of safeCommands) {
      it(`允许安全命令: ${command}`, async () => {
        const result = await checker.check({
          command,
          workdir: testProjectRoot,
        });

        expect(result.allowed).toBe(true);
        expect(result.action).toBe('allow');
      });
    }
  });

  describe('危险命令检查（默认拒绝）', () => {
    // 这些命令精确匹配默认拒绝规则
    const denyCommands = [
      'sudo rm file',
    ];

    for (const command of denyCommands) {
      it(`拒绝危险命令: ${command}`, async () => {
        const result = await checker.check({
          command,
          workdir: testProjectRoot,
        });

        expect(result.allowed).toBe(false);
        expect(result.action).toBe('deny');
      });
    }

    // 这些命令可能匹配 ask 规则或默认行为
    const askCommands = [
      'rm -rf /',
      'rm -rf /*',
      'chmod 777 /',
    ];

    for (const command of askCommands) {
      it(`危险命令需要确认或拒绝: ${command}`, async () => {
        const result = await checker.check({
          command,
          workdir: testProjectRoot,
        });

        // 这些命令应该不允许直接执行
        expect(result.allowed).toBe(false);
        // 可能是 deny 或 ask（取决于具体规则匹配）
        expect(['deny', 'ask']).toContain(result.action);
      });
    }
  });

  describe('需要确认的命令', () => {
    const askCommands = [
      'git push origin main',
      'git commit -m "test"',
      'git checkout feature',
      'npm install lodash',
    ];

    for (const command of askCommands) {
      it(`需要确认: ${command}`, async () => {
        // 不设置回调，应该返回 ask
        const result = await checker.check({
          command,
          workdir: testProjectRoot,
        });

        expect(result.action).toBe('ask');
        expect(result.needsConfirmation).toBe(true);
      });
    }
  });

  describe('回调处理', () => {
    it('用户允许时返回允许', async () => {
      const mockCallback = vi.fn().mockResolvedValue({
        allow: true,
        remember: false,
      } as PermissionDecision);

      checker.setAskCallback(mockCallback);

      const result = await checker.check({
        command: 'git push origin main',
        workdir: testProjectRoot,
      });

      expect(result.allowed).toBe(true);
      expect(mockCallback).toHaveBeenCalled();
    });

    it('用户拒绝时返回拒绝', async () => {
      const mockCallback = vi.fn().mockResolvedValue({
        allow: false,
        remember: false,
      } as PermissionDecision);

      checker.setAskCallback(mockCallback);

      const result = await checker.check({
        command: 'git push origin main',
        workdir: testProjectRoot,
      });

      expect(result.allowed).toBe(false);
      expect(result.action).toBe('deny');
    });

    it('remember=true 时记住决定', async () => {
      const mockCallback = vi.fn().mockResolvedValue({
        allow: true,
        remember: true,
      } as PermissionDecision);

      checker.setAskCallback(mockCallback);

      // 第一次调用
      await checker.check({
        command: 'git push origin main',
        workdir: testProjectRoot,
      });

      // 第二次调用应该不再询问
      const result = await checker.check({
        command: 'git commit -m "test"',
        workdir: testProjectRoot,
      });

      // 记住的是整个模式，第二次可能仍需询问（取决于实现）
      expect(result.allowed).toBeDefined();
    });
  });

  describe('会话权限管理', () => {
    it('清除会话权限', async () => {
      const mockCallback = vi.fn().mockResolvedValue({
        allow: true,
        remember: true,
      } as PermissionDecision);

      checker.setAskCallback(mockCallback);

      // 第一次调用
      await checker.check({
        command: 'git push origin main',
        workdir: testProjectRoot,
      });

      // 清除会话权限
      checker.clearSessionPermissions();

      // 再次调用应该重新询问
      await checker.check({
        command: 'git push origin main',
        workdir: testProjectRoot,
      });

      // 应该被调用两次
      expect(mockCallback).toHaveBeenCalledTimes(2);
    });
  });

  describe('规则管理', () => {
    it('添加新规则', () => {
      checker.addRule({
        pattern: 'custom-cmd *',
        action: 'allow',
      });

      const config = checker.getConfig();
      const hasRule = config.rules.some(r => r.pattern === 'custom-cmd *');
      expect(hasRule).toBe(true);
    });

    it('更新已有规则', () => {
      // 添加规则
      checker.addRule({
        pattern: 'test-cmd',
        action: 'allow',
      });

      // 更新规则
      checker.addRule({
        pattern: 'test-cmd',
        action: 'deny',
      });

      const config = checker.getConfig();
      const rule = config.rules.find(r => r.pattern === 'test-cmd');
      expect(rule?.action).toBe('deny');
    });
  });

  describe('项目目录检查', () => {
    it('识别项目内路径', async () => {
      const result = await checker.check({
        command: 'cat ./src/index.ts',
        workdir: testProjectRoot,
      });

      // 项目内路径应该正常检查
      expect(result).toBeDefined();
    });

    it('识别项目外路径', async () => {
      // 访问项目外的绝对路径
      const result = await checker.check({
        command: 'cat /etc/passwd',
        workdir: testProjectRoot,
      });

      // 外部路径可能需要确认或拒绝
      expect(result).toBeDefined();
    });
  });
});
