import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { ApprovalManager } from '../approval-manager.js';
import { ConfigManager } from '../../../config/config-manager.js';
import { ApprovalRequest } from '../types.js';

vi.mock('../../../config/config-manager.js');

describe('ApprovalManager', () => {
  let approvalManager: ApprovalManager;
  let mockConfigManager: vi.Mocked<ConfigManager>;

  beforeEach(async () => {
    mockConfigManager = new ConfigManager() as vi.Mocked<ConfigManager>;
    mockConfigManager.getStorageManager = vi.fn().mockReturnValue({
      loadData: vi.fn().mockResolvedValue(null),
      saveData: vi.fn().mockResolvedValue(undefined),
      ensureStorageDirectories: vi.fn().mockResolvedValue(undefined)
    });

    approvalManager = new ApprovalManager(mockConfigManager);
    await approvalManager.init();
  });

  afterEach(() => {
    approvalManager.dispose();
  });

  describe('createApprovalRequest', () => {
    it('should create approval request for high-risk operation', async () => {
      const params = {
        toolName: 'deployment-management',
        action: 'deploy_to_production',
        parameters: { environment: 'production' },
        userId: 'user123'
      };

      const request = await approvalManager.createApprovalRequest(params);

      expect(request).toBeDefined();
      expect(request.toolName).toBe(params.toolName);
      expect(request.action).toBe(params.action);
      expect(request.riskLevel).toBe('critical');
      expect(request.status).toBe('pending');
      expect(request.requestedBy).toBe(params.userId);
    });

    it('should auto-approve based on policy conditions', async () => {
      // This would require setting up a policy with auto-approve conditions
      // For now, we'll test that low-risk operations don't require approval
      const params = {
        toolName: 'unknown-tool',
        action: 'safe-action',
        parameters: {},
        userId: 'user123'
      };

      let autoApproved = false;
      approvalManager.once('auto-approved', () => {
        autoApproved = true;
      });

      const request = await approvalManager.createApprovalRequest(params);

      // Low risk operations with no policy should not require approval
      expect(request).toBeDefined(); // In this implementation, it still creates a request
    });

    it('should set appropriate TTL based on risk level', async () => {
      const criticalParams = {
        toolName: 'deployment-management',
        action: 'deploy_to_production',
        parameters: {},
        userId: 'user123'
      };

      const criticalRequest = await approvalManager.createApprovalRequest(criticalParams);
      expect(criticalRequest.ttl).toBe(300); // 5 minutes for critical

      const mediumParams = {
        toolName: 'process-automation',
        action: 'execute_process',
        parameters: {},
        userId: 'user123'
      };

      const mediumRequest = await approvalManager.createApprovalRequest(mediumParams);
      expect(mediumRequest.ttl).toBe(1800); // 30 minutes for medium
    });
  });

  describe('approveRequest', () => {
    it('should approve pending request', async () => {
      const request = await approvalManager.createApprovalRequest({
        toolName: 'test-tool',
        action: 'test-action',
        parameters: {},
        userId: 'requester'
      });

      const success = await approvalManager.approveRequest(request.id, 'approver');

      expect(success).toBe(true);
      
      // Verify request status updated
      const pending = await approvalManager.getPendingRequests();
      expect(pending.find(r => r.id === request.id)).toBeUndefined();
    });

    it('should not approve already processed request', async () => {
      const request = await approvalManager.createApprovalRequest({
        toolName: 'test-tool',
        action: 'test-action',
        parameters: {},
        userId: 'requester'
      });

      // Approve once
      await approvalManager.approveRequest(request.id, 'approver');
      
      // Try to approve again
      const success = await approvalManager.approveRequest(request.id, 'approver2');
      expect(success).toBe(false);
    });

    it('should emit approval-granted event', async () => {
      const request = await approvalManager.createApprovalRequest({
        toolName: 'test-tool',
        action: 'test-action',
        parameters: {},
        userId: 'requester'
      });

      let eventEmitted = false;
      approvalManager.once('approval-granted', (approved) => {
        eventEmitted = true;
        expect(approved.id).toBe(request.id);
      });

      await approvalManager.approveRequest(request.id, 'approver');
      expect(eventEmitted).toBe(true);
    });
  });

  describe('rejectRequest', () => {
    it('should reject pending request with reason', async () => {
      const request = await approvalManager.createApprovalRequest({
        toolName: 'test-tool',
        action: 'test-action',
        parameters: {},
        userId: 'requester'
      });

      const reason = 'Too risky for current state';
      const success = await approvalManager.rejectRequest(request.id, 'reviewer', reason);

      expect(success).toBe(true);
    });

    it('should emit approval-rejected event', async () => {
      const request = await approvalManager.createApprovalRequest({
        toolName: 'test-tool',
        action: 'test-action',
        parameters: {},
        userId: 'requester'
      });

      let eventEmitted = false;
      approvalManager.once('approval-rejected', (rejected) => {
        eventEmitted = true;
        expect(rejected.id).toBe(request.id);
        expect(rejected.rejectionReason).toBe('Not safe');
      });

      await approvalManager.rejectRequest(request.id, 'reviewer', 'Not safe');
      expect(eventEmitted).toBe(true);
    });
  });

  describe('getPendingRequests', () => {
    it('should return only pending requests', async () => {
      // Create multiple requests
      const req1 = await approvalManager.createApprovalRequest({
        toolName: 'tool1',
        action: 'action1',
        parameters: {},
        userId: 'user1'
      });

      const req2 = await approvalManager.createApprovalRequest({
        toolName: 'tool2',
        action: 'action2',
        parameters: {},
        userId: 'user2'
      });

      // Approve one
      await approvalManager.approveRequest(req1.id, 'approver');

      const pending = await approvalManager.getPendingRequests();
      
      expect(pending.length).toBe(1);
      expect(pending[0].id).toBe(req2.id);
    });

    it('should exclude requests from same user when userId provided', async () => {
      await approvalManager.createApprovalRequest({
        toolName: 'tool1',
        action: 'action1',
        parameters: {},
        userId: 'user1'
      });

      await approvalManager.createApprovalRequest({
        toolName: 'tool2',
        action: 'action2',
        parameters: {},
        userId: 'user2'
      });

      const pendingForUser1 = await approvalManager.getPendingRequests('user1');
      
      expect(pendingForUser1.length).toBe(1);
      expect(pendingForUser1[0].requestedBy).toBe('user2');
    });
  });

  describe('getAuditLog', () => {
    it('should log all approval decisions', async () => {
      const request = await approvalManager.createApprovalRequest({
        toolName: 'test-tool',
        action: 'test-action',
        parameters: {},
        userId: 'requester'
      });

      await approvalManager.approveRequest(request.id, 'approver');

      const logs = await approvalManager.getAuditLog();
      
      expect(logs.length).toBeGreaterThan(0);
      const approvalLog = logs.find(l => l.result === 'approved');
      expect(approvalLog).toBeDefined();
      expect(approvalLog?.toolName).toBe('test-tool');
      expect(approvalLog?.action).toBe('test-action');
    });

    it('should filter audit logs by criteria', async () => {
      // Create and process multiple requests
      const req1 = await approvalManager.createApprovalRequest({
        toolName: 'tool1',
        action: 'action1',
        parameters: {},
        userId: 'user1'
      });

      const req2 = await approvalManager.createApprovalRequest({
        toolName: 'tool2',
        action: 'action2',
        parameters: {},
        userId: 'user2'
      });

      await approvalManager.approveRequest(req1.id, 'approver');
      await approvalManager.rejectRequest(req2.id, 'reviewer');

      // Filter by tool name
      const tool1Logs = await approvalManager.getAuditLog({ toolName: 'tool1' });
      expect(tool1Logs.every(l => l.toolName === 'tool1')).toBe(true);

      // Filter by result
      const approvedLogs = await approvalManager.getAuditLog({ result: 'approved' });
      expect(approvedLogs.every(l => l.result === 'approved')).toBe(true);
    });
  });

  describe('High-risk operation detection', () => {
    it('should identify critical risk operations', async () => {
      const policy = approvalManager.getActionPolicy('deployment-management', 'deploy_to_production');
      
      expect(policy.requiresApproval).toBe(true);
      expect(policy.riskLevel).toBe('critical');
      expect(policy.approvalLevel).toBe('admin');
    });

    it('should identify high risk operations', async () => {
      const policy = approvalManager.getActionPolicy('git-workflow', 'force_push');
      
      expect(policy.requiresApproval).toBe(true);
      expect(policy.riskLevel).toBe('high');
    });

    it('should use default policy for unknown operations', async () => {
      const policy = approvalManager.getActionPolicy('unknown-tool', 'unknown-action');
      
      expect(policy.requiresApproval).toBe(false);
      expect(policy.riskLevel).toBe('low');
    });
  });
});