/**
 * Tests for TypeScriptExpressGenerator
 */

import { TypeScriptExpressGenerator } from './typescript-express-generator';
import { WorkflowParser } from '../../parser/workflow-parser';
import { WorkflowEngine } from '../../engine/workflow-engine';
import * as path from 'path';

describe('TypeScriptExpressGenerator', () => {
  let generator: TypeScriptExpressGenerator;

  beforeEach(() => {
    generator = new TypeScriptExpressGenerator();
  });

  it('should have correct metadata', () => {
    expect(generator.id).toBe('typescript-express-backend');
    expect(generator.name).toBe('TypeScript/Express Backend Generator');
    expect(generator.language).toBe('typescript');
    expect(generator.framework).toBe('express');
  });

  it('should generate empty result for workflow without backend components', () => {
    const workflow = {
      workflow: {
        id: 'test',
        name: 'Test',
        version: '1.0.0',
        description: 'Test workflow',
      },
      stack: {
        'web-app': {
          type: 'frontend',
          technology: 'react',
        },
      },
    };

    const engine = new WorkflowEngine();
    const executionPlan = engine.analyze(workflow as any);

    const result = generator.generate({
      workflow: workflow as any,
      executionPlan,
      outputDirectory: '/test',
    });

    expect(result.files).toHaveLength(0);
  });

  it('should generate Express API from user-authentication workflow', async () => {
    const parser = new WorkflowParser();
    const workflowPath = path.join(__dirname, '../../../examples/user-authentication.workflow.yaml');
    const result = await parser.parseFile(workflowPath);

    const engine = new WorkflowEngine();
    const executionPlan = engine.analyze(result.workflow);

    const genResult = generator.generate({
      workflow: result.workflow,
      executionPlan,
      outputDirectory: '/test',
    });

    // Should generate multiple files
    expect(genResult.files.length).toBeGreaterThan(0);

    // Should generate route handler
    const routeFile = genResult.files.find(f => f.path.includes('login-endpoint.ts') && f.path.includes('routes'));
    expect(routeFile).toBeDefined();
    expect(routeFile?.content).toContain('router.post');
    expect(routeFile?.content).toContain('bcrypt');
    expect(routeFile?.content).toContain('jwt');

    // Should generate types file
    const typesFile = genResult.files.find(f => f.path.includes('login-endpoint.types.ts'));
    expect(typesFile).toBeDefined();
    expect(typesFile?.content).toContain('LoginEndpointRequest');
    expect(typesFile?.content).toContain('email');
    expect(typesFile?.content).toContain('password');

    // Should generate routes index
    const routesIndex = genResult.files.find(f => f.path.includes('routes/index.ts'));
    expect(routesIndex).toBeDefined();
    expect(routesIndex?.content).toContain('import');

    // Should generate database client
    const dbClient = genResult.files.find(f => f.path.includes('database/client.ts'));
    expect(dbClient).toBeDefined();
    expect(dbClient?.content).toContain('Pool');
    expect(dbClient?.content).toContain('pg');

    // Should generate middleware
    const middleware = genResult.files.find(f => f.path.includes('middleware/index.ts'));
    expect(middleware).toBeDefined();
    expect(middleware?.content).toContain('authenticate');
    expect(middleware?.content).toContain('errorHandler');

    // Should generate server file
    const serverFile = genResult.files.find(f => f.path.includes('src/index.ts'));
    expect(serverFile).toBeDefined();
    expect(serverFile?.content).toContain('express()');
    expect(serverFile?.content).toContain('app.listen');

    // Should generate package.json
    const pkgFile = genResult.files.find(f => f.path.includes('package.json'));
    expect(pkgFile).toBeDefined();
    const pkg = JSON.parse(pkgFile?.content || '{}');
    expect(pkg.dependencies.express).toBeDefined();
    expect(pkg.dependencies.pg).toBeDefined();
    expect(pkg.dependencies.bcrypt).toBeDefined();
    expect(pkg.dependencies.jsonwebtoken).toBeDefined();

    // Should generate .env.example
    const envFile = genResult.files.find(f => f.path.includes('.env.example'));
    expect(envFile).toBeDefined();
    expect(envFile?.content).toContain('DATABASE_URL');
    expect(envFile?.content).toContain('JWT_SECRET');
  });

  it('should generate endpoint with request/response types', () => {
    const workflow = {
      workflow: {
        id: 'test',
        name: 'Test',
        version: '1.0.0',
        description: 'Test',
      },
      stack: {
        api: {
          type: 'backend',
          technology: 'typescript/express',
          endpoints: [
            {
              id: 'test-endpoint',
              path: '/api/test',
              method: 'POST',
              request: {
                body: {
                  type: 'object',
                  required: ['name'],
                  properties: {
                    name: { type: 'string' },
                    age: { type: 'number' },
                  },
                },
              },
              responses: [
                {
                  status: 200,
                  body: {
                    type: 'object',
                    properties: {
                      id: { type: 'string' },
                      name: { type: 'string' },
                    },
                  },
                },
              ],
            },
          ],
        },
      },
    };

    const engine = new WorkflowEngine();
    const executionPlan = engine.analyze(workflow as any);

    const result = generator.generate({
      workflow: workflow as any,
      executionPlan,
      outputDirectory: '/test',
    });

    const typesFile = result.files.find(f => f.path.includes('test-endpoint.types.ts'));
    expect(typesFile).toBeDefined();
    expect(typesFile?.content).toContain('export interface TestEndpointRequest');
    expect(typesFile?.content).toContain('name: string');
    expect(typesFile?.content).toContain('age?: number');
    expect(typesFile?.content).toContain('export interface TestEndpointResponse');
    expect(typesFile?.content).toContain('id: string');
  });

  it('should generate route with correct HTTP method', () => {
    const workflow = {
      workflow: {
        id: 'test',
        name: 'Test',
        version: '1.0.0',
        description: 'Test',
      },
      stack: {
        api: {
          type: 'backend',
          technology: 'typescript/express',
          endpoints: [
            {
              id: 'get-users',
              path: '/api/users',
              method: 'GET',
            },
            {
              id: 'create-user',
              path: '/api/users',
              method: 'POST',
            },
          ],
        },
      },
    };

    const engine = new WorkflowEngine();
    const executionPlan = engine.analyze(workflow as any);

    const result = generator.generate({
      workflow: workflow as any,
      executionPlan,
      outputDirectory: '/test',
    });

    const getRoute = result.files.find(f => f.path.includes('get-users.ts'));
    expect(getRoute).toBeDefined();
    expect(getRoute?.content).toContain('router.get');

    const postRoute = result.files.find(f => f.path.includes('create-user.ts'));
    expect(postRoute).toBeDefined();
    expect(postRoute?.content).toContain('router.post');
  });

  it('should generate implementation steps in correct order', () => {
    const workflow = {
      workflow: {
        id: 'test',
        name: 'Test',
        version: '1.0.0',
        description: 'Test',
      },
      stack: {
        api: {
          type: 'backend',
          technology: 'typescript/express',
          endpoints: [
            {
              id: 'login',
              path: '/api/login',
              method: 'POST',
              implementation: {
                steps: [
                  {
                    id: 'validate-request',
                    action: 'validate-schema',
                  },
                  {
                    id: 'find-user',
                    action: 'database-query',
                    depends_on: ['validate-request'],
                    database: 'users',
                    query: {
                      type: 'findOne',
                      filter: { email: '{{ request.body.email }}' },
                    },
                  },
                  {
                    id: 'respond',
                    action: 'respond',
                    depends_on: ['find-user'],
                    status: 200,
                    response: { success: true },
                  },
                ],
              },
            },
          ],
        },
      },
    };

    const engine = new WorkflowEngine();
    const executionPlan = engine.analyze(workflow as any);

    const result = generator.generate({
      workflow: workflow as any,
      executionPlan,
      outputDirectory: '/test',
    });

    const routeFile = result.files.find(f => f.path.includes('login.ts'));
    expect(routeFile).toBeDefined();
    expect(routeFile?.content).toContain('Validate request schema');
    expect(routeFile?.content).toContain('find-user');
    expect(routeFile?.content).toContain('db.query');
    expect(routeFile?.content).toContain('return res.status(200)');
  });

  it('should include bcrypt for password comparison', () => {
    const workflow = {
      workflow: {
        id: 'test',
        name: 'Test',
        version: '1.0.0',
        description: 'Test',
      },
      stack: {
        api: {
          type: 'backend',
          technology: 'typescript/express',
          endpoints: [
            {
              id: 'login',
              path: '/api/login',
              method: 'POST',
              implementation: {
                steps: [
                  {
                    id: 'verify-password',
                    action: 'crypto-compare',
                    implementation: 'bcrypt',
                    compare: {
                      plain: '{{ request.body.password }}',
                      hashed: '{{ user.passwordHash }}',
                    },
                  },
                ],
              },
            },
          ],
        },
      },
    };

    const engine = new WorkflowEngine();
    const executionPlan = engine.analyze(workflow as any);

    const result = generator.generate({
      workflow: workflow as any,
      executionPlan,
      outputDirectory: '/test',
    });

    const routeFile = result.files.find(f => f.path.includes('login.ts'));
    expect(routeFile).toBeDefined();
    expect(routeFile?.content).toContain("import bcrypt from 'bcrypt'");
    expect(routeFile?.content).toContain('bcrypt.compare');
  });

  it('should include JWT for token generation', () => {
    const workflow = {
      workflow: {
        id: 'test',
        name: 'Test',
        version: '1.0.0',
        description: 'Test',
      },
      stack: {
        api: {
          type: 'backend',
          technology: 'typescript/express',
          endpoints: [
            {
              id: 'login',
              path: '/api/login',
              method: 'POST',
              implementation: {
                steps: [
                  {
                    id: 'generate-token',
                    action: 'generate-jwt',
                    implementation: 'jsonwebtoken',
                    payload: {
                      userId: '{{ user.id }}',
                    },
                    expiresIn: '7d',
                  },
                ],
              },
            },
          ],
        },
      },
    };

    const engine = new WorkflowEngine();
    const executionPlan = engine.analyze(workflow as any);

    const result = generator.generate({
      workflow: workflow as any,
      executionPlan,
      outputDirectory: '/test',
    });

    const routeFile = result.files.find(f => f.path.includes('login.ts'));
    expect(routeFile).toBeDefined();
    expect(routeFile?.content).toContain("import jwt from 'jsonwebtoken'");
    expect(routeFile?.content).toContain('jwt.sign');
    expect(routeFile?.content).toContain('expiresIn:');
  });

  it('should not overwrite package.json and tsconfig.json', () => {
    const workflow = {
      workflow: {
        id: 'test',
        name: 'Test',
        version: '1.0.0',
        description: 'Test',
      },
      stack: {
        api: {
          type: 'backend',
          technology: 'typescript/express',
          endpoints: [],
        },
      },
    };

    const engine = new WorkflowEngine();
    const executionPlan = engine.analyze(workflow as any);

    const result = generator.generate({
      workflow: workflow as any,
      executionPlan,
      outputDirectory: '/test',
    });

    const pkgFile = result.files.find(f => f.path.includes('package.json'));
    expect(pkgFile?.overwrite).toBe(false);

    const tsConfigFile = result.files.find(f => f.path.includes('tsconfig.json'));
    expect(tsConfigFile?.overwrite).toBe(false);
  });
});
