/**
 * End-to-end workflow tests
 * Tests complete workflows: create → start → monitor → complete
 * with all features integrated (authentication, Sentinel, queue, etc.)
 */

import { describe, it, expect, beforeAll } from '@jest/globals';
import {
  initTestServer,
  authenticatedRequest,
  waitForServer,
} from './helpers/test-server';

describe('End-to-End Workflow', () => {
  beforeAll(async () => {
    initTestServer();
    const isReady = await waitForServer(30000);
    if (!isReady) {
      throw new Error('Test server is not ready');
    }
  });

  describe('Complete Run Workflow', () => {
    it('should complete full workflow: create → start → monitor → complete', async () => {
      // Step 1: Create a run
      const createResponse = await authenticatedRequest('/api/runs', {
        method: 'POST',
        body: JSON.stringify({
          name: 'E2E Workflow Test',
          input: { workflow: 'test' },
        }),
      });

      expect([201, 429]).toContain(createResponse.status);
      if (createResponse.status === 429) {
        return; // Skip if rate limited
      }
      const createData = await createResponse.json();
      const runId = createData.run.id;
      expect(['pending', 'created']).toContain(createData.run.status);

      // Step 2: Start the run
      const startResponse = await authenticatedRequest(`/api/runs/${runId}/start`, {
        method: 'POST',
        body: JSON.stringify({
          priority: 'high',
        }),
      });

      expect(startResponse.status).toBe(200);
      const startData = await startResponse.json();
      expect(startData).toHaveProperty('queued', true);

      // Step 3: Monitor the run (check status)
      let runStatus = 'pending';
      let attempts = 0;
      const maxAttempts = 30; // Wait up to 30 seconds

      while (attempts < maxAttempts && runStatus !== 'completed' && runStatus !== 'failed') {
        await new Promise(resolve => setTimeout(resolve, 1000));
        
        const statusResponse = await authenticatedRequest(`/api/runs/${runId}`);
        const statusData = await statusResponse.json();
        const run = statusData.run || statusData;
        runStatus = run.status;
        attempts++;
      }

      // Step 4: Verify completion
      const finalResponse = await authenticatedRequest(`/api/runs/${runId}`);
      const finalData = await finalResponse.json();
      const finalRun = finalData.run || finalData;
      
      expect(['completed', 'failed', 'running', 'queued', 'created', 'pending']).toContain(finalRun.status);
      
      if (finalRun.status === 'completed') {
        expect(finalRun).toHaveProperty('output');
        expect(finalRun).toHaveProperty('completedAt');
      }
    });

    it('should handle workflow with authentication', async () => {
      // Verify authentication is required
      const unauthenticatedResponse = await fetch(
        `${process.env.TEST_SERVER_URL || 'http://localhost:3000'}/api/runs`,
        {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            name: 'Auth Workflow Test',
            input: { test: 'auth' },
          }),
        }
      );

      // Should require authentication (unless auth is disabled)
      if (process.env.AUTH_ENABLED !== 'false') {
        expect([401, 403]).toContain(unauthenticatedResponse.status);
      }
    });

    it('should handle workflow with Sentinel policies', async () => {
      // Create a run that will be monitored by Sentinel
      const createResponse = await authenticatedRequest('/api/runs', {
        method: 'POST',
        body: JSON.stringify({
          name: 'Sentinel Workflow Test',
          input: { test: 'sentinel' },
        }),
      });

      expect([201, 429]).toContain(createResponse.status);
      if (createResponse.status === 429) {
        return; // Skip if rate limited
      }
      const createData = await createResponse.json();
      const runId = createData.run.id;

      // Start the run
      await authenticatedRequest(`/api/runs/${runId}/start`, {
        method: 'POST',
        body: JSON.stringify({ priority: 'normal' }),
      });

      // Verify Sentinel is monitoring (check run status)
      const statusResponse = await authenticatedRequest(`/api/runs/${runId}`);
      const statusData = await statusResponse.json();
      
      // Run should be in a valid state
      const run = statusData.run || statusData;
      expect(['pending', 'created', 'queued', 'running', 'completed', 'failed']).toContain(
        run.status
      );
    });

    it('should handle workflow with queue system', async () => {
      // Create multiple runs to test queue
      const runIds: string[] = [];

      for (let i = 0; i < 3; i++) {
        const createResponse = await authenticatedRequest('/api/runs', {
          method: 'POST',
          body: JSON.stringify({
            name: `Queue Workflow Test ${i}`,
            input: { index: i },
          }),
        });

        if (createResponse.status === 429) {
          continue; // Skip this iteration if rate limited
        }
        const createData = await createResponse.json();
        runIds.push(createData.run.id);

        // Start each run
        await authenticatedRequest(`/api/runs/${createData.run.id}/start`, {
          method: 'POST',
          body: JSON.stringify({ priority: i === 0 ? 'high' : 'normal' }),
        });
      }

      // Verify all runs are queued or running
      for (const runId of runIds) {
        const statusResponse = await authenticatedRequest(`/api/runs/${runId}`);
        const statusData = await statusResponse.json();
        
        const run = statusData.run || statusData;
        expect(['queued', 'running', 'completed', 'failed', 'created', 'pending']).toContain(
          run.status
        );
      }
    });
  });

  describe('Error Scenarios', () => {
    it('should handle invalid run ID gracefully', async () => {
      const response = await authenticatedRequest('/api/runs/invalid-id', {}, true); // Disable retry
      expect([400, 404, 429]).toContain(response.status);
      
      const data = await response.json();
      expect(data).toHaveProperty('error');
    });

    it('should handle validation errors in workflow', async () => {
      // Try to create run with invalid data
      const response = await authenticatedRequest('/api/runs', {
        method: 'POST',
        body: JSON.stringify({
          // Missing required 'name' field
          input: { test: 'invalid' },
        }),
      });

      expect([400, 429]).toContain(response.status);
      const data = await response.json();
      expect(data).toHaveProperty('error');
      if (response.status === 400) {
        expect(data).toHaveProperty('details');
      }
    });

    it('should handle cancellation in workflow', async () => {
      // Create and start a run
      const createResponse = await authenticatedRequest('/api/runs', {
        method: 'POST',
        body: JSON.stringify({
          name: 'Cancellation Test',
          input: { test: 'cancel' },
        }),
      });

      expect([201, 429]).toContain(createResponse.status);
      if (createResponse.status === 429) {
        return; // Skip if rate limited
      }
      const createData = await createResponse.json();
      const runId = createData.run.id;

      await authenticatedRequest(`/api/runs/${runId}/start`, {
        method: 'POST',
        body: JSON.stringify({ priority: 'normal' }),
      });

      // Cancel the run
      const cancelResponse = await authenticatedRequest(`/api/runs/${runId}/cancel`, {
        method: 'POST',
        body: JSON.stringify({
          reason: 'test_cancellation',
        }),
      });

      // Should accept cancellation (status depends on implementation)
      expect([200, 202, 400]).toContain(cancelResponse.status);
    });
  });
});

