import { describe, it, expect, beforeEach, vi } from 'vitest';
import { GitPermissionChecker } from '../../../src/permission/checkers/git.js';
import type { GitPermissionContext, PermissionDecision } from '../../../src/permission/types.js';

describe('GitPermissionChecker - Git 权限检查器', () => {
  let checker: GitPermissionChecker;

  beforeEach(() => {
    checker = new GitPermissionChecker();
  });

  describe('读操作（默认允许）', () => {
    const readOperations: GitPermissionContext['operation'][] = [
      'status',
      'diff',
      'log',
      'branch_list',
      'show',
    ];

    for (const operation of readOperations) {
      it(`${operation} 默认允许`, async () => {
        const result = await checker.checkGitPermission({ operation });

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

  describe('写操作（默认询问）', () => {
    const writeOperations: GitPermissionContext['operation'][] = [
      'add',
      'commit',
      'push',
      'pull',
      'checkout',
      'branch_create',
      'branch_delete',
      'stash',
      'stash_pop',
      'merge',
      'rebase',
    ];

    for (const operation of writeOperations) {
      it(`${operation} 无回调时需要确认`, async () => {
        const result = await checker.checkGitPermission({ operation });

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

  describe('危险操作', () => {
    it('reset 总是危险操作', async () => {
      const result = await checker.checkGitPermission({ operation: 'reset' });

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

    it('push --force 是危险操作', async () => {
      const result = await checker.checkGitPermission({
        operation: 'push',
        force: true,
      });

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

    it('checkout --force 是危险操作', async () => {
      const result = await checker.checkGitPermission({
        operation: 'checkout',
        force: true,
      });

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

    it('rebase --force 是危险操作', async () => {
      const result = await checker.checkGitPermission({
        operation: 'rebase',
        force: true,
      });

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

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

      checker.setAskCallback(mockCallback);

      const result = await checker.checkGitPermission({ operation: 'commit' });

      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.checkGitPermission({ operation: 'push' });

      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.checkGitPermission({ operation: 'commit' });

      // 第二次调用不应该再询问
      const secondResult = await checker.checkGitPermission({ operation: 'push' });

      expect(secondResult.allowed).toBe(true);
      // 第二次不需要回调（因为记住了写操作的权限）
      expect(mockCallback).toHaveBeenCalledTimes(1);
    });
  });

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

      checker.setAskCallback(mockCallback);

      // 第一次调用并记住
      await checker.checkGitPermission({ operation: 'commit' });

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

      // 再次调用应该重新询问
      await checker.checkGitPermission({ operation: 'commit' });

      expect(mockCallback).toHaveBeenCalledTimes(2);
    });

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

      checker.setAskCallback(mockCallback);

      // 第一次拒绝并记住
      await checker.checkGitPermission({ operation: 'push' });

      // 第二次直接拒绝
      const result = await checker.checkGitPermission({ operation: 'commit' });

      expect(result.allowed).toBe(false);
      expect(mockCallback).toHaveBeenCalledTimes(1);
    });
  });

  describe('配置管理', () => {
    it('获取默认配置', () => {
      const config = checker.getConfig();

      expect(config.readOperations).toBe('allow');
      expect(config.writeOperations).toBe('ask');
      expect(config.dangerousOperations).toBe('ask');
    });

    it('更新配置', () => {
      checker.setConfig({ writeOperations: 'allow' });

      const config = checker.getConfig();
      expect(config.writeOperations).toBe('allow');
    });

    it('配置更改后影响权限检查', async () => {
      checker.setConfig({ writeOperations: 'allow' });

      const result = await checker.checkGitPermission({ operation: 'commit' });

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

    it('配置危险操作为拒绝', async () => {
      checker.setConfig({ dangerousOperations: 'deny' });

      const result = await checker.checkGitPermission({ operation: 'reset' });

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

  describe('操作描述生成', () => {
    it('带 target 的操作描述', async () => {
      checker.setAskCallback(vi.fn().mockResolvedValue({ allow: true, remember: false }));

      await checker.checkGitPermission({
        operation: 'checkout',
        target: 'feature-branch',
      });

      // 验证回调收到正确的描述
      const callArg = vi.mocked(checker['askCallback']!).mock.calls[0][0];
      expect(callArg.command).toContain('feature-branch');
    });

    it('带 remote 的操作描述', async () => {
      checker.setAskCallback(vi.fn().mockResolvedValue({ allow: true, remember: false }));

      await checker.checkGitPermission({
        operation: 'push',
        remote: 'origin',
      });

      const callArg = vi.mocked(checker['askCallback']!).mock.calls[0][0];
      expect(callArg.command).toContain('origin');
    });

    it('带 commit message 的操作描述（截断）', async () => {
      checker.setAskCallback(vi.fn().mockResolvedValue({ allow: true, remember: false }));

      const longMessage = 'a'.repeat(100);
      await checker.checkGitPermission({
        operation: 'commit',
        message: longMessage,
      });

      const callArg = vi.mocked(checker['askCallback']!).mock.calls[0][0];
      expect(callArg.command).toContain('...');
      expect(callArg.command.length).toBeLessThan(longMessage.length + 50);
    });

    it('--force 显示在描述中', async () => {
      checker.setAskCallback(vi.fn().mockResolvedValue({ allow: true, remember: false }));

      await checker.checkGitPermission({
        operation: 'push',
        force: true,
      });

      const callArg = vi.mocked(checker['askCallback']!).mock.calls[0][0];
      expect(callArg.command).toContain('--force');
    });
  });

  describe('check 接口（从命令解析）', () => {
    it('解析 git status', async () => {
      const result = await checker.check({
        command: 'git status',
        workdir: '/test',
      });

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

    it('解析 git_commit', async () => {
      const result = await checker.check({
        command: 'git_commit',
        workdir: '/test',
      });

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

    it('无法解析的命令返回拒绝', async () => {
      const result = await checker.check({
        command: 'invalid command',
        workdir: '/test',
      });

      expect(result.allowed).toBe(false);
      expect(result.reason).toContain('无法解析');
    });
  });
});
