import { describe, it, expect, beforeEach, vi } from 'vitest';
import { adminSecurityRouter } from './adminSecurity.ts';
import { TRPCError } from '@trpc/server';

// Mock context
const mockAdminContext = {
  user: {
    id: 1,
    role: 'admin',
    email: 'admin@example.com',
    sessionToken: 'test_token_123',
  },
};

const mockUserContext = {
  user: {
    id: 2,
    role: 'user',
    email: 'user@example.com',
  },
};

describe('Admin Security Router', () => {
  describe('Two-Factor Authentication', () => {
    it('should generate TOTP secret for 2FA setup', async () => {
      const caller = adminSecurityRouter.createCaller(mockAdminContext);
      const result = await caller.enableTwoFactor();

      expect(result).toHaveProperty('secret');
      expect(result).toHaveProperty('backupCodes');
      expect(result).toHaveProperty('qrCodeUrl');
      expect(result.backupCodes).toHaveLength(10);
      expect(result.qrCodeUrl).toContain('otpauth://totp/');
    });

    it('should verify 2FA setup with valid token', async () => {
      const caller = adminSecurityRouter.createCaller(mockAdminContext);
      const result = await caller.verifyTwoFactorSetup({
        token: '123456',
        backupCodes: ['A1B2C3D4E5F6', 'G7H8I9J0K1L2'],
      });

      expect(result.success).toBe(true);
      expect(result.message).toContain('Two-factor authentication enabled');
    });

    it('should reject invalid 2FA token', async () => {
      const caller = adminSecurityRouter.createCaller(mockAdminContext);

      try {
        await caller.verifyTwoFactorSetup({
          token: 'invalid',
          backupCodes: [],
        });
        expect.fail('Should have thrown error');
      } catch (error) {
        expect(error).toBeInstanceOf(TRPCError);
        expect((error as TRPCError<any>).code).toBe('BAD_REQUEST');
      }
    });

    it('should disable 2FA with password verification', async () => {
      const caller = adminSecurityRouter.createCaller(mockAdminContext);
      const result = await caller.disableTwoFactor({ password: 'correct_password' });

      expect(result.success).toBe(true);
      expect(result.message).toContain('Two-factor authentication disabled');
    });
  });

  describe('Session Management', () => {
    it('should get active sessions for admin', async () => {
      const caller = adminSecurityRouter.createCaller(mockAdminContext);
      const sessions = await caller.getAdminSessions();

      expect(Array.isArray(sessions)).toBe(true);
      expect(sessions.length).toBeGreaterThan(0);
      sessions.forEach(session => {
        expect(session).toHaveProperty('id');
        expect(session).toHaveProperty('ipAddress');
        expect(session).toHaveProperty('userAgent');
        expect(session).toHaveProperty('createdAt');
        expect(session).toHaveProperty('isCurrentSession');
      });
    });

    it('should terminate a session', async () => {
      const caller = adminSecurityRouter.createCaller(mockAdminContext);
      const result = await caller.terminateSession({ sessionId: 'session_123' });

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

    it('should terminate all other sessions', async () => {
      const caller = adminSecurityRouter.createCaller(mockAdminContext);
      const result = await caller.terminateAllOtherSessions();

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

    it('should get login history', async () => {
      const caller = adminSecurityRouter.createCaller(mockAdminContext);
      const history = await caller.getLoginHistory({ limit: 50 });

      expect(Array.isArray(history)).toBe(true);
      history.forEach(entry => {
        expect(entry).toHaveProperty('id');
        expect(entry).toHaveProperty('timestamp');
        expect(entry).toHaveProperty('ipAddress');
        expect(entry).toHaveProperty('status');
      });
    });
  });

  describe('API Key Management', () => {
    it('should generate API key', async () => {
      const caller = adminSecurityRouter.createCaller(mockAdminContext);
      const result = await caller.generateApiKey({
        name: 'Test API Key',
        expiresIn: 2592000000, // 30 days
      });

      expect(result).toHaveProperty('apiKey');
      expect(result).toHaveProperty('hashedKey');
      expect(result).toHaveProperty('createdAt');
      expect(result).toHaveProperty('expiresAt');
      expect(result.apiKey).toMatch(/^[a-f0-9]{64}$/);
    });

    it('should revoke API key', async () => {
      const caller = adminSecurityRouter.createCaller(mockAdminContext);
      const result = await caller.revokeApiKey({ keyId: 'key_123' });

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

  describe('Security Audit Log', () => {
    it('should get security audit log', async () => {
      const caller = adminSecurityRouter.createCaller(mockAdminContext);
      const logs = await caller.getSecurityAuditLog({ limit: 100, offset: 0 });

      expect(Array.isArray(logs)).toBe(true);
    });
  });

  describe('Backup & Restore', () => {
    it('should create backup', async () => {
      const caller = adminSecurityRouter.createCaller(mockAdminContext);
      const result = await caller.createBackup();

      expect(result).toHaveProperty('backupId');
      expect(result).toHaveProperty('createdAt');
      expect(result.status).toBe('completed');
      expect(result).toHaveProperty('size');
    });

    it('should list backups', async () => {
      const caller = adminSecurityRouter.createCaller(mockAdminContext);
      const backups = await caller.listBackups();

      expect(Array.isArray(backups)).toBe(true);
      backups.forEach(backup => {
        expect(backup).toHaveProperty('id');
        expect(backup).toHaveProperty('createdAt');
        expect(backup).toHaveProperty('size');
        expect(backup).toHaveProperty('status');
      });
    });

    it('should restore backup', async () => {
      const caller = adminSecurityRouter.createCaller(mockAdminContext);
      const result = await caller.restoreBackup({ backupId: 'backup_001' });

      expect(result.success).toBe(true);
      expect(result.message).toContain('Backup restore initiated');
      expect(result).toHaveProperty('estimatedTime');
    });
  });

  describe('System Health', () => {
    it('should get system health', async () => {
      const caller = adminSecurityRouter.createCaller(mockAdminContext);
      const health = await caller.getSystemHealth();

      expect(health).toHaveProperty('status');
      expect(health).toHaveProperty('uptime');
      expect(health).toHaveProperty('cpuUsage');
      expect(health).toHaveProperty('memoryUsage');
      expect(health).toHaveProperty('diskUsage');
      expect(health).toHaveProperty('databaseConnections');
      expect(health).toHaveProperty('activeUsers');
      expect(health).toHaveProperty('components');

      // Validate component statuses
      Object.values(health.components).forEach(status => {
        expect(['healthy', 'warning', 'critical']).toContain(status);
      });
    });

    it('should get system metrics', async () => {
      const caller = adminSecurityRouter.createCaller(mockAdminContext);
      const metrics = await caller.getSystemMetrics();

      expect(metrics).toHaveProperty('requests');
      expect(metrics).toHaveProperty('performance');
      expect(metrics).toHaveProperty('errors');

      expect(metrics.requests).toHaveProperty('total');
      expect(metrics.requests).toHaveProperty('perSecond');
      expect(metrics.requests).toHaveProperty('errorRate');

      expect(metrics.performance).toHaveProperty('avgResponseTime');
      expect(metrics.performance).toHaveProperty('p95ResponseTime');
      expect(metrics.performance).toHaveProperty('p99ResponseTime');

      expect(metrics.errors).toHaveProperty('total');
      expect(metrics.errors).toHaveProperty('last24h');
      expect(metrics.errors).toHaveProperty('critical');
    });
  });

  describe('Permission Checks', () => {
    it('should deny access to non-admin users', async () => {
      const caller = adminSecurityRouter.createCaller(mockUserContext);

      try {
        await caller.enableTwoFactor();
        expect.fail('Should have thrown FORBIDDEN error');
      } catch (error) {
        expect(error).toBeInstanceOf(TRPCError);
        expect((error as TRPCError<any>).code).toBe('FORBIDDEN');
      }
    });
  });
});
