import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
import express, { Application } from 'express';
import request from 'supertest';
import { setupSecurityAPI } from '../../api/security.js';
import { SecurityManager } from '../../../utils/security-manager.js';

// Mock the SecurityManager
vi.mock('../../../utils/security-manager.js', () => ({
  SecurityManager: {
    getInstance: vi.fn()
  }
}));

describe('Security API', () => {
  let app: Application;
  let mockSecurityManager: any;

  beforeEach(() => {
    app = express();
    app.use(express.json());

    // Create mock security manager with all required methods
    mockSecurityManager = {
      getSecurityStatus: vi.fn(),
      getSecurityEvents: vi.fn(),
      getPendingApprovals: vi.fn(),
      processApproval: vi.fn(),
      configureSecurityPolicy: vi.fn(),
      validateToolAccess: vi.fn(),
      generateSecurityMetrics: vi.fn(),
      getSecurityAlerts: vi.fn(),
      generateSecurityReport: vi.fn(),
      auditToolExecution: vi.fn(),
      setSecurityLevel: vi.fn(),
      getBlockedTools: vi.fn(),
      addToolRestriction: vi.fn(),
      removeToolRestriction: vi.fn(),
      checkCompliance: vi.fn()
    };

    // Mock getInstance to return our mock
    (SecurityManager.getInstance as any).mockReturnValue(mockSecurityManager);

    // Setup the API
    setupSecurityAPI(app, mockSecurityManager);
  });

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

  describe('GET /api/security/status', () => {
    it('should return security status', async () => {
      const mockStatus = {
        securityLevel: 'high',
        activeApprovals: 5,
        blockedActions: 2,
        lastSecurityEvent: new Date().toISOString(),
        complianceStatus: 'compliant'
      };

      mockSecurityManager.getSecurityStatus.mockResolvedValue(mockStatus);

      const response = await request(app).get('/api/security/status');

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(response.body.data).toEqual(mockStatus);
      expect(mockSecurityManager.getSecurityStatus).toHaveBeenCalled();
    });

    it('should handle errors gracefully', async () => {
      mockSecurityManager.getSecurityStatus.mockRejectedValue(new Error('Security error'));

      const response = await request(app).get('/api/security/status');

      expect(response.status).toBe(500);
      expect(response.body.success).toBe(false);
      expect(response.body.error).toBe('Failed to fetch security status');
    });
  });

  describe('GET /api/security/events', () => {
    it('should return security events with filters', async () => {
      const mockEvents = [
        {
          id: 'event-1',
          type: 'access_denied',
          severity: 'high',
          timestamp: new Date().toISOString(),
          details: { tool: 'dangerous_tool', user: 'user1' }
        }
      ];

      mockSecurityManager.getSecurityEvents.mockResolvedValue(mockEvents);

      const response = await request(app)
        .get('/api/security/events')
        .query({ 
          type: 'access_denied', 
          severity: 'high', 
          limit: '50',
          startDate: '2024-01-01'
        });

      expect(response.status).toBe(200);
      expect(response.body.data).toEqual(mockEvents);
      expect(mockSecurityManager.getSecurityEvents).toHaveBeenCalledWith({
        type: 'access_denied',
        severity: 'high',
        limit: 50,
        startDate: '2024-01-01'
      });
    });
  });

  describe('GET /api/security/approvals', () => {
    it('should return pending approvals', async () => {
      const mockApprovals = [
        {
          id: 'approval-1',
          toolName: 'system_modify',
          requestedBy: 'user1',
          status: 'pending',
          createdAt: new Date().toISOString()
        }
      ];

      mockSecurityManager.getPendingApprovals.mockResolvedValue(mockApprovals);

      const response = await request(app)
        .get('/api/security/approvals')
        .query({ status: 'pending', toolName: 'system_modify' });

      expect(response.status).toBe(200);
      expect(response.body.data.approvals).toEqual(mockApprovals);
      expect(mockSecurityManager.getPendingApprovals).toHaveBeenCalledWith({
        status: 'pending',
        toolName: 'system_modify'
      });
    });

    it('should return mock data when getPendingApprovals is not implemented', async () => {
      mockSecurityManager.getPendingApprovals = undefined;

      const response = await request(app).get('/api/security/approvals');

      expect(response.status).toBe(200);
      expect(response.body.data.approvals).toBeInstanceOf(Array);
      expect(response.body.data.approvals[0]).toHaveProperty('id');
      expect(response.body.data.approvals[0]).toHaveProperty('status', 'pending');
    });
  });

  describe('POST /api/security/approvals/:id', () => {
    it('should process approval decision', async () => {
      const approvalData = {
        decision: 'approved',
        reason: 'Legitimate use case',
        restrictions: ['read_only']
      };

      mockSecurityManager.processApproval.mockResolvedValue({
        id: 'approval-1',
        status: 'approved',
        processedAt: new Date().toISOString()
      });

      const response = await request(app)
        .post('/api/security/approvals/approval-1')
        .send(approvalData);

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(mockSecurityManager.processApproval).toHaveBeenCalledWith({
        approvalId: 'approval-1',
        decision: 'approve',
        reason: 'Legitimate use case'
      });
    });

    it('should validate required fields', async () => {
      const response = await request(app)
        .post('/api/security/approvals/approval-1')
        .send({ reason: 'Test' }); // missing decision

      expect(response.status).toBe(400);
      expect(response.body.error).toBe('Decision is required (approved/denied)');
    });
  });

  describe('PUT /api/security/policy', () => {
    it('should update security policy', async () => {
      const policyUpdate = {
        requireApproval: ['system_modify', 'delete_data'],
        autoApprove: ['read_only'],
        securityLevel: 'high'
      };

      mockSecurityManager.configureSecurityPolicy.mockResolvedValue({
        updated: true,
        policy: policyUpdate
      });

      const response = await request(app)
        .put('/api/security/policy')
        .send(policyUpdate);

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(mockSecurityManager.configureSecurityPolicy).toHaveBeenCalledWith(policyUpdate);
    });

    it('should return mock data when configureSecurityPolicy is not implemented', async () => {
      mockSecurityManager.configureSecurityPolicy = undefined;

      const response = await request(app)
        .put('/api/security/policy')
        .send({ securityLevel: 'medium' });

      expect(response.status).toBe(200);
      expect(response.body.data).toHaveProperty('requireApproval');
      expect(response.body.data).toHaveProperty('blockedTools');
    });
  });

  describe('POST /api/security/validate', () => {
    it('should validate tool access', async () => {
      const validationRequest = {
        tool: 'create_file',
        context: { path: '/safe/path/file.txt' },
        user: 'user1'
      };

      mockSecurityManager.validateToolAccess.mockResolvedValue({
        allowed: true,
        requiresApproval: false
      });

      const response = await request(app)
        .post('/api/security/validate')
        .send(validationRequest);

      expect(response.status).toBe(200);
      expect(response.body.data.allowed).toBe(true);
      expect(mockSecurityManager.validateToolAccess).toHaveBeenCalledWith(
        'create_file',
        { path: '/safe/path/file.txt' }
      );
    });

    it('should require tool name', async () => {
      const response = await request(app)
        .post('/api/security/validate')
        .send({ context: {} });

      expect(response.status).toBe(400);
      expect(response.body.error).toBe('Tool name is required');
    });
  });

  describe('GET /api/security/metrics', () => {
    it('should return security metrics', async () => {
      const mockMetrics = {
        totalEvents: 150,
        blockedActions: 25,
        approvalRate: 85,
        averageApprovalTime: 300,
        securityScore: 92
      };

      mockSecurityManager.generateSecurityMetrics.mockResolvedValue(mockMetrics);

      const response = await request(app)
        .get('/api/security/metrics')
        .query({ timeRange: '7d' });

      expect(response.status).toBe(200);
      expect(response.body.data).toEqual(mockMetrics);
      expect(mockSecurityManager.generateSecurityMetrics).toHaveBeenCalled();
    });

    it('should return 500 when generateSecurityMetrics fails', async () => {
      mockSecurityManager.generateSecurityMetrics.mockRejectedValue(new Error('Metrics error'));

      const response = await request(app).get('/api/security/metrics');

      expect(response.status).toBe(500);
      expect(response.body.error).toBe('Failed to fetch security metrics');
    });
  });

  describe('GET /api/security/alerts', () => {
    it('should return security alerts', async () => {
      const mockStatus = {
        alerts: [
          {
            id: 'alert-1',
            type: 'suspicious_activity',
            severity: 'critical',
            message: 'Multiple failed access attempts',
            timestamp: new Date().toISOString()
          }
        ]
      };

      mockSecurityManager.getSecurityStatus.mockResolvedValue(mockStatus);

      const response = await request(app)
        .get('/api/security/alerts')
        .query({ active: 'true', severity: 'critical' });

      expect(response.status).toBe(200);
      expect(response.body.data).toEqual({ alerts: mockStatus.alerts });
      expect(mockSecurityManager.getSecurityStatus).toHaveBeenCalled();
    });
  });

  describe('GET /api/security/report', () => {
    it('should return 404 for security report (not implemented)', async () => {
      const response = await request(app)
        .get('/api/security/report')
        .query({ 
          format: 'json', 
          period: 'monthly',
          includeRecommendations: 'true'
        });

      expect(response.status).toBe(404);
      expect(response.body.error).toBe('Security report generation not available');
    });

    it('should return 404 for CSV report (not implemented)', async () => {
      const response = await request(app)
        .get('/api/security/report')
        .query({ format: 'csv' });

      expect(response.status).toBe(404);
      expect(response.body.error).toBe('Security report generation not available');
    });
  });

  describe('POST /api/security/audit', () => {
    it('should return 404 for audit (not implemented)', async () => {
      const auditData = {
        tool: 'delete_file',
        action: 'execution',
        result: 'blocked',
        reason: 'Unauthorized path'
      };

      const response = await request(app)
        .post('/api/security/audit')
        .send(auditData);

      expect(response.status).toBe(404);
      expect(response.body.error).toBe('Audit functionality not available');
    });
  });

  describe('PUT /api/security/level', () => {
    it('should return 404 for security level update (not implemented)', async () => {
      const response = await request(app)
        .put('/api/security/level')
        .send({ level: 'high' });

      expect(response.status).toBe(404);
      expect(response.body.error).toBe('Security level configuration not available');
    });

    it('should validate security level', async () => {
      const response = await request(app)
        .put('/api/security/level')
        .send({ level: 'invalid' });

      expect(response.status).toBe(400);
      expect(response.body.error).toBe('Invalid security level. Must be one of: low, medium, high, critical');
    });
  });

  describe('GET /api/security/blocked-tools', () => {
    it('should return 404 for blocked tools (not implemented)', async () => {
      const response = await request(app).get('/api/security/blocked-tools');

      expect(response.status).toBe(404);
      expect(response.body.error).toBe('Blocked tools list not available');
    });
  });

  describe('POST /api/security/restrictions', () => {
    it('should return 404 for adding restrictions (not implemented)', async () => {
      const restriction = {
        tool: 'file_write',
        restriction: 'path_pattern',
        value: '/restricted/*'
      };

      const response = await request(app)
        .post('/api/security/restrictions')
        .send(restriction);

      expect(response.status).toBe(404);
      expect(response.body.error).toBe('Tool restriction management not available');
    });
  });

  describe('DELETE /api/security/restrictions/:id', () => {
    it('should return 404 for removing restrictions (not implemented)', async () => {
      const response = await request(app).delete('/api/security/restrictions/restriction-1');

      expect(response.status).toBe(404);
      expect(response.body.error).toBe('Tool restriction management not available');
    });
  });

  describe('GET /api/security/compliance', () => {
    it('should return 404 for compliance check (not implemented)', async () => {
      const response = await request(app)
        .get('/api/security/compliance')
        .query({ standards: 'SOC2,ISO27001' });

      expect(response.status).toBe(404);
      expect(response.body.error).toBe('Compliance checking not available');
    });
  });
});