"use strict";
/**
 * TypeScript/Express Backend Generator
 * Generates Express API with TypeScript from workflow definitions
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypeScriptExpressGenerator = void 0;
const path = __importStar(require("path"));
const prisma_schema_builder_1 = require("../database/prisma-schema-builder");
const drizzle_schema_builder_1 = require("../database/drizzle-schema-builder");
class TypeScriptExpressGenerator {
    id = 'typescript-express-backend';
    name = 'TypeScript/Express Backend Generator';
    description = 'Generates Express API with TypeScript';
    language = 'typescript';
    framework = 'express';
    generate(context) {
        const { workflow, extendedWorkflow, outputDirectory } = context;
        const files = [];
        // Use extended workflow if available
        if (extendedWorkflow) {
            return this.generateFromExtendedWorkflow(extendedWorkflow, outputDirectory);
        }
        // Legacy generation from stack components
        // Find all backend components in the stack
        const backendComponents = this.getBackendComponents(workflow);
        if (backendComponents.length === 0) {
            return { files: [] };
        }
        // Generate each backend stack component
        for (const [componentName, component] of backendComponents) {
            // Generate routes for endpoints
            if (component.endpoints) {
                for (const endpoint of component.endpoints) {
                    files.push(...this.generateEndpointFiles(endpoint, componentName, workflow, outputDirectory));
                }
                // Generate main routes index
                files.push(this.generateRoutesIndex(component.endpoints, componentName, outputDirectory));
            }
            // Generate database client
            const databaseComponents = this.getDatabaseComponents(workflow);
            if (databaseComponents.length > 0) {
                files.push(this.generateDatabaseClient(componentName, databaseComponents[0][1], outputDirectory));
            }
            // Generate middleware
            files.push(this.generateMiddleware(componentName, outputDirectory));
            // Generate main server file
            files.push(this.generateServerFile(componentName, component, workflow, outputDirectory));
            // Generate package.json for the backend
            files.push(this.generatePackageJson(componentName, outputDirectory));
            // Generate tsconfig.json
            files.push(this.generateTsConfig(componentName, outputDirectory));
            // Generate .env.example
            files.push(this.generateEnvExample(componentName, workflow, outputDirectory));
        }
        return { files };
    }
    /**
     * Generate backend from extended workflow schema
     * Creates full CRUD API with Prisma, auth middleware, and role-based access control
     */
    generateFromExtendedWorkflow(workflow, outputDir) {
        const files = [];
        const componentName = 'backend'; // Default component name
        if (!workflow.models || Object.keys(workflow.models).length === 0) {
            return { files: [], warnings: ['No models defined - skipping backend generation'] };
        }
        // Get technology configuration
        const orm = workflow.technology?.backend?.orm || 'prisma';
        const usePrisma = orm === 'prisma';
        const useDrizzle = orm === 'drizzle';
        const apiEndpoints = workflow.api?.endpoints || [];
        // Generate CRUD routes for each model
        for (const [modelName, modelDef] of Object.entries(workflow.models)) {
            files.push(...this.generateModelCrudRoutes(modelName, modelDef, workflow, outputDir));
        }
        // Generate custom API endpoints if defined
        for (const endpoint of apiEndpoints) {
            files.push(...this.generateCustomEndpoint(endpoint, workflow, outputDir));
        }
        // Generate routes index
        const allModels = Object.keys(workflow.models);
        files.push(this.generateRoutesIndexFromModels(allModels, apiEndpoints, componentName, outputDir));
        // Generate ORM-specific files
        if (usePrisma) {
            files.push(this.generatePrismaClient(componentName, outputDir));
            files.push(this.generatePrismaSchema(workflow, componentName, outputDir));
        }
        else if (useDrizzle) {
            files.push(this.generateDrizzleClient(workflow, componentName, outputDir));
            files.push(this.generateDrizzleSchema(workflow, componentName, outputDir));
        }
        // Generate enhanced auth middleware with roles
        files.push(this.generateEnhancedMiddleware(workflow, componentName, outputDir));
        // Generate validation middleware
        files.push(this.generateValidationMiddleware(componentName, outputDir));
        // Generate main server file
        files.push(this.generateServerFileFromExtended(componentName, workflow, outputDir));
        // Generate package.json with Prisma
        files.push(this.generatePackageJsonWithPrisma(componentName, outputDir));
        // Generate tsconfig.json
        files.push(this.generateTsConfig(componentName, outputDir));
        // Generate .env.example
        files.push(this.generateEnvExampleExtended(componentName, workflow, outputDir));
        return { files };
    }
    getBackendComponents(workflow) {
        const components = [];
        for (const [name, component] of Object.entries(workflow.stack)) {
            const comp = component;
            if (comp.type === 'backend' && comp.technology?.startsWith('typescript')) {
                components.push([name, comp]);
            }
        }
        return components;
    }
    getDatabaseComponents(workflow) {
        const components = [];
        for (const [name, component] of Object.entries(workflow.stack)) {
            const comp = component;
            if (comp.type === 'database') {
                components.push([name, comp]);
            }
        }
        return components;
    }
    generateEndpointFiles(endpoint, stackName, workflow, outputDir) {
        const files = [];
        // Generate route handler
        files.push({
            path: path.join(outputDir, stackName, 'src', 'routes', `${endpoint.id}.ts`),
            content: this.generateRouteHandler(endpoint, workflow),
            overwrite: true,
        });
        // Generate types for request/response
        files.push({
            path: path.join(outputDir, stackName, 'src', 'types', `${endpoint.id}.types.ts`),
            content: this.generateEndpointTypes(endpoint),
            overwrite: true,
        });
        return files;
    }
    generateRouteHandler(endpoint, _workflow) {
        let code = `import { Router, Request, Response } from 'express';\n`;
        code += `import { db } from '../database/client';\n`;
        // Check if endpoint needs bcrypt or jwt
        const needsBcrypt = this.endpointNeedsBcrypt(endpoint);
        const needsJwt = this.endpointNeedsJwt(endpoint);
        if (needsBcrypt) {
            code += `import bcrypt from 'bcrypt';\n`;
        }
        if (needsJwt) {
            code += `import jwt from 'jsonwebtoken';\n`;
        }
        // Determine which types will be generated
        const types = this.getGeneratedTypes(endpoint);
        if (types.length > 0) {
            code += `import { ${types.join(', ')} } from '../types/${endpoint.id}.types';\n`;
        }
        code += `\n`;
        code += `const router = Router();\n\n`;
        // Generate the route handler
        const method = endpoint.method.toLowerCase();
        code += `router.${method}('${endpoint.path.replace(/^\/api/, '')}', async (req: Request, res: Response) => {\n`;
        code += `  try {\n`;
        // Generate implementation steps
        if (endpoint.implementation?.steps) {
            code += this.generateImplementationSteps(endpoint.implementation.steps, '    ', endpoint);
        }
        else {
            code += `    // TODO: Implement endpoint logic\n`;
            code += `    res.status(200).json({ message: 'Success' });\n`;
        }
        code += `  } catch (error) {\n`;
        code += `    console.error('Error:', error);\n`;
        code += `    res.status(500).json({ message: 'Internal server error' });\n`;
        code += `  }\n`;
        code += `});\n\n`;
        code += `export default router;\n`;
        return code;
    }
    generateImplementationSteps(steps, indent, endpoint) {
        let code = '';
        // Sort steps by dependencies
        const sortedSteps = this.topologicalSort(steps);
        for (const step of sortedSteps) {
            switch (step.action) {
                case 'validate-schema': {
                    code += `${indent}// Validate request schema\n`;
                    // Generate validation from request body schema
                    const requiredFields = this.getRequiredFieldsFromEndpoint(endpoint);
                    if (requiredFields.length > 0) {
                        const checks = requiredFields.map(field => `!req.body.${field}`).join(' || ');
                        code += `${indent}if (!req.body || ${checks}) {\n`;
                        if (step.onError) {
                            const errorResponse = {
                                ...step.onError.response,
                                errors: requiredFields.map(f => `${f} is required`)
                            };
                            code += `${indent}  return res.status(${step.onError.status}).json(${JSON.stringify(errorResponse)});\n`;
                        }
                        code += `${indent}}\n\n`;
                    }
                    break;
                }
                case 'database-query': {
                    const varName = this.toCamelCase(step.id);
                    code += `${indent}// ${step.id}\n`;
                    if (step.query?.type === 'findOne') {
                        const table = step.database;
                        const { sqlFilter, params } = this.generateFilterWithParams(step.query.filter, endpoint);
                        code += `${indent}const ${varName}Result = await db.query(\n`;
                        code += `${indent}  'SELECT * FROM ${table} WHERE ${sqlFilter} LIMIT 1',\n`;
                        code += `${indent}  [${params.join(', ')}]\n`;
                        code += `${indent});\n\n`;
                        if (step.onNotFound) {
                            code += `${indent}if (!${varName}Result.rows || ${varName}Result.rows.length === 0) {\n`;
                            code += `${indent}  return res.status(${step.onNotFound.status}).json(${JSON.stringify(step.onNotFound.response)});\n`;
                            code += `${indent}}\n\n`;
                        }
                    }
                    else if (step.query?.type === 'findMany') {
                        const table = step.database;
                        const { sqlFilter, params } = this.generateFilterWithParams(step.query.filter, endpoint);
                        const orderBy = step.query.orderBy ? Object.entries(step.query.orderBy).map(([k, v]) => `${k} ${v}`).join(', ') : '';
                        code += `${indent}const ${varName}Result = await db.query(\n`;
                        code += `${indent}  'SELECT * FROM ${table} WHERE ${sqlFilter}${orderBy ? ` ORDER BY ${orderBy}` : ''}',\n`;
                        code += `${indent}  [${params.join(', ')}]\n`;
                        code += `${indent});\n\n`;
                    }
                    break;
                }
                case 'crypto-compare': {
                    const compareVar = this.toCamelCase(step.id);
                    code += `${indent}// ${step.id}\n`;
                    code += `${indent}const ${compareVar} = await bcrypt.compare(\n`;
                    code += `${indent}  req.body.password,\n`;
                    code += `${indent}  ${this.parseTemplateValue(step.compare.hashed, 'findUser')}\n`;
                    code += `${indent});\n\n`;
                    if (step.onMismatch) {
                        code += `${indent}if (!${compareVar}) {\n`;
                        code += `${indent}  return res.status(${step.onMismatch.status}).json(${JSON.stringify(step.onMismatch.response)});\n`;
                        code += `${indent}}\n\n`;
                    }
                    break;
                }
                case 'generate-jwt': {
                    const tokenVar = this.toCamelCase(step.id);
                    code += `${indent}// ${step.id}\n`;
                    code += `${indent}const ${tokenVar} = jwt.sign(\n`;
                    code += `${indent}  ${this.generateJwtPayload(step.payload, 'findUser')},\n`;
                    code += `${indent}  process.env.JWT_SECRET || 'dev-secret',\n`;
                    code += `${indent}  { expiresIn: '${step.expiresIn || '7d'}' }\n`;
                    code += `${indent});\n\n`;
                    break;
                }
                case 'database-insert': {
                    const insertVarName = this.toCamelCase(step.id);
                    code += `${indent}// ${step.id}\n`;
                    const insertTable = step.database;
                    const insertData = this.generateInsertData(step.data, 'findUser');
                    code += `${indent}const ${insertVarName}Result = await db.query(\n`;
                    code += `${indent}  'INSERT INTO ${insertTable} (${Object.keys(insertData).join(', ')}) VALUES (${Object.keys(insertData).map((_, i) => `$${i + 1}`).join(', ')}) RETURNING *',\n`;
                    code += `${indent}  [${Object.values(insertData).join(', ')}]\n`;
                    code += `${indent});\n\n`;
                    break;
                }
                case 'respond': {
                    code += `${indent}// ${step.id}\n`;
                    const responseData = this.generateResponseData(step.response, sortedSteps, 'findUser', 'generateToken');
                    code += `${indent}return res.status(${step.status}).json(${responseData});\n`;
                    break;
                }
                default:
                    code += `${indent}// TODO: Implement ${step.action}\n`;
            }
        }
        return code;
    }
    generateEndpointTypes(endpoint) {
        let code = '';
        // Generate request type
        if (endpoint.request?.body) {
            code += `export interface ${this.capitalize(endpoint.id)}Request {\n`;
            if (endpoint.request.body.properties) {
                for (const [key, prop] of Object.entries(endpoint.request.body.properties)) {
                    const p = prop;
                    const optional = !endpoint.request.body.required?.includes(key);
                    code += `  ${key}${optional ? '?' : ''}: ${this.mapTypeToTs(p.type)};\n`;
                }
            }
            code += `}\n\n`;
        }
        // Generate response type
        const successResponse = endpoint.responses?.find((r) => r.status === 200);
        if (successResponse?.body) {
            code += `export interface ${this.capitalize(endpoint.id)}Response {\n`;
            if (successResponse.body.properties) {
                for (const [key, prop] of Object.entries(successResponse.body.properties)) {
                    const p = prop;
                    if (p.type === 'object' && p.properties) {
                        code += `  ${key}: {\n`;
                        for (const [subKey, subProp] of Object.entries(p.properties)) {
                            const sp = subProp;
                            code += `    ${subKey}: ${this.mapTypeToTs(sp.type)};\n`;
                        }
                        code += `  };\n`;
                    }
                    else {
                        code += `  ${key}: ${this.mapTypeToTs(p.type)};\n`;
                    }
                }
            }
            code += `}\n\n`;
        }
        // Generate error response types
        const errorResponses = endpoint.responses?.filter((r) => r.status >= 400);
        if (errorResponses && errorResponses.length > 0) {
            code += `export interface ${this.capitalize(endpoint.id)}ErrorResponse {\n`;
            code += `  message: string;\n`;
            code += `  errors?: any[];\n`;
            code += `}\n`;
        }
        return code;
    }
    generateRoutesIndex(endpoints, stackName, outputDir) {
        let code = `import { Router } from 'express';\n`;
        // Import all endpoint routes
        for (const endpoint of endpoints) {
            code += `import ${this.toCamelCase(endpoint.id)}Route from './${endpoint.id}';\n`;
        }
        code += `\nconst router = Router();\n\n`;
        // Use all routes
        for (const endpoint of endpoints) {
            code += `router.use(${this.toCamelCase(endpoint.id)}Route);\n`;
        }
        code += `\nexport default router;\n`;
        return {
            path: path.join(outputDir, stackName, 'src', 'routes', 'index.ts'),
            content: code,
            overwrite: true,
        };
    }
    generateDatabaseClient(stackName, dbComponent, outputDir) {
        let code = `import { Pool } from 'pg';\n\n`;
        code += `const pool = new Pool({\n`;
        code += `  connectionString: process.env.DATABASE_URL,\n`;
        code += `});\n\n`;
        code += `export const db = {\n`;
        code += `  query: (text: string, params?: any[]) => pool.query(text, params),\n`;
        code += `  getClient: () => pool.connect(),\n`;
        code += `};\n`;
        return {
            path: path.join(outputDir, stackName, 'src', 'database', 'client.ts'),
            content: code,
            overwrite: true,
        };
    }
    generateMiddleware(stackName, outputDir) {
        let code = `import { Request, Response, NextFunction } from 'express';\n`;
        code += `import jwt from 'jsonwebtoken';\n\n`;
        code += `export const authenticate = (req: Request, res: Response, next: NextFunction) => {\n`;
        code += `  const token = req.headers.authorization?.split(' ')[1];\n\n`;
        code += `  if (!token) {\n`;
        code += `    return res.status(401).json({ message: 'No token provided' });\n`;
        code += `  }\n\n`;
        code += `  try {\n`;
        code += `    const decoded = jwt.verify(token, process.env.JWT_SECRET || 'dev-secret');\n`;
        code += `    (req as any).user = decoded;\n`;
        code += `    next();\n`;
        code += `  } catch (error) {\n`;
        code += `    return res.status(401).json({ message: 'Invalid token' });\n`;
        code += `  }\n`;
        code += `};\n\n`;
        code += `export const errorHandler = (error: Error, req: Request, res: Response, next: NextFunction) => {\n`;
        code += `  console.error(error);\n`;
        code += `  res.status(500).json({ message: 'Internal server error' });\n`;
        code += `};\n`;
        return {
            path: path.join(outputDir, stackName, 'src', 'middleware', 'index.ts'),
            content: code,
            overwrite: true,
        };
    }
    generateServerFile(stackName, component, workflow, outputDir) {
        // Check if testing is configured
        const hasTests = workflow.testing?.unit_tests || workflow.testing?.integration_tests;
        let code = `import express from 'express';\n`;
        code += `import cors from 'cors';\n`;
        code += `import routes from './routes';\n`;
        code += `import { errorHandler } from './middleware';\n\n`;
        code += `const app = express();\n`;
        code += `const PORT = process.env.PORT || 3000;\n\n`;
        code += `// Middleware\n`;
        code += `app.use(cors());\n`;
        code += `app.use(express.json());\n\n`;
        code += `// Routes\n`;
        code += `app.use('/api', routes);\n\n`;
        code += `// Health check\n`;
        code += `app.get('/health', (req, res) => {\n`;
        code += `  res.json({ status: 'ok' });\n`;
        code += `});\n\n`;
        code += `// Error handler\n`;
        code += `app.use(errorHandler);\n\n`;
        // Export app for testing if tests are configured
        if (hasTests) {
            code += `// Only start server if this file is run directly\n`;
            code += `if (require.main === module) {\n`;
            code += `  app.listen(PORT, () => {\n`;
            code += `    console.log(\`Server running on port \${PORT}\`);\n`;
            code += `  });\n`;
            code += `}\n\n`;
            code += `export { app };\n`;
        }
        else {
            code += `app.listen(PORT, () => {\n`;
            code += `  console.log(\`Server running on port \${PORT}\`);\n`;
            code += `});\n`;
        }
        return {
            path: path.join(outputDir, stackName, 'src', 'index.ts'),
            content: code,
            overwrite: true,
        };
    }
    generatePackageJson(stackName, outputDir) {
        const pkg = {
            name: stackName,
            version: '1.0.0',
            main: 'dist/index.js',
            scripts: {
                dev: 'ts-node-dev --respawn src/index.ts',
                build: 'tsc',
                start: 'node dist/index.js',
                test: 'jest',
                'test:watch': 'jest --watch',
                'test:coverage': 'jest --coverage',
            },
            dependencies: {
                express: '^4.18.0',
                cors: '^2.8.5',
                pg: '^8.11.0',
                bcrypt: '^5.1.0',
                jsonwebtoken: '^9.0.0',
                dotenv: '^16.0.0',
            },
            devDependencies: {
                '@types/express': '^4.17.0',
                '@types/cors': '^2.8.0',
                '@types/node': '^20.0.0',
                '@types/pg': '^8.10.0',
                '@types/bcrypt': '^5.0.0',
                '@types/jsonwebtoken': '^9.0.0',
                typescript: '^5.0.0',
                'ts-node-dev': '^2.0.0',
                // Testing dependencies
                jest: '^29.0.0',
                '@types/jest': '^29.0.0',
                'ts-jest': '^29.1.0',
                supertest: '^6.3.0',
                '@types/supertest': '^2.0.12',
            },
        };
        return {
            path: path.join(outputDir, stackName, 'package.json'),
            content: JSON.stringify(pkg, null, 2),
            overwrite: false,
        };
    }
    generateTsConfig(stackName, outputDir) {
        const config = {
            compilerOptions: {
                target: 'ES2020',
                module: 'commonjs',
                lib: ['ES2020'],
                outDir: './dist',
                rootDir: './src',
                strict: true,
                esModuleInterop: true,
                skipLibCheck: true,
                forceConsistentCasingInFileNames: true,
                resolveJsonModule: true,
                declaration: true,
                declarationMap: true,
                sourceMap: true,
            },
            include: ['src/**/*'],
            exclude: ['node_modules', 'dist', 'src/**/*.test.ts', 'src/test/**/*'],
        };
        return {
            path: path.join(outputDir, stackName, 'tsconfig.json'),
            content: JSON.stringify(config, null, 2),
            overwrite: false,
        };
    }
    generateEnvExample(stackName, workflow, outputDir) {
        let content = '# Database\n';
        content += 'DATABASE_URL=postgresql://localhost:5432/myapp\n\n';
        content += '# Authentication\n';
        content += 'JWT_SECRET=your-secret-key-here\n\n';
        content += '# Server\n';
        content += 'PORT=3000\n';
        content += 'NODE_ENV=development\n';
        return {
            path: path.join(outputDir, stackName, '.env.example'),
            content,
            overwrite: false,
        };
    }
    // Helper methods
    capitalize(str) {
        return str
            .split('-')
            .map(part => part.charAt(0).toUpperCase() + part.slice(1))
            .join('');
    }
    toCamelCase(str) {
        return str.replace(/-+([a-zA-Z])/g, (_match, letter) => letter.toUpperCase());
    }
    mapTypeToTs(type) {
        switch (type) {
            case 'string':
                return 'string';
            case 'number':
            case 'integer':
                return 'number';
            case 'boolean':
                return 'boolean';
            case 'array':
                return 'any[]';
            case 'object':
                return 'any';
            default:
                return 'any';
        }
    }
    endpointNeedsBcrypt(endpoint) {
        if (!endpoint.implementation?.steps)
            return false;
        return endpoint.implementation.steps.some((s) => s.action === 'crypto-compare');
    }
    endpointNeedsJwt(endpoint) {
        if (!endpoint.implementation?.steps)
            return false;
        return endpoint.implementation.steps.some((s) => s.action === 'generate-jwt');
    }
    topologicalSort(steps) {
        // Simple topological sort based on depends_on
        const sorted = [];
        const visited = new Set();
        const visit = (step) => {
            if (visited.has(step.id))
                return;
            if (step.depends_on) {
                for (const depId of step.depends_on) {
                    const depStep = steps.find(s => s.id === depId);
                    if (depStep) {
                        visit(depStep);
                    }
                }
            }
            visited.add(step.id);
            sorted.push(step);
        };
        for (const step of steps) {
            visit(step);
        }
        return sorted;
    }
    parseTemplateValue(template, _contextVar) {
        // Handle non-string values
        if (typeof template === 'boolean') {
            return String(template);
        }
        if (typeof template === 'number') {
            return String(template);
        }
        if (typeof template !== 'string') {
            return JSON.stringify(template);
        }
        // Parse template strings like {{ find-user.result.passwordHash }}
        const match = template.match(/\{\{\s*([^}]+)\s*\}\}/);
        if (match) {
            const path = match[1].trim();
            const parts = path.split('.');
            if (parts.length === 0) {
                return template;
            }
            // Handle special variables
            if (parts[0] === 'now') {
                return 'new Date()';
            }
            if (parts[0] === 'request') {
                return `req.${parts.slice(1).join('.')}`;
            }
            // For step results: {{ find-user.result.id }} -> findUserResult.id
            // The .result part is semantic in the workflow but maps to the Result variable
            const varName = this.toCamelCase(parts[0]);
            // If it's just the step name (e.g., {{ generate-token }})
            // Return the variable name directly (jwt.sign returns token directly)
            if (parts.length === 1) {
                return varName;
            }
            // If the second part is "result", skip it
            // {{ generate-token.result }} -> generateToken (not generateTokenResult)
            // {{ find-user.result.id }} -> findUserResult.id (not findUserResult.result.id)
            const remainingParts = parts[1] === 'result' ? parts.slice(2) : parts.slice(1);
            // If no parts remain after stripping .result, return just the variable name
            // This handles cases like {{ generate-token.result }} where the variable IS the result
            if (remainingParts.length === 0) {
                return varName;
            }
            // For database queries/inserts, access .rows[0] for the actual result
            // {{ find-user.result.id }} -> findUserResult.rows[0].id
            // {{ create-validation.result.id }} -> createValidationResult.rows[0].id
            return `${varName}Result.rows[0].${remainingParts.join('.')}`;
        }
        // Plain string - return as quoted string literal
        return `'${template}'`;
    }
    generateFilterString(filter) {
        // Simple filter generation for now
        if (filter.email) {
            return 'email = $1';
        }
        return 'id = $1';
    }
    /**
     * Generate SQL filter with proper parameter detection (params vs body vs query)
     */
    generateFilterWithParams(filter, _endpoint) {
        const conditions = [];
        const params = [];
        let paramIndex = 1;
        for (const [field, value] of Object.entries(filter)) {
            // Check if value is a template like "{{ request.params.id }}"
            if (typeof value === 'string' && value.includes('{{')) {
                const match = value.match(/\{\{\s*request\.([^}]+)\s*\}\}/);
                if (match) {
                    const path = match[1]; // e.g., "params.id" or "query.email" or "body.email"
                    conditions.push(`${field} = $${paramIndex}`);
                    params.push(`req.${path}`);
                    paramIndex++;
                }
            }
            else {
                // Direct value
                conditions.push(`${field} = $${paramIndex}`);
                params.push(typeof value === 'string' ? `'${value}'` : String(value));
                paramIndex++;
            }
        }
        return {
            sqlFilter: conditions.join(' AND ') || '1=1',
            params
        };
    }
    generateJwtPayload(payload, contextVar) {
        let obj = '{\n';
        for (const [key, value] of Object.entries(payload)) {
            const val = this.parseTemplateValue(value, contextVar);
            obj += `      ${key}: ${val},\n`;
        }
        obj += '    }';
        return obj;
    }
    generateInsertData(data, contextVar) {
        const result = {};
        for (const [key, value] of Object.entries(data)) {
            const val = this.parseTemplateValue(value, contextVar);
            result[key] = val;
        }
        return result;
    }
    generateResponseData(response, steps, ...contextVars) {
        let obj = '{\n';
        for (const [key, value] of Object.entries(response)) {
            if (typeof value === 'object' && value !== null) {
                obj += `      ${key}: {\n`;
                for (const [subKey, subValue] of Object.entries(value)) {
                    const val = this.parseTemplateValueWithSteps(subValue, steps, contextVars[0]);
                    obj += `        ${subKey}: ${val},\n`;
                }
                obj += '      },\n';
            }
            else {
                const val = this.parseTemplateValueWithSteps(value, steps, contextVars[0]);
                obj += `      ${key}: ${val},\n`;
            }
        }
        obj += '    }';
        return obj;
    }
    /**
     * Parse template value with awareness of step types (findMany returns array)
     */
    parseTemplateValueWithSteps(template, steps, contextVar) {
        if (typeof template !== 'string' || !template.includes('{{')) {
            return this.parseTemplateValue(template, contextVar);
        }
        const match = template.match(/\{\{\s*([^}]+)\s*\}\}/);
        if (!match) {
            return this.parseTemplateValue(template, contextVar);
        }
        const path = match[1].trim();
        const parts = path.split('.');
        if (parts.length === 0 || parts[0] === 'now' || parts[0] === 'request') {
            return this.parseTemplateValue(template, contextVar);
        }
        // Find the step to check if it's a findMany query
        const stepId = this.toKebabCase(parts[0]);
        const step = steps.find(s => s.id === stepId);
        if (step?.action === 'database-query' && step?.query?.type === 'findMany') {
            // For findMany, use .rows directly (no [0])
            const varName = this.toCamelCase(parts[0]);
            // Handle different template patterns:
            // {{ query-validations.result }} -> queryValidationsResult.rows
            // {{ query-validations.result.length }} -> queryValidationsResult.rows.length
            if (parts.length === 1 || (parts.length === 2 && parts[1] === 'result')) {
                return `${varName}Result.rows`;
            }
            else if (parts.length === 3 && parts[1] === 'result' && parts[2] === 'length') {
                return `${varName}Result.rows.length`;
            }
        }
        return this.parseTemplateValue(template, contextVar);
    }
    /**
     * Convert camelCase/PascalCase to kebab-case
     */
    toKebabCase(str) {
        return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
    }
    /**
     * Generate full CRUD routes for a model
     */
    generateModelCrudRoutes(modelName, modelDef, workflow, outputDir) {
        const files = [];
        const routeName = modelName.toLowerCase();
        // Generate route file with all CRUD operations
        const routeContent = this.generateModelRouteFile(modelName, modelDef, workflow);
        files.push({
            path: path.join(outputDir, 'backend', 'src', 'routes', `${routeName}.ts`),
            content: routeContent,
            overwrite: true,
        });
        return files;
    }
    /**
     * Generate route file with CRUD operations for a model
     */
    generateModelRouteFile(modelName, modelDef, workflow) {
        const modelLower = modelName.toLowerCase();
        const modelVar = this.toCamelCase(modelName);
        // Check if events are enabled
        const hasEvents = workflow.events && Object.keys(workflow.events).length > 0;
        let code = `import { Router, Request, Response } from 'express';\n`;
        code += `import { prisma } from '../database/prisma';\n`;
        code += `import { authenticate, authorize } from '../middleware/auth';\n`;
        code += `import { validateRequest } from '../middleware/validation';\n`;
        // Import event emitter if events are enabled
        if (hasEvents) {
            code += `import { emitEventWithContext } from '../events/event-emitter';\n`;
        }
        code += `\n`;
        code += `const router = Router();\n\n`;
        // List all (GET /)
        code += `// List all ${modelName}s\n`;
        code += `router.get('/', authenticate, async (req: Request, res: Response) => {\n`;
        code += `  try {\n`;
        code += `    const { skip, take, ...where } = req.query;\n`;
        code += `    const ${modelVar}s = await prisma.${modelLower}.findMany({\n`;
        code += `      where: where as any,\n`;
        code += `      skip: skip ? parseInt(skip as string) : undefined,\n`;
        code += `      take: take ? Math.min(parseInt(take as string), 100) : 20,\n`;
        code += `    });\n`;
        code += `    res.json(${modelVar}s);\n`;
        code += `  } catch (error) {\n`;
        code += `    console.error(error);\n`;
        code += `    res.status(500).json({ message: 'Internal server error' });\n`;
        code += `  }\n`;
        code += `});\n\n`;
        // Get by ID (GET /:id)
        code += `// Get ${modelName} by ID\n`;
        code += `router.get('/:id', authenticate, async (req: Request, res: Response) => {\n`;
        code += `  try {\n`;
        code += `    const ${modelVar} = await prisma.${modelLower}.findUnique({\n`;
        code += `      where: { id: req.params.id },\n`;
        code += `    });\n`;
        code += `    if (!${modelVar}) {\n`;
        code += `      return res.status(404).json({ message: '${modelName} not found' });\n`;
        code += `    }\n`;
        code += `    res.json(${modelVar});\n`;
        code += `  } catch (error) {\n`;
        code += `    console.error(error);\n`;
        code += `    res.status(500).json({ message: 'Internal server error' });\n`;
        code += `  }\n`;
        code += `});\n\n`;
        // Create (POST /)
        code += `// Create ${modelName}\n`;
        code += `router.post('/', authenticate, async (req: Request, res: Response) => {\n`;
        code += `  try {\n`;
        code += `    const ${modelVar} = await prisma.${modelLower}.create({\n`;
        code += `      data: req.body,\n`;
        code += `    });\n`;
        // Emit event if configured
        if (hasEvents) {
            const createEventName = `${modelLower}_created`;
            if (workflow.events[createEventName]) {
                code += `\n    // Emit ${createEventName} event\n`;
                code += `    await emitEventWithContext(req, '${createEventName}', ${modelVar} as any);\n`;
            }
        }
        code += `    res.status(201).json(${modelVar});\n`;
        code += `  } catch (error) {\n`;
        code += `    console.error(error);\n`;
        code += `    res.status(500).json({ message: 'Internal server error' });\n`;
        code += `  }\n`;
        code += `});\n\n`;
        // Update (PUT /:id)
        code += `// Update ${modelName}\n`;
        code += `router.put('/:id', authenticate, async (req: Request, res: Response) => {\n`;
        code += `  try {\n`;
        code += `    const ${modelVar} = await prisma.${modelLower}.update({\n`;
        code += `      where: { id: req.params.id },\n`;
        code += `      data: req.body,\n`;
        code += `    });\n`;
        // Emit event if configured
        if (hasEvents) {
            const updateEventName = `${modelLower}_updated`;
            if (workflow.events[updateEventName]) {
                code += `\n    // Emit ${updateEventName} event\n`;
                code += `    await emitEventWithContext(req, '${updateEventName}', ${modelVar} as any);\n`;
            }
        }
        code += `    res.json(${modelVar});\n`;
        code += `  } catch (error) {\n`;
        code += `    console.error(error);\n`;
        code += `    res.status(500).json({ message: 'Internal server error' });\n`;
        code += `  }\n`;
        code += `});\n\n`;
        // Delete (DELETE /:id)
        code += `// Delete ${modelName}\n`;
        code += `router.delete('/:id', authenticate, async (req: Request, res: Response) => {\n`;
        code += `  try {\n`;
        // Use soft delete if enabled
        if (modelDef.soft_delete) {
            code += `    const ${modelVar} = await prisma.${modelLower}.update({\n`;
            code += `      where: { id: req.params.id },\n`;
            code += `      data: { deleted_at: new Date() },\n`;
            code += `    });\n`;
        }
        else {
            code += `    const ${modelVar} = await prisma.${modelLower}.delete({\n`;
            code += `      where: { id: req.params.id },\n`;
            code += `    });\n`;
        }
        // Emit event if configured
        if (hasEvents) {
            const deleteEventName = `${modelLower}_deleted`;
            if (workflow.events[deleteEventName]) {
                code += `\n    // Emit ${deleteEventName} event\n`;
                code += `    await emitEventWithContext(req, '${deleteEventName}', ${modelVar} as any);\n`;
            }
        }
        code += `    res.json(${modelVar});\n`;
        code += `  } catch (error) {\n`;
        code += `    console.error(error);\n`;
        code += `    res.status(500).json({ message: 'Internal server error' });\n`;
        code += `  }\n`;
        code += `});\n\n`;
        code += `export default router;\n`;
        return code;
    }
    /**
     * Generate custom endpoint from API definition
     */
    generateCustomEndpoint(endpoint, workflow, outputDir) {
        const files = [];
        const endpointId = endpoint.path.replace(/[/:]/g, '-').replace(/^-/, '');
        // Check if events are enabled
        const hasEvents = workflow.events && Object.keys(workflow.events).length > 0;
        let code = `import { Router, Request, Response } from 'express';\n`;
        code += `import { prisma } from '../database/prisma';\n`;
        code += `import { authenticate, authorize } from '../middleware/auth';\n`;
        // Import event emitter if this endpoint emits an event
        if (hasEvents && endpoint.emits) {
            code += `import { emitEventWithContext } from '../events/event-emitter';\n`;
        }
        code += `\n`;
        code += `const router = Router();\n\n`;
        const method = endpoint.method.toLowerCase();
        const middlewares = [];
        // Add auth if required
        if (endpoint.auth === 'required') {
            middlewares.push('authenticate');
            // Add role authorization
            if (endpoint.roles && endpoint.roles.length > 0) {
                middlewares.push(`authorize([${endpoint.roles.map((r) => `'${r}'`).join(', ')}])`);
            }
        }
        const middlewareStr = middlewares.length > 0 ? `, ${middlewares.join(', ')}` : '';
        code += `router.${method}('${endpoint.path}'${middlewareStr}, async (req: Request, res: Response) => {\n`;
        code += `  try {\n`;
        code += `    // TODO: Implement custom endpoint logic\n`;
        code += `    const result = { message: 'Endpoint not implemented yet' };\n`;
        // Emit event if configured
        if (hasEvents && endpoint.emits) {
            code += `\n    // Emit ${endpoint.emits} event\n`;
            code += `    await emitEventWithContext(req, '${endpoint.emits}', result);\n`;
        }
        code += `    res.json(result);\n`;
        code += `  } catch (error) {\n`;
        code += `    console.error(error);\n`;
        code += `    res.status(500).json({ message: 'Internal server error' });\n`;
        code += `  }\n`;
        code += `});\n\n`;
        code += `export default router;\n`;
        files.push({
            path: path.join(outputDir, 'backend', 'src', 'routes', `${endpointId}.ts`),
            content: code,
            overwrite: true,
        });
        return files;
    }
    /**
     * Generate routes index that imports all model routes
     */
    generateRoutesIndexFromModels(models, endpoints, stackName, outputDir) {
        let code = `import { Router } from 'express';\n\n`;
        // Import model routes
        for (const modelName of models) {
            const routeName = modelName.toLowerCase();
            code += `import ${this.toCamelCase(modelName)}Routes from './${routeName}';\n`;
        }
        // Deduplicate endpoints by path to avoid duplicate imports
        const uniqueEndpoints = endpoints.reduce((acc, endpoint) => {
            if (!acc.find(e => e.path === endpoint.path)) {
                acc.push(endpoint);
            }
            return acc;
        }, []);
        // Import custom endpoints
        for (const endpoint of uniqueEndpoints) {
            const endpointId = endpoint.path.replace(/[/:]/g, '-').replace(/^-/, '');
            code += `import ${this.toCamelCase(endpointId)}Route from './${endpointId}';\n`;
        }
        code += `\nconst router = Router();\n\n`;
        // Mount model routes
        for (const modelName of models) {
            const routePath = `/${modelName.toLowerCase()}s`;
            code += `router.use('${routePath}', ${this.toCamelCase(modelName)}Routes);\n`;
        }
        // Mount custom endpoints
        for (const endpoint of uniqueEndpoints) {
            const endpointId = endpoint.path.replace(/[/:]/g, '-').replace(/^-/, '');
            code += `router.use(${this.toCamelCase(endpointId)}Route);\n`;
        }
        code += `\nexport default router;\n`;
        return {
            path: path.join(outputDir, stackName, 'src', 'routes', 'index.ts'),
            content: code,
            overwrite: true,
        };
    }
    /**
     * Generate Prisma client wrapper
     */
    generatePrismaClient(stackName, outputDir) {
        const code = `import { PrismaClient } from '@prisma/client';\n\n` +
            `export const prisma = new PrismaClient({\n` +
            `  log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],\n` +
            `});\n\n` +
            `// Graceful shutdown\n` +
            `process.on('beforeExit', async () => {\n` +
            `  await prisma.$disconnect();\n` +
            `});\n`;
        return {
            path: path.join(outputDir, stackName, 'src', 'database', 'prisma.ts'),
            content: code,
            overwrite: true,
        };
    }
    /**
     * Generate Prisma schema file from workflow models using the reusable builder
     */
    generatePrismaSchema(workflow, stackName, outputDir) {
        const dbType = workflow.technology?.database?.type || 'postgresql';
        const schema = prisma_schema_builder_1.PrismaSchemaBuilder.generateSchema(workflow, {
            provider: dbType,
            includeGenerator: true,
            includeDatasource: true,
        });
        return {
            path: path.join(outputDir, stackName, 'prisma', 'schema.prisma'),
            content: schema,
            overwrite: true,
        };
    }
    /**
     * Generate enhanced middleware with role-based auth
     */
    generateEnhancedMiddleware(workflow, stackName, outputDir) {
        let code = `import { Request, Response, NextFunction } from 'express';\n`;
        code += `import jwt from 'jsonwebtoken';\n\n`;
        // Extend Express Request type
        code += `declare global {\n`;
        code += `  namespace Express {\n`;
        code += `    interface Request {\n`;
        code += `      user?: {\n`;
        code += `        id: string;\n`;
        code += `        email: string;\n`;
        code += `        role: string;\n`;
        code += `      };\n`;
        code += `    }\n`;
        code += `  }\n`;
        code += `}\n\n`;
        // Authenticate middleware
        code += `export const authenticate = (req: Request, res: Response, next: NextFunction) => {\n`;
        code += `  const token = req.headers.authorization?.split(' ')[1];\n\n`;
        code += `  if (!token) {\n`;
        code += `    return res.status(401).json({ message: 'No token provided' });\n`;
        code += `  }\n\n`;
        code += `  try {\n`;
        code += `    const decoded = jwt.verify(token, process.env.JWT_SECRET || 'dev-secret') as any;\n`;
        code += `    req.user = decoded;\n`;
        code += `    next();\n`;
        code += `  } catch (error) {\n`;
        code += `    return res.status(401).json({ message: 'Invalid token' });\n`;
        code += `  }\n`;
        code += `};\n\n`;
        // Role-based authorization middleware
        code += `export const authorize = (allowedRoles: string[]) => {\n`;
        code += `  return (req: Request, res: Response, next: NextFunction) => {\n`;
        code += `    if (!req.user) {\n`;
        code += `      return res.status(401).json({ message: 'Not authenticated' });\n`;
        code += `    }\n\n`;
        code += `    if (!allowedRoles.includes(req.user.role)) {\n`;
        code += `      return res.status(403).json({ message: 'Insufficient permissions' });\n`;
        code += `    }\n\n`;
        code += `    next();\n`;
        code += `  };\n`;
        code += `};\n\n`;
        // Error handler
        code += `export const errorHandler = (error: Error, req: Request, res: Response, next: NextFunction) => {\n`;
        code += `  console.error(error);\n`;
        code += `  res.status(500).json({ message: 'Internal server error' });\n`;
        code += `};\n`;
        return {
            path: path.join(outputDir, stackName, 'src', 'middleware', 'auth.ts'),
            content: code,
            overwrite: true,
        };
    }
    /**
     * Generate validation middleware
     */
    generateValidationMiddleware(stackName, outputDir) {
        const code = `import { Request, Response, NextFunction } from 'express';\n\n` +
            `export const validateRequest = (schema: any) => {\n` +
            `  return (req: Request, res: Response, next: NextFunction) => {\n` +
            `    // TODO: Implement request validation with Zod or similar\n` +
            `    next();\n` +
            `  };\n` +
            `};\n`;
        return {
            path: path.join(outputDir, stackName, 'src', 'middleware', 'validation.ts'),
            content: code,
            overwrite: true,
        };
    }
    /**
     * Generate server file for extended workflow
     */
    generateServerFileFromExtended(stackName, workflow, outputDir) {
        // Check if events are enabled
        const hasEvents = workflow.events && Object.keys(workflow.events).length > 0;
        const hasRedis = workflow.technology?.cache?.provider?.includes('redis');
        let code = `import express from 'express';\n`;
        code += `import cors from 'cors';\n`;
        code += `import routes from './routes';\n`;
        code += `import { errorHandler } from './middleware/auth';\n`;
        // Import event bus and WebSocket if events and Redis are configured
        if (hasEvents && hasRedis) {
            code += `import { createServer } from 'http';\n`;
            code += `import { getEventBus } from './events/event-bus';\n`;
            code += `import { eventContextMiddleware } from './events/event-emitter';\n`;
            code += `import { initializeAllSubscribers } from './events/subscriber-initializer';\n`;
            code += `import { initializeRealtimeServer } from './websocket/server';\n`;
            code += `import { initializeEventBridge } from './websocket/event-bridge';\n`;
        }
        code += `\n`;
        code += `const app = express();\n`;
        code += `const PORT = process.env.PORT || 3000;\n\n`;
        code += `// Middleware\n`;
        code += `app.use(cors());\n`;
        code += `app.use(express.json());\n`;
        // Add event context middleware if events are enabled
        if (hasEvents && hasRedis) {
            code += `app.use(eventContextMiddleware);\n`;
        }
        code += `\n`;
        code += `// Routes\n`;
        code += `app.use('/api', routes);\n\n`;
        code += `// Health check\n`;
        code += `app.get('/health', (req, res) => {\n`;
        code += `  res.json({ status: 'ok' });\n`;
        code += `});\n\n`;
        code += `// Error handler\n`;
        code += `app.use(errorHandler);\n\n`;
        // Initialize event bus, subscribers, and WebSocket if events and Redis are configured
        if (hasEvents && hasRedis) {
            code += `// Initialize event-driven architecture\n`;
            code += `const initializeEventInfrastructure = async () => {\n`;
            code += `  try {\n`;
            code += `    // 1. Initialize event bus\n`;
            code += `    const eventBus = getEventBus({\n`;
            code += `      redis: {\n`;
            code += `        host: process.env.REDIS_HOST || 'localhost',\n`;
            code += `        port: parseInt(process.env.REDIS_PORT || '6379'),\n`;
            code += `        password: process.env.REDIS_PASSWORD,\n`;
            code += `      },\n`;
            code += `      channelPrefix: 'events:',\n`;
            code += `    });\n`;
            code += `    await eventBus.initialize();\n`;
            code += `    console.log('Event bus initialized');\n`;
            code += `\n`;
            code += `    // 2. Initialize event subscribers\n`;
            code += `    await initializeAllSubscribers();\n`;
            code += `    console.log('Event subscribers initialized');\n`;
            code += `\n`;
            code += `    // 3. Initialize WebSocket server\n`;
            code += `    const realtimeServer = initializeRealtimeServer(server);\n`;
            code += `    console.log('WebSocket server initialized');\n`;
            code += `\n`;
            code += `    // 4. Connect event bus to WebSocket (event bridge)\n`;
            code += `    await initializeEventBridge();\n`;
            code += `    console.log('Event bridge initialized - events will be broadcast to WebSocket clients');\n`;
            code += `  } catch (error) {\n`;
            code += `    console.error('Failed to initialize event infrastructure:', error);\n`;
            code += `  }\n`;
            code += `};\n\n`;
            code += `// Create HTTP server for WebSocket support\n`;
            code += `const server = createServer(app);\n\n`;
            code += `server.listen(PORT, async () => {\n`;
            code += `  console.log(\`Server running on port \${PORT}\`);\n`;
            code += `  await initializeEventInfrastructure();\n`;
            code += `});\n\n`;
        }
        else {
            code += `app.listen(PORT, async () => {\n`;
            code += `  console.log(\`Server running on port \${PORT}\`);\n`;
            code += `});\n\n`;
        }
        code += `export { app };\n`;
        return {
            path: path.join(outputDir, stackName, 'src', 'index.ts'),
            content: code,
            overwrite: true,
        };
    }
    /**
     * Generate package.json with Prisma dependencies
     */
    generatePackageJsonWithPrisma(stackName, outputDir, workflow) {
        // Check if events are enabled and Redis is configured
        const hasEvents = workflow?.events && Object.keys(workflow.events).length > 0;
        const hasRedis = workflow?.technology?.cache?.provider?.includes('redis');
        const dependencies = {
            '@prisma/client': '^5.0.0',
            express: '^4.18.0',
            cors: '^2.8.5',
            bcrypt: '^5.1.0',
            jsonwebtoken: '^9.0.0',
            dotenv: '^16.0.0',
        };
        // Add Redis and WebSocket dependencies if events are enabled
        if (hasEvents && hasRedis) {
            dependencies.ioredis = '^5.3.0';
            dependencies.ws = '^8.13.0';
        }
        const pkg = {
            name: stackName,
            version: '1.0.0',
            main: 'dist/index.js',
            scripts: {
                dev: 'ts-node-dev --respawn src/index.ts',
                build: 'tsc',
                start: 'node dist/index.js',
                'prisma:generate': 'prisma generate',
                'prisma:migrate': 'prisma migrate dev',
                'prisma:studio': 'prisma studio',
                test: 'jest',
                'test:watch': 'jest --watch',
            },
            dependencies,
            devDependencies: {
                '@types/express': '^4.17.0',
                '@types/cors': '^2.8.0',
                '@types/node': '^20.0.0',
                '@types/bcrypt': '^5.0.0',
                '@types/jsonwebtoken': '^9.0.0',
                ...(hasEvents && hasRedis ? { '@types/ws': '^8.5.0' } : {}),
                prisma: '^5.0.0',
                typescript: '^5.0.0',
                'ts-node-dev': '^2.0.0',
                jest: '^29.0.0',
                '@types/jest': '^29.0.0',
                'ts-jest': '^29.1.0',
                supertest: '^6.3.0',
                '@types/supertest': '^2.0.12',
            },
        };
        return {
            path: path.join(outputDir, stackName, 'package.json'),
            content: JSON.stringify(pkg, null, 2),
            overwrite: false,
        };
    }
    /**
     * Generate .env.example for extended workflow
     */
    generateEnvExampleExtended(stackName, workflow, outputDir) {
        let content = '# Database\n';
        const dbType = workflow.technology?.database?.type || 'postgresql';
        const dbExamples = {
            postgresql: 'postgresql://user:password@localhost:5432/mydb',
            mysql: 'mysql://user:password@localhost:3306/mydb',
            sqlite: 'file:./dev.db',
        };
        content += `DATABASE_URL="${dbExamples[dbType] || dbExamples.postgresql}"\n\n`;
        content += '# Authentication\n';
        content += 'JWT_SECRET=your-secret-key-change-this-in-production\n\n';
        content += '# Server\n';
        content += 'PORT=3000\n';
        content += 'NODE_ENV=development\n';
        return {
            path: path.join(outputDir, stackName, '.env.example'),
            content,
            overwrite: false,
        };
    }
    /**
     * Get required fields from endpoint request schema
     */
    getRequiredFieldsFromEndpoint(endpoint) {
        if (!endpoint || !endpoint.request?.body) {
            return [];
        }
        return endpoint.request.body.required || [];
    }
    /**
     * Determine which types will be generated for an endpoint
     * Only import types that actually exist
     */
    getGeneratedTypes(endpoint) {
        const types = [];
        const capitalizedName = this.capitalize(endpoint.id);
        // Request type is only generated if endpoint has request body (POST/PUT/PATCH)
        if (endpoint.request?.body) {
            types.push(`${capitalizedName}Request`);
        }
        // Response type is only generated if there's a 200 response with a body
        const successResponse = endpoint.responses?.find((r) => r.status === 200);
        if (successResponse?.body) {
            types.push(`${capitalizedName}Response`);
        }
        // Error response type is generated if there are error responses (4xx/5xx)
        const errorResponses = endpoint.responses?.filter((r) => r.status >= 400);
        if (errorResponses && errorResponses.length > 0) {
            types.push(`${capitalizedName}ErrorResponse`);
        }
        return types;
    }
    /**
     * Generate Drizzle client wrapper
     */
    generateDrizzleClient(workflow, stackName, outputDir) {
        const dbType = workflow.technology?.database?.type || 'postgresql';
        const _providerModule = dbType === 'postgresql' ? 'drizzle-orm/node-postgres' :
            dbType === 'mysql' ? 'drizzle-orm/mysql2' :
                'drizzle-orm/better-sqlite3';
        let code = '';
        if (dbType === 'postgresql') {
            code = `import { drizzle } from 'drizzle-orm/node-postgres';\n`;
            code += `import { Pool } from 'pg';\n`;
            code += `import * as schema from './schema';\n\n`;
            code += `const pool = new Pool({\n`;
            code += `  connectionString: process.env.DATABASE_URL,\n`;
            code += `});\n\n`;
            code += `export const db = drizzle(pool, { schema });\n`;
        }
        return {
            path: path.join(outputDir, stackName, 'src', 'database', 'client.ts'),
            content: code,
            overwrite: true,
        };
    }
    /**
     * Generate Drizzle schema file from workflow models using the reusable builder
     */
    generateDrizzleSchema(workflow, stackName, outputDir) {
        const dbType = workflow.technology?.database?.type || 'postgresql';
        const schema = drizzle_schema_builder_1.DrizzleSchemaBuilder.generateSchema(workflow, {
            provider: dbType,
        });
        return {
            path: path.join(outputDir, stackName, 'src', 'database', 'schema.ts'),
            content: schema,
            overwrite: true,
        };
    }
    /**
     * Generate an API endpoint
     * Implements the recommended BackendGenerator.generateEndpoint method
     */
    generateEndpoint(endpoint, context) {
        // Delegate to existing generateEndpointFiles method
        const stackName = 'backend';
        return this.generateEndpointFiles(endpoint, stackName, context.workflow, context.outputDirectory);
    }
}
exports.TypeScriptExpressGenerator = TypeScriptExpressGenerator;
//# sourceMappingURL=typescript-express-generator.js.map