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

describe('PolicyService', () => {
  let service: PolicyService;
  let originalEnv: NodeJS.ProcessEnv;

  beforeEach(() => {
    originalEnv = process.env;
    process.env = { ...originalEnv };
    service = new PolicyService();
  });

  afterEach(() => {
    process.env = originalEnv;
    vi.restoreAllMocks();
  });

  describe('getSnapshot', () => {
    it('should return cached snapshot when not expired', async () => {
      // Set a long cache time
      process.env.GRIDPILOT_POLICY_CACHE_MS = '60000';

      const snapshot1 = await service.getSnapshot();
      const snapshot2 = await service.getSnapshot();

      expect(snapshot1).toEqual(snapshot2);
      expect(snapshot1.policyVersion).toBeDefined();
      expect(snapshot1.loadedAtIso).toBeDefined();
    });

    it('should return new snapshot when cache expires', async () => {
      // Set cache to 1ms to force expiration
      process.env.GRIDPILOT_POLICY_CACHE_MS = '1';

      const snapshot1 = await service.getSnapshot();
      
      // Wait for cache to expire
      await new Promise(resolve => setTimeout(resolve, 10));
      
      const snapshot2 = await service.getSnapshot();

      expect(snapshot1.loadedAtIso).not.toBe(snapshot2.loadedAtIso);
    });

    it('should load from file when GRIDPILOT_POLICY_PATH is set', async () => {
      // We can't easily mock readFile in this context, so we'll test the default path
      // This test verifies the service structure works
      const snapshot = await service.getSnapshot();
      
      expect(snapshot).toBeDefined();
      expect(snapshot.policyVersion).toBeGreaterThanOrEqual(1);
      expect(snapshot.operationalMode).toBeDefined();
      expect(snapshot.capabilities).toBeDefined();
    });

    it('should use default values when no env vars are set', async () => {
      // Clear all policy-related env vars
      delete process.env.GRIDPILOT_POLICY_PATH;
      delete process.env.GRIDPILOT_POLICY_CACHE_MS;
      delete process.env.GRIDPILOT_OPERATIONAL_MODE;
      delete process.env.GRIDPILOT_MAINTENANCE_ALLOW_VIEW;
      delete process.env.GRIDPILOT_MAINTENANCE_ALLOW_MUTATE;

      const snapshot = await service.getSnapshot();

      expect(snapshot.policyVersion).toBe(1);
      expect(snapshot.operationalMode).toBe('normal');
      expect(snapshot.maintenanceAllowlist.view).toEqual([]);
      expect(snapshot.maintenanceAllowlist.mutate).toEqual([]);
      expect(snapshot.capabilities).toBeDefined();
      expect(snapshot.loadedFrom).toBeDefined();
      expect(snapshot.loadedAtIso).toBeDefined();
    });

    it('should parse operational mode from env', async () => {
      process.env.GRIDPILOT_OPERATIONAL_MODE = 'maintenance';
      process.env.GRIDPILOT_POLICY_CACHE_MS = '5000';

      const snapshot = await service.getSnapshot();

      expect(snapshot.operationalMode).toBe('maintenance');
    });

    it('should handle invalid operational mode gracefully', async () => {
      process.env.GRIDPILOT_OPERATIONAL_MODE = 'invalid-mode';

      const snapshot = await service.getSnapshot();

      expect(snapshot.operationalMode).toBe('normal');
    });

    it('should parse maintenance allowlist from env', async () => {
      process.env.GRIDPILOT_MAINTENANCE_ALLOW_VIEW = 'health, status, api';
      process.env.GRIDPILOT_MAINTENANCE_ALLOW_MUTATE = 'admin, config';

      const snapshot = await service.getSnapshot();

      expect(snapshot.maintenanceAllowlist.view).toEqual(['health', 'status', 'api']);
      expect(snapshot.maintenanceAllowlist.mutate).toEqual(['admin', 'config']);
    });

    it('should handle empty maintenance allowlist', async () => {
      process.env.GRIDPILOT_MAINTENANCE_ALLOW_VIEW = '';
      process.env.GRIDPILOT_MAINTENANCE_ALLOW_MUTATE = '';

      const snapshot = await service.getSnapshot();

      expect(snapshot.maintenanceAllowlist.view).toEqual([]);
      expect(snapshot.maintenanceAllowlist.mutate).toEqual([]);
    });

    it('should handle missing maintenance allowlist', async () => {
      delete process.env.GRIDPILOT_MAINTENANCE_ALLOW_VIEW;
      delete process.env.GRIDPILOT_MAINTENANCE_ALLOW_MUTATE;

      const snapshot = await service.getSnapshot();

      expect(snapshot.maintenanceAllowlist.view).toEqual([]);
      expect(snapshot.maintenanceAllowlist.mutate).toEqual([]);
    });
  });
});