/**
 * End-to-End Generation Tests
 * Tests the complete workflow generation process from YAML to generated files
 */

import * as fs from 'fs';
import * as path from 'path';
import { GenerationOrchestrator } from './generation-orchestrator';
import { Workflow } from '../types/workflow';

describe('End-to-End Generation Tests', () => {
  const testOutputDir = '/tmp/bangstack-e2e-test';

  beforeEach(() => {
    // Clean up test directory
    if (fs.existsSync(testOutputDir)) {
      fs.rmSync(testOutputDir, { recursive: true });
    }
    fs.mkdirSync(testOutputDir, { recursive: true });
  });

  afterEach(() => {
    // Clean up after tests
    if (fs.existsSync(testOutputDir)) {
      fs.rmSync(testOutputDir, { recursive: true });
    }
  });

  it('should generate infrastructure for full-stack application', async () => {
    const orchestrator = new GenerationOrchestrator();

    const workflow: Workflow = {
      workflow: {
        id: 'e2e-test-app',
        name: 'E2E Test Application',
        version: '1.0.0',
        description: 'Complete test application for end-to-end verification',
      },
      stack: {
        frontend: {
          type: 'frontend',
          technology: 'react',
          components: [
            {
              id: 'login-form',
              name: 'LoginForm',
              type: 'form-component',
              description: 'User login form',
              state: [
                { name: 'email', type: 'string', initial: '' },
                { name: 'password', type: 'string', initial: '' },
              ],
              actions: [
                {
                  id: 'handle-submit',
                  trigger: 'form-submit',
                  description: 'Handle login form submission',
                  steps: [
                    {
                      id: 'call-api',
                      action: 'api-call',
                      api_workflow: 'backend.login',
                      payload: {
                        email: '{{ state.email }}',
                        password: '{{ state.password }}',
                      },
                    },
                  ],
                },
              ],
            },
          ],
        },
        backend: {
          type: 'backend',
          technology: 'typescript/express',
          endpoints: [
            {
              id: 'login',
              path: '/api/auth/login',
              method: 'POST',
              name: 'Login',
              description: 'User login endpoint',
              responses: [
                { status: 200, description: 'Login successful' },
                { status: 401, description: 'Invalid credentials' },
              ],
              implementation: {
                steps: [
                  { id: 'validate', action: 'validate-schema' },
                  { id: 'authenticate', action: 'authenticate-user' },
                  { id: 'respond', action: 'respond' },
                ],
              },
            },
          ],
        },
      },
    };

    const result = await orchestrator.generate(workflow, {
      outputDirectory: testOutputDir,
      dryRun: false,
      noCheckpoint: true, // Disable checkpointing for tests
    });

    expect(result.files.length).toBeGreaterThan(0);

    // Write all generated files
    for (const file of result.files) {
      const filePath = file.path;
      const dir = path.dirname(filePath);
      if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
      }
      fs.writeFileSync(filePath, file.content);
    }

    // Verify infrastructure directories
    const frontendDir = path.join(testOutputDir, 'frontend');
    expect(fs.existsSync(frontendDir)).toBe(true);

    const backendDir = path.join(testOutputDir, 'backend');
    expect(fs.existsSync(backendDir)).toBe(true);

    // Verify Docker files
    const frontendDockerfile = path.join(frontendDir, 'Dockerfile');
    expect(fs.existsSync(frontendDockerfile)).toBe(true);

    const backendDockerfile = path.join(backendDir, 'Dockerfile');
    expect(fs.existsSync(backendDockerfile)).toBe(true);

    const dockerCompose = path.join(testOutputDir, 'docker-compose.yml');
    expect(fs.existsSync(dockerCompose)).toBe(true);

    // Verify manifest exists and is valid JSON
    const manifestPath = path.join(testOutputDir, '.bangstack/manifest.json');
    expect(fs.existsSync(manifestPath)).toBe(true);
    const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
    expect(manifest).toBeDefined();
    expect(typeof manifest).toBe('object');
  }, 30000); // 30 second timeout for full generation

  it('should generate application infrastructure with database', async () => {
    const orchestrator = new GenerationOrchestrator();

    const workflow: Workflow = {
      workflow: {
        id: 'task-manager',
        name: 'Task Manager',
        version: '1.0.0',
        description: 'Task management application',
      },
      stack: {
        backend: {
          type: 'backend',
          technology: 'typescript/express',
          endpoints: [
            {
              id: 'create-task',
              path: '/api/tasks',
              method: 'POST',
              name: 'Create Task',
              description: 'Create a new task',
              responses: [{ status: 201, description: 'Task created' }],
            },
          ],
        },
        database: {
          type: 'database',
          technology: 'postgresql',
          tables: [
            {
              name: 'tasks',
              description: 'Tasks table',
              required_fields: [
                { name: 'id', type: 'uuid', primary_key: true },
                { name: 'title', type: 'string' },
                { name: 'completed', type: 'boolean' },
              ],
            },
          ],
        },
      },
    };

    const result = await orchestrator.generate(workflow, {
      outputDirectory: testOutputDir,
      dryRun: false,
      noCheckpoint: true, // Disable checkpointing for tests
    });

    // Write files
    for (const file of result.files) {
      const filePath = file.path;
      const dir = path.dirname(filePath);
      if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
      }
      fs.writeFileSync(filePath, file.content);
    }

    // Verify Docker files exist
    const dockerComposePath = path.join(testOutputDir, 'docker-compose.yml');
    expect(fs.existsSync(dockerComposePath)).toBe(true);

    // Verify database service is configured
    const dockerComposeContent = fs.readFileSync(dockerComposePath, 'utf-8');
    expect(dockerComposeContent).toContain('postgres');

    // Verify manifest
    const manifestPath = path.join(testOutputDir, '.bangstack/manifest.json');
    expect(fs.existsSync(manifestPath)).toBe(true);
  }, 30000);

  it('should generate application with Docker configuration', async () => {
    const orchestrator = new GenerationOrchestrator();

    const workflow: Workflow = {
      workflow: {
        id: 'dockerized-app',
        name: 'Dockerized App',
        version: '1.0.0',
        description: 'Application with Docker',
      },
      stack: {
        backend: {
          type: 'backend',
          technology: 'typescript/express',
          endpoints: [
            {
              id: 'health',
              path: '/health',
              method: 'GET',
              name: 'Health Check',
              description: 'Health check endpoint',
              responses: [{ status: 200, description: 'Healthy' }],
            },
          ],
        },
      },
    };

    const result = await orchestrator.generate(workflow, {
      outputDirectory: testOutputDir,
      dryRun: false,
      noCheckpoint: true, // Disable checkpointing for tests
    });

    // Write files
    for (const file of result.files) {
      const filePath = file.path;
      const dir = path.dirname(filePath);
      if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
      }
      fs.writeFileSync(filePath, file.content);
    }

    // Verify Docker files
    const dockerComposePath = path.join(testOutputDir, 'docker-compose.yml');
    expect(fs.existsSync(dockerComposePath)).toBe(true);
    const dockerComposeContent = fs.readFileSync(dockerComposePath, 'utf-8');
    expect(dockerComposeContent).toContain('version:');
    expect(dockerComposeContent).toContain('services:');

    // Verify backend Dockerfile
    const backendDockerfile = path.join(testOutputDir, 'backend/Dockerfile');
    expect(fs.existsSync(backendDockerfile)).toBe(true);
    const dockerfileContent = fs.readFileSync(backendDockerfile, 'utf-8');
    expect(dockerfileContent).toContain('FROM node:');
  }, 30000);

  it('should generate infrastructure files for frontend and backend', async () => {
    const orchestrator = new GenerationOrchestrator();

    const workflow: Workflow = {
      workflow: {
        id: 'count-test',
        name: 'File Count Test',
        version: '1.0.0',
        description: 'Verify file count',
      },
      stack: {
        frontend: {
          type: 'frontend',
          technology: 'react',
          components: [
            {
              id: 'test-component',
              name: 'TestComponent',
              type: 'simple-component',
              description: 'Test',
            },
          ],
        },
        backend: {
          type: 'backend',
          technology: 'typescript/express',
          endpoints: [
            {
              id: 'test-endpoint',
              path: '/api/test',
              method: 'GET',
              name: 'Test',
              description: 'Test endpoint',
              responses: [{ status: 200, description: 'OK' }],
            },
          ],
        },
      },
    };

    const result = await orchestrator.generate(workflow, {
      outputDirectory: testOutputDir,
      dryRun: false,
      noCheckpoint: true, // Disable checkpointing for tests
    });

    // Should generate infrastructure files
    expect(result.files.length).toBeGreaterThan(0);

    // Write files and verify they exist
    for (const file of result.files) {
      const filePath = file.path;
      const dir = path.dirname(filePath);
      if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
      }
      fs.writeFileSync(filePath, file.content);
      expect(fs.existsSync(filePath)).toBe(true);
    }

    // Verify core infrastructure exists
    expect(fs.existsSync(path.join(testOutputDir, 'docker-compose.yml'))).toBe(true);
    expect(fs.existsSync(path.join(testOutputDir, 'frontend/Dockerfile'))).toBe(true);
    expect(fs.existsSync(path.join(testOutputDir, 'backend/Dockerfile'))).toBe(true);
    expect(fs.existsSync(path.join(testOutputDir, '.bangstack/manifest.json'))).toBe(true);
  }, 30000);

  it('should handle minimal backend-only workflow', async () => {
    const orchestrator = new GenerationOrchestrator();

    const workflow: Workflow = {
      workflow: {
        id: 'minimal-api',
        name: 'Minimal API',
        version: '1.0.0',
        description: 'Minimal backend API',
      },
      stack: {
        backend: {
          type: 'backend',
          technology: 'typescript/express',
          endpoints: [
            {
              id: 'ping',
              path: '/ping',
              method: 'GET',
              name: 'Ping',
              description: 'Simple ping endpoint',
              responses: [{ status: 200, description: 'Pong' }],
            },
          ],
        },
      },
    };

    const result = await orchestrator.generate(workflow, {
      outputDirectory: testOutputDir,
      dryRun: false,
      noCheckpoint: true,
    });

    // Write files
    for (const file of result.files) {
      const filePath = file.path;
      const dir = path.dirname(filePath);
      if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
      }
      fs.writeFileSync(filePath, file.content);
    }

    // Should have at least backend Docker files
    expect(fs.existsSync(path.join(testOutputDir, 'backend/Dockerfile'))).toBe(true);
    expect(fs.existsSync(path.join(testOutputDir, '.bangstack/manifest.json'))).toBe(true);

    // Should generate at least a few files
    expect(result.files.length).toBeGreaterThanOrEqual(3);
  }, 30000);
});
