/**
 * Permission Handler 测试
 *
 * 测试权限类型检测、请求构建、响应处理、超时处理等功能
 */

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

// Mock broadcastToSession before importing handler
vi.mock('../../../src/ws.js', () => ({
  broadcastToSession: vi.fn(),
}));

import {
  createServerPermissionCallback,
  handlePermissionResponse,
  cancelPendingRequests,
  getPendingRequestCount,
} from '../../../src/permission/handler.js';
import { broadcastToSession } from '../../../src/ws.js';

describe('Permission Handler', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.useFakeTimers();
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  describe('detectPermissionType - 权限类型检测', () => {
    it('检测 git 命令', async () => {
      const callback = createServerPermissionCallback('session-1');
      // 触发一个 git 命令的权限请求
      const promise = callback({ command: 'git push origin main', workdir: '/test' });

      // 检查发送的消息类型
      expect(broadcastToSession).toHaveBeenCalledWith(
        'session-1',
        expect.objectContaining({
          type: 'permission_request',
          payload: expect.objectContaining({
            permissionType: 'git',
            context: expect.objectContaining({
              gitOperation: 'push',
            }),
          }),
        })
      );

      // 模拟响应
      const call = vi.mocked(broadcastToSession).mock.calls[0];
      const requestId = (call[1] as any).payload.requestId;
      handlePermissionResponse(requestId, true);
      await promise;
    });

    it('检测文件操作命令', async () => {
      const callback = createServerPermissionCallback('session-1');
      const promise = callback({ command: 'read /etc/passwd', workdir: '/test' });

      expect(broadcastToSession).toHaveBeenCalledWith(
        'session-1',
        expect.objectContaining({
          payload: expect.objectContaining({
            permissionType: 'file',
            context: expect.objectContaining({
              operation: 'read',
              path: '/etc/passwd',
            }),
          }),
        })
      );

      const call = vi.mocked(broadcastToSession).mock.calls[0];
      const requestId = (call[1] as any).payload.requestId;
      handlePermissionResponse(requestId, true);
      await promise;
    });

    it('检测 write 文件操作', async () => {
      const callback = createServerPermissionCallback('session-1');
      const promise = callback({ command: 'write /tmp/test.txt', workdir: '/test' });

      expect(broadcastToSession).toHaveBeenCalledWith(
        'session-1',
        expect.objectContaining({
          payload: expect.objectContaining({
            permissionType: 'file',
            context: expect.objectContaining({
              operation: 'write',
            }),
          }),
        })
      );

      const call = vi.mocked(broadcastToSession).mock.calls[0];
      const requestId = (call[1] as any).payload.requestId;
      handlePermissionResponse(requestId, true);
      await promise;
    });

    it('检测 web 操作', async () => {
      const callback = createServerPermissionCallback('session-1');
      const promise = callback({ command: 'fetch https://api.example.com', workdir: '/test' });

      expect(broadcastToSession).toHaveBeenCalledWith(
        'session-1',
        expect.objectContaining({
          payload: expect.objectContaining({
            permissionType: 'web',
          }),
        })
      );

      const call = vi.mocked(broadcastToSession).mock.calls[0];
      const requestId = (call[1] as any).payload.requestId;
      handlePermissionResponse(requestId, true);
      await promise;
    });

    it('默认为 bash 类型', async () => {
      const callback = createServerPermissionCallback('session-1');
      const promise = callback({ command: 'npm install', workdir: '/test' });

      expect(broadcastToSession).toHaveBeenCalledWith(
        'session-1',
        expect.objectContaining({
          payload: expect.objectContaining({
            permissionType: 'bash',
          }),
        })
      );

      const call = vi.mocked(broadcastToSession).mock.calls[0];
      const requestId = (call[1] as any).payload.requestId;
      handlePermissionResponse(requestId, true);
      await promise;
    });
  });

  describe('buildRequestContext - 请求上下文构建', () => {
    it('git 命令包含 gitOperation', async () => {
      const callback = createServerPermissionCallback('session-1');
      const promise = callback({ command: 'git commit -m "test"', workdir: '/test' });

      expect(broadcastToSession).toHaveBeenCalledWith(
        'session-1',
        expect.objectContaining({
          payload: expect.objectContaining({
            context: expect.objectContaining({
              command: 'git commit -m "test"',
              gitOperation: 'commit',
            }),
          }),
        })
      );

      const call = vi.mocked(broadcastToSession).mock.calls[0];
      const requestId = (call[1] as any).payload.requestId;
      handlePermissionResponse(requestId, true);
      await promise;
    });

    it('文件操作包含 patterns 和 externalPaths', async () => {
      const callback = createServerPermissionCallback('session-1');
      const promise = callback({
        command: 'edit /test/file.ts',
        workdir: '/test',
        patterns: ['*.ts'],
        externalPaths: ['/external'],
      });

      expect(broadcastToSession).toHaveBeenCalledWith(
        'session-1',
        expect.objectContaining({
          payload: expect.objectContaining({
            context: expect.objectContaining({
              operation: 'edit',
              patterns: ['*.ts'],
              externalPaths: ['/external'],
            }),
          }),
        })
      );

      const call = vi.mocked(broadcastToSession).mock.calls[0];
      const requestId = (call[1] as any).payload.requestId;
      handlePermissionResponse(requestId, true);
      await promise;
    });

    it('bash 命令包含 command', async () => {
      const callback = createServerPermissionCallback('session-1');
      const promise = callback({ command: 'rm -rf /tmp/test', workdir: '/test' });

      expect(broadcastToSession).toHaveBeenCalledWith(
        'session-1',
        expect.objectContaining({
          payload: expect.objectContaining({
            context: expect.objectContaining({
              command: 'rm -rf /tmp/test',
            }),
          }),
        })
      );

      const call = vi.mocked(broadcastToSession).mock.calls[0];
      const requestId = (call[1] as any).payload.requestId;
      handlePermissionResponse(requestId, true);
      await promise;
    });
  });

  describe('createServerPermissionCallback - 权限回调', () => {
    it('发送权限请求消息', async () => {
      const callback = createServerPermissionCallback('session-1');
      const promise = callback({ command: 'ls -la', workdir: '/test' });

      expect(broadcastToSession).toHaveBeenCalledTimes(1);
      expect(broadcastToSession).toHaveBeenCalledWith(
        'session-1',
        expect.objectContaining({
          type: 'permission_request',
          sessionId: 'session-1',
          payload: expect.objectContaining({
            requestId: expect.any(String),
            permissionType: 'bash',
          }),
        })
      );

      // 响应以完成 promise
      const call = vi.mocked(broadcastToSession).mock.calls[0];
      const requestId = (call[1] as any).payload.requestId;
      handlePermissionResponse(requestId, true);
      await promise;
    });

    it('返回 allow: true 当响应允许时', async () => {
      const callback = createServerPermissionCallback('session-1');
      const promise = callback({ command: 'ls', workdir: '/test' });

      const call = vi.mocked(broadcastToSession).mock.calls[0];
      const requestId = (call[1] as any).payload.requestId;
      handlePermissionResponse(requestId, true, true);

      const result = await promise;
      expect(result).toEqual({ allow: true, remember: true });
    });

    it('返回 allow: false 当响应拒绝时', async () => {
      const callback = createServerPermissionCallback('session-1');
      const promise = callback({ command: 'rm -rf /', workdir: '/test' });

      const call = vi.mocked(broadcastToSession).mock.calls[0];
      const requestId = (call[1] as any).payload.requestId;
      handlePermissionResponse(requestId, false, false);

      const result = await promise;
      expect(result).toEqual({ allow: false, remember: false });
    });
  });

  describe('handlePermissionResponse - 权限响应处理', () => {
    it('处理存在的请求返回 true', async () => {
      const callback = createServerPermissionCallback('session-1');
      const promise = callback({ command: 'ls', workdir: '/test' });

      const call = vi.mocked(broadcastToSession).mock.calls[0];
      const requestId = (call[1] as any).payload.requestId;

      const result = handlePermissionResponse(requestId, true);
      expect(result).toBe(true);

      await promise;
    });

    it('处理不存在的请求返回 false', () => {
      const result = handlePermissionResponse('non-existent-request-id', true);
      expect(result).toBe(false);
    });

    it('清除超时定时器', async () => {
      const callback = createServerPermissionCallback('session-1');
      const promise = callback({ command: 'ls', workdir: '/test' });

      const call = vi.mocked(broadcastToSession).mock.calls[0];
      const requestId = (call[1] as any).payload.requestId;

      // 响应请求
      handlePermissionResponse(requestId, true);
      await promise;

      // 快进超过超时时间，不应有任何错误
      vi.advanceTimersByTime(70000);
    });
  });

  describe('超时处理', () => {
    it('超时后返回 allow: false', async () => {
      const callback = createServerPermissionCallback('session-1');
      const promise = callback({ command: 'dangerous-command', workdir: '/test' });

      // 快进 60 秒（超时时间）
      vi.advanceTimersByTime(60000);

      const result = await promise;
      expect(result).toEqual({ allow: false, remember: false });
    });

    it('超时后从 pending 中移除', async () => {
      const initialCount = getPendingRequestCount();

      const callback = createServerPermissionCallback('session-1');
      const promise = callback({ command: 'test', workdir: '/test' });

      expect(getPendingRequestCount()).toBe(initialCount + 1);

      // 超时
      vi.advanceTimersByTime(60000);
      await promise;

      expect(getPendingRequestCount()).toBe(initialCount);
    });
  });

  describe('getPendingRequestCount - 待处理请求计数', () => {
    it('返回正确的待处理请求数', async () => {
      const initialCount = getPendingRequestCount();

      const callback = createServerPermissionCallback('session-1');
      const promise1 = callback({ command: 'cmd1', workdir: '/test' });
      const promise2 = callback({ command: 'cmd2', workdir: '/test' });

      expect(getPendingRequestCount()).toBe(initialCount + 2);

      // 响应第一个
      const call1 = vi.mocked(broadcastToSession).mock.calls[0];
      const requestId1 = (call1[1] as any).payload.requestId;
      handlePermissionResponse(requestId1, true);
      await promise1;

      expect(getPendingRequestCount()).toBe(initialCount + 1);

      // 响应第二个
      const call2 = vi.mocked(broadcastToSession).mock.calls[1];
      const requestId2 = (call2[1] as any).payload.requestId;
      handlePermissionResponse(requestId2, true);
      await promise2;

      expect(getPendingRequestCount()).toBe(initialCount);
    });
  });

  describe('cancelPendingRequests - 取消待处理请求', () => {
    it('调用不抛出错误', () => {
      // 目前这个函数是占位符，只是确保不抛出错误
      expect(() => cancelPendingRequests('session-1')).not.toThrow();
    });
  });
});
