import { describe, it, expect, beforeEach } from 'vitest';
import { escalationPolicyService } from './escalationPolicyService.ts';

describe('EscalationPolicyService', () => {
  beforeEach(() => {
    // Service is initialized with default policies in constructor
  });

  describe('Policy Management', () => {
    it('should list default policies', () => {
      const policies = escalationPolicyService.listPolicies();
      expect(policies.length).toBeGreaterThan(0);
    });

    it('should create new policy', () => {
      const policy = escalationPolicyService.createPolicy({
        name: 'Test Policy',
        description: 'Test escalation policy',
        alertTypes: ['test_alert'],
        steps: [
          {
            step: 1,
            minutesAfterAlert: 0,
            channels: ['slack'],
            notifyUsers: ['test-user'],
          },
        ],
        enabled: true,
      });

      expect(policy.id).toBeDefined();
      expect(policy.name).toBe('Test Policy');
      expect(policy.createdAt).toBeInstanceOf(Date);
    });

    it('should get policy by ID', () => {
      const policies = escalationPolicyService.listPolicies();
      const policyId = policies[0].id;

      const policy = escalationPolicyService.getPolicy(policyId);
      expect(policy).toBeDefined();
      expect(policy?.id).toBe(policyId);
    });

    it('should get policies for alert type', () => {
      const policies = escalationPolicyService.getPoliciesForAlertType('latency_critical');
      expect(policies.length).toBeGreaterThan(0);
      expect(policies.every((p) => p.alertTypes.includes('latency_critical'))).toBe(true);
    });

    it('should update policy', () => {
      const policies = escalationPolicyService.listPolicies();
      const policyId = policies[0].id;

      const updated = escalationPolicyService.updatePolicy(policyId, {
        enabled: false,
      });

      expect(updated?.enabled).toBe(false);
      expect(updated?.updatedAt).toBeInstanceOf(Date);
    });

    it('should delete policy', () => {
      const policy = escalationPolicyService.createPolicy({
        name: 'Delete Test',
        description: 'Policy to delete',
        alertTypes: ['test'],
        steps: [],
        enabled: true,
      });

      const deleted = escalationPolicyService.deletePolicy(policy.id);
      expect(deleted).toBe(true);

      const retrieved = escalationPolicyService.getPolicy(policy.id);
      expect(retrieved).toBeUndefined();
    });
  });

  describe('Runbook Management', () => {
    it('should list default runbooks', () => {
      const runbooks = escalationPolicyService.listRunbooks();
      expect(runbooks.length).toBeGreaterThan(0);
    });

    it('should create new runbook', () => {
      const runbook = escalationPolicyService.createRunbook({
        alertType: 'test_alert',
        title: 'Test Runbook',
        description: 'Test runbook description',
        steps: ['Step 1', 'Step 2', 'Step 3'],
        estimatedResolutionTime: 10,
        tags: ['test'],
      });

      expect(runbook.id).toBeDefined();
      expect(runbook.title).toBe('Test Runbook');
      expect(runbook.steps).toHaveLength(3);
    });

    it('should get runbook by ID', () => {
      const runbooks = escalationPolicyService.listRunbooks();
      const runbookId = runbooks[0].id;

      const runbook = escalationPolicyService.getRunbook(runbookId);
      expect(runbook).toBeDefined();
      expect(runbook?.id).toBe(runbookId);
    });

    it('should get runbook for alert type', () => {
      const runbook = escalationPolicyService.getRunbookForAlertType('latency_critical');
      expect(runbook).toBeDefined();
      expect(runbook?.alertType).toBe('latency_critical');
    });

    it('should update runbook', () => {
      const runbooks = escalationPolicyService.listRunbooks();
      const runbookId = runbooks[0].id;

      const updated = escalationPolicyService.updateRunbook(runbookId, {
        enabled: false,
      });

      expect(updated?.enabled).toBe(false);
    });

    it('should delete runbook', () => {
      const runbook = escalationPolicyService.createRunbook({
        alertType: 'test_alert',
        title: 'Delete Test',
        description: 'Runbook to delete',
        steps: [],
        estimatedResolutionTime: 5,
        tags: [],
      });

      const deleted = escalationPolicyService.deleteRunbook(runbook.id);
      expect(deleted).toBe(true);

      const retrieved = escalationPolicyService.getRunbook(runbook.id);
      expect(retrieved).toBeUndefined();
    });
  });

  describe('Escalation Execution', () => {
    it('should start escalation', () => {
      const policies = escalationPolicyService.getPoliciesForAlertType('latency_critical');
      const policyId = policies[0].id;

      const escalation = escalationPolicyService.startEscalation('alert-123', 'latency_critical', policyId);
      expect(escalation).toBeDefined();
      expect(escalation?.alertId).toBe('alert-123');
      expect(escalation?.currentStep).toBe(1);
      expect(escalation?.status).toBe('active');
    });

    it('should get active escalation', () => {
      const policies = escalationPolicyService.getPoliciesForAlertType('latency_critical');
      const policyId = policies[0].id;

      const escalation = escalationPolicyService.startEscalation('alert-456', 'latency_critical', policyId);
      const escalationId = escalation!.id;

      const retrieved = escalationPolicyService.getActiveEscalation(escalationId);
      expect(retrieved).toBeDefined();
      expect(retrieved?.id).toBe(escalationId);
    });

    it('should get escalations for alert', () => {
      const policies = escalationPolicyService.getPoliciesForAlertType('latency_critical');
      const policyId = policies[0].id;

      escalationPolicyService.startEscalation('alert-789', 'latency_critical', policyId);
      escalationPolicyService.startEscalation('alert-789', 'latency_critical', policyId);

      const escalations = escalationPolicyService.getEscalationsForAlert('alert-789');
      expect(escalations.length).toBeGreaterThanOrEqual(2);
      expect(escalations.every((e) => e.alertId === 'alert-789')).toBe(true);
    });

    it('should acknowledge escalation', () => {
      const policies = escalationPolicyService.getPoliciesForAlertType('latency_critical');
      const policyId = policies[0].id;

      const escalation = escalationPolicyService.startEscalation('alert-ack', 'latency_critical', policyId);
      const escalationId = escalation!.id;

      const acknowledged = escalationPolicyService.acknowledgeEscalation(escalationId);
      expect(acknowledged?.status).toBe('acknowledged');
    });

    it('should resolve escalation', () => {
      const policies = escalationPolicyService.getPoliciesForAlertType('latency_critical');
      const policyId = policies[0].id;

      const escalation = escalationPolicyService.startEscalation('alert-resolve', 'latency_critical', policyId);
      const escalationId = escalation!.id;

      const resolved = escalationPolicyService.resolveEscalation(escalationId);
      expect(resolved?.status).toBe('resolved');

      const retrieved = escalationPolicyService.getActiveEscalation(escalationId);
      expect(retrieved).toBeUndefined();
    });

    it('should list active escalations', () => {
      const policies = escalationPolicyService.getPoliciesForAlertType('latency_critical');
      const policyId = policies[0].id;

      escalationPolicyService.startEscalation('alert-active-1', 'latency_critical', policyId);
      escalationPolicyService.startEscalation('alert-active-2', 'latency_critical', policyId);

      const active = escalationPolicyService.listActiveEscalations();
      expect(active.length).toBeGreaterThan(0);
      expect(active.every((e) => e.status === 'active')).toBe(true);
    });
  });

  describe('Escalation Steps', () => {
    it('should execute next escalation step', () => {
      const policies = escalationPolicyService.getPoliciesForAlertType('latency_critical');
      const policyId = policies[0].id;

      const escalation = escalationPolicyService.startEscalation('alert-step', 'latency_critical', policyId);
      const escalationId = escalation!.id;

      const result = escalationPolicyService.executeNextStep(escalationId);
      expect(result.success).toBe(true);
    });

    it('should get escalations due for next step', () => {
      const policies = escalationPolicyService.getPoliciesForAlertType('latency_critical');
      const policyId = policies[0].id;

      // Create escalation with past next escalation time
      const escalation = escalationPolicyService.startEscalation('alert-due', 'latency_critical', policyId);
      if (escalation) {
        escalation.nextEscalationAt = new Date(Date.now() - 1000); // 1 second in the past
      }

      const due = escalationPolicyService.getEscalationsDueForNextStep();
      expect(due.length).toBeGreaterThan(0);
    });
  });

  describe('Default Policies and Runbooks', () => {
    it('should have latency critical policy', () => {
      const policies = escalationPolicyService.getPoliciesForAlertType('latency_critical');
      expect(policies.length).toBeGreaterThan(0);
      expect(policies[0].name).toContain('Latency');
    });

    it('should have delivery failure policy', () => {
      const policies = escalationPolicyService.getPoliciesForAlertType('delivery_failure');
      expect(policies.length).toBeGreaterThan(0);
      expect(policies[0].name).toContain('Delivery');
    });

    it('should have forecast accuracy policy', () => {
      const policies = escalationPolicyService.getPoliciesForAlertType('forecast_accuracy_degrading');
      expect(policies.length).toBeGreaterThan(0);
      expect(policies[0].name).toContain('Forecast');
    });

    it('should have latency critical runbook', () => {
      const runbook = escalationPolicyService.getRunbookForAlertType('latency_critical');
      expect(runbook).toBeDefined();
      expect(runbook?.title).toContain('Latency');
      expect(runbook?.steps.length).toBeGreaterThan(0);
    });

    it('should have delivery failure runbook', () => {
      const runbook = escalationPolicyService.getRunbookForAlertType('delivery_failure');
      expect(runbook).toBeDefined();
      expect(runbook?.title).toContain('Delivery');
    });

    it('should have forecast accuracy runbook', () => {
      const runbook = escalationPolicyService.getRunbookForAlertType('forecast_accuracy_degrading');
      expect(runbook).toBeDefined();
      expect(runbook?.title).toContain('Forecast');
    });
  });
});
