"use strict";
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;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@oclif/core");
const internals_1 = require("@prisma/internals");
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const ts_morph_1 = require("ts-morph");
const chalk_1 = __importDefault(require("chalk"));
/**
 * TEACHING NOTE: This is the CORE of DataBridge!
 *
 * What is DMMF? Data Model Meta Format
 * - Prisma's internal representation of your database
 * - It's like a JSON map of all your tables, columns, relationships
 * - We read this to know what code to generate
 *
 * What is ts-morph?
 * - Library for manipulating TypeScript code
 * - Instead of writing strings like "export class Product {}"
 * - We use ts-morph API: addClass(), addProperty(), etc.
 * - Safer and easier than string templates!
 */
class Generate extends core_1.Command {
    static description = 'Generate API routes and Angular SDK from Prisma schema';
    static examples = ['<%= config.bin %> <%= command.id %>'];
    async run() {
        await this.parse(Generate);
        try {
            this.log(chalk_1.default.bold.blue('\n⚙️  Generating code from Prisma schema...\n'));
            // 1. Load config
            const config = await this.loadConfig();
            // 2. Get Prisma DMMF (database structure)
            const schemaPath = path.join(process.cwd(), 'prisma', 'schema.prisma');
            const dmmf = await this.getPrismaDMMF(schemaPath);
            this.log(chalk_1.default.bold(`📊 Found ${dmmf.datamodel.models.length} models\n`));
            // 3. Generate API routes (Fastify)
            await this.generateApiRoutes(dmmf, config);
            // 4. Generate Angular SDK (services + models)
            await this.generateAngularSdk(dmmf, config);
            // 5. Generate test script
            await this.generateTestScript(dmmf, config);
            // 6. Generate Prisma plugin
            await this.generatePrismaPlugin(config);
            // 7. Generate logger utility
            await this.generateLoggerUtility(config);
            // 8. Generate server file
            await this.generateServerFile(dmmf, config);
            // 9. Copy bundled dependencies (zero npm install!)
            await this.copyBundledDependencies();
            // 10. Generate package.json with locked versions
            await this.generatePackageJson(config);
            // 11. Run prisma generate to create Prisma Client
            await this.runPrismaGenerate();
            // Success!
            this.log(chalk_1.default.green('\n✅ Code generation complete!'));
            this.log(chalk_1.default.bold('\nGenerated:'));
            this.log(chalk_1.default.gray(`  API routes:      ${config.api.outputPath}/routes/`));
            this.log(chalk_1.default.gray(`  Angular services: ${config.angular.outputPath}/services/`));
            this.log(chalk_1.default.gray(`  TypeScript models: ${config.angular.outputPath}/models/`));
            this.log(chalk_1.default.gray(`  Logger utility:  ${config.api.outputPath}/utils/logger.ts`));
            this.log(chalk_1.default.gray(`  Test script:      test-api.sh`));
            this.log(chalk_1.default.bold('\nNext step:'));
            this.log('  Run: ' + chalk_1.default.cyan('npm run dev') + ' (start API server)\n');
        }
        catch (error) {
            this.error(`Generation failed: ${error.message}\n${error.stack}`);
        }
    }
    async loadConfig() {
        const configPath = path.join(process.cwd(), '.databridge.json');
        if (!(await fs.pathExists(configPath))) {
            this.error('No .databridge.json found. Run "databridge init" first.');
        }
        return fs.readJSON(configPath);
    }
    /**
     * TEACHING NOTE: Get Prisma DMMF
     * This reads your schema.prisma and converts it to a JavaScript object
     * with all the information about your models, fields, types, etc.
     */
    async getPrismaDMMF(schemaPath) {
        if (!(await fs.pathExists(schemaPath))) {
            this.error('No Prisma schema found. Run "databridge introspect" first.');
        }
        try {
            // Read schema file content
            const schemaContent = await fs.readFile(schemaPath, 'utf-8');
            console.log('DEBUG: Schema content length:', schemaContent.length);
            console.log('DEBUG: Schema preview:', schemaContent.substring(0, 200));
            const dmmf = await (0, internals_1.getDMMF)({
                datamodel: schemaContent,
            });
            console.log('DEBUG: DMMF object keys:', Object.keys(dmmf));
            console.log('DEBUG: DMMF.datamodel keys:', Object.keys(dmmf.datamodel));
            console.log('DEBUG: Number of models in DMMF:', dmmf.datamodel.models?.length || 0);
            return dmmf;
        }
        catch (error) {
            console.error('DEBUG: getDMMF error:', error);
            this.error(`Failed to parse Prisma schema: ${error.message}`);
        }
    }
    /**
     * TEACHING NOTE: Generate Fastify API Routes
     *
     * For each model (like "Product"), we create a file with:
     * - GET /products - list all
     * - GET /products/:id - get one
     * - POST /products - create
     * - PATCH /products/:id - update
     * - DELETE /products/:id - delete
     *
     * This is called CRUD: Create, Read, Update, Delete
     */
    async generateApiRoutes(dmmf, config) {
        const project = new ts_morph_1.Project(); // ts-morph project
        const routesDir = path.join(process.cwd(), config.api.outputPath, 'routes');
        await fs.ensureDir(routesDir);
        for (const model of dmmf.datamodel.models) {
            const modelName = model.name; // e.g., "Product"
            const routeName = modelName.toLowerCase(); // e.g., "product"
            this.log(chalk_1.default.gray(`  🔧 Generating API route: ${routeName}.ts`));
            // Create a new TypeScript file
            const sourceFile = project.createSourceFile(path.join(routesDir, `${routeName}.ts`), '', { overwrite: true });
            // Add imports at the top
            sourceFile.addImportDeclaration({
                moduleSpecifier: 'fastify',
                namedImports: ['FastifyInstance', 'FastifyRequest', 'FastifyReply'],
            });
            sourceFile.addImportDeclaration({
                moduleSpecifier: '../plugins/prisma',
                namedImports: ['prisma'],
            });
            sourceFile.addImportDeclaration({
                moduleSpecifier: 'zod',
                namedImports: ['z'],
            });
            // Generate the route handler function
            // This creates: export async function productRoutes(fastify) { ... }
            sourceFile.addFunction({
                name: `${routeName}Routes`,
                isAsync: true,
                isExported: true,
                isDefaultExport: true,
                parameters: [{ name: 'fastify', type: 'FastifyInstance' }],
                statements: this.generateRouteStatements(modelName, routeName, model),
            });
            await sourceFile.save();
        }
    }
    /**
     * TEACHING NOTE: This generates the actual route handling code
     * It's returned as a string that becomes the function body
     */
    generateRouteStatements(modelName, routeName, model) {
        // Generate Zod schema for validation
        const zodSchema = this.generateZodSchema(model);
        return `
  // Zod validation schema
  const create${modelName}Schema = ${zodSchema};
  const update${modelName}Schema = create${modelName}Schema.partial();

  // GET /${routeName} - List all ${modelName}
  fastify.get('/${routeName}', async (request: FastifyRequest, reply: FastifyReply) => {
    const items = await prisma.${routeName}.findMany();
    return reply.send(items);
  });

  // GET /${routeName}/:id - Get one ${modelName}
  fastify.get<{ Params: { id: string } }>('/${routeName}/:id', async (request, reply) => {
    try {
      const id = parseInt(request.params.id);
      if (isNaN(id)) {
        return reply.status(400).send({ error: 'Invalid ID format', message: 'ID must be a number' });
      }
      
      const item = await prisma.${routeName}.findUnique({ where: { id } });
      
      if (!item) {
        return reply.status(404).send({ error: '${modelName} not found' });
      }
      
      return reply.send(item);
    } catch (error: any) {
      return reply.status(500).send({ error: 'Internal Server Error', message: error.message });
    }
  });

  // POST /${routeName} - Create ${modelName}
  fastify.post('/${routeName}', async (request: FastifyRequest, reply: FastifyReply) => {
    try {
      const validatedData = create${modelName}Schema.parse(request.body);
      const item = await prisma.${routeName}.create({
        data: validatedData,
      });
      return reply.status(201).send(item);
    } catch (error: any) {
      if (error.name === 'ZodError') {
        return reply.status(400).send({ error: 'Validation failed', details: error.errors });
      }
      if (error.code === 'P2002') {
        // Unique constraint violation
        return reply.status(409).send({ 
          error: 'Conflict', 
          message: 'A record with this unique value already exists',
          field: error.meta?.target 
        });
      }
      if (error.code === 'P2003') {
        // Foreign key constraint violation
        return reply.status(400).send({ 
          error: 'Bad Request', 
          message: 'Referenced record does not exist',
          field: error.meta?.field_name 
        });
      }
      if (error.code?.startsWith('P')) {
        // Other Prisma errors
        return reply.status(500).send({ 
          statusCode: 500, 
          code: error.code, 
          error: 'Internal Server Error', 
          message: error.message 
        });
      }
      return reply.status(500).send({ error: 'Internal Server Error', message: error.message });
    }
  });

  // PATCH /${routeName}/:id - Update ${modelName}
  fastify.patch<{ Params: { id: string } }>('/${routeName}/:id', async (request, reply) => {
    try {
      const id = parseInt(request.params.id);
      if (isNaN(id)) {
        return reply.status(400).send({ error: 'Invalid ID format', message: 'ID must be a number' });
      }
      
      const validatedData = update${modelName}Schema.parse(request.body);
      const item = await prisma.${routeName}.update({
        where: { id },
        data: validatedData,
      });
      return reply.send(item);
    } catch (error: any) {
      if (error.name === 'ZodError') {
        return reply.status(400).send({ error: 'Validation failed', details: error.errors });
      }
      if (error.code === 'P2025') {
        // Record not found
        return reply.status(404).send({ error: '${modelName} not found' });
      }
      if (error.code === 'P2002') {
        // Unique constraint violation
        return reply.status(409).send({ 
          error: 'Conflict', 
          message: 'A record with this unique value already exists',
          field: error.meta?.target 
        });
      }
      if (error.code === 'P2003') {
        // Foreign key constraint violation
        return reply.status(400).send({ 
          error: 'Bad Request', 
          message: 'Referenced record does not exist',
          field: error.meta?.field_name 
        });
      }
      if (error.code?.startsWith('P')) {
        // Other Prisma errors
        return reply.status(500).send({ 
          statusCode: 500, 
          code: error.code, 
          error: 'Internal Server Error', 
          message: error.message 
        });
      }
      return reply.status(500).send({ error: 'Internal Server Error', message: error.message });
    }
  });

  // DELETE /${routeName}/:id - Delete ${modelName}
  fastify.delete<{ Params: { id: string } }>('/${routeName}/:id', async (request, reply) => {
    try {
      const id = parseInt(request.params.id);
      if (isNaN(id)) {
        return reply.status(400).send({ error: 'Invalid ID format', message: 'ID must be a number' });
      }
      
      await prisma.${routeName}.delete({ where: { id } });
      return reply.status(204).send();
    } catch (error: any) {
      if (error.code === 'P2025') {
        // Record not found
        return reply.status(404).send({ error: '${modelName} not found' });
      }
      if (error.code === 'P2003') {
        // Foreign key constraint - record is referenced
        return reply.status(409).send({ 
          error: 'Conflict', 
          message: 'Cannot delete record because it is referenced by other records',
          field: error.meta?.field_name 
        });
      }
      if (error.code?.startsWith('P')) {
        // Other Prisma errors
        return reply.status(500).send({ 
          statusCode: 500, 
          code: error.code, 
          error: 'Internal Server Error', 
          message: error.message 
        });
      }
      return reply.status(500).send({ error: 'Internal Server Error', message: error.message });
    }
  });
    `.trim();
    }
    /**
     * TEACHING NOTE: Generate Angular SDK
     * Creates TypeScript services that your Angular app uses
     *
     * Example: ProductService with methods:
     * - list(): Observable<Product[]>
     * - getById(id): Observable<Product>
     * - create(data): Observable<Product>
     * - etc.
     */
    async generateAngularSdk(dmmf, config) {
        const project = new ts_morph_1.Project();
        const sdkDir = path.join(process.cwd(), config.angular.outputPath, 'services');
        await fs.ensureDir(sdkDir);
        for (const model of dmmf.datamodel.models) {
            const modelName = model.name;
            const serviceName = `${modelName}Service`;
            const routeName = modelName.toLowerCase();
            this.log(chalk_1.default.gray(`  🔧 Generating Angular service: ${serviceName}`));
            const sourceFile = project.createSourceFile(path.join(sdkDir, `${routeName}.service.ts`), '', { overwrite: true });
            // Imports
            sourceFile.addImportDeclarations([
                {
                    moduleSpecifier: '@angular/core',
                    namedImports: ['Injectable'],
                },
                {
                    moduleSpecifier: '@angular/common/http',
                    namedImports: ['HttpClient'],
                },
                {
                    moduleSpecifier: 'rxjs',
                    namedImports: ['Observable'],
                },
                {
                    moduleSpecifier: '../models',
                    namedImports: [modelName],
                },
            ]);
            // Service class with @Injectable decorator
            const serviceClass = sourceFile.addClass({
                name: serviceName,
                isExported: true,
                decorators: [
                    {
                        name: 'Injectable',
                        arguments: ['{ providedIn: \'root\' }'],
                    },
                ],
            });
            // Constructor with HttpClient injection
            serviceClass.addConstructor({
                parameters: [
                    {
                        name: 'http',
                        type: 'HttpClient',
                        scope: ts_morph_1.Scope.Private,
                    },
                ],
            });
            // API URL property
            serviceClass.addProperty({
                name: 'apiUrl',
                scope: ts_morph_1.Scope.Private,
                initializer: `'http://localhost:${config.api.port}/${routeName}'`,
            });
            // CRUD methods
            this.addServiceMethods(serviceClass, modelName);
            await sourceFile.save();
        }
        // Generate TypeScript interfaces for models
        await this.generateModels(dmmf, config);
    }
    /**
     * Add CRUD methods to Angular service
     */
    addServiceMethods(serviceClass, modelName) {
        // list()
        serviceClass.addMethod({
            name: 'list',
            returnType: `Observable<${modelName}[]>`,
            statements: `return this.http.get<${modelName}[]>(this.apiUrl);`,
        });
        // getById(id)
        serviceClass.addMethod({
            name: 'getById',
            parameters: [{ name: 'id', type: 'number' }],
            returnType: `Observable<${modelName}>`,
            statements: `return this.http.get<${modelName}>(\`\${this.apiUrl}/\${id}\`);`,
        });
        // create(data)
        serviceClass.addMethod({
            name: 'create',
            parameters: [{ name: 'data', type: `Partial<${modelName}>` }],
            returnType: `Observable<${modelName}>`,
            statements: `return this.http.post<${modelName}>(this.apiUrl, data);`,
        });
        // update(id, data)
        serviceClass.addMethod({
            name: 'update',
            parameters: [
                { name: 'id', type: 'number' },
                { name: 'data', type: `Partial<${modelName}>` },
            ],
            returnType: `Observable<${modelName}>`,
            statements: `return this.http.patch<${modelName}>(\`\${this.apiUrl}/\${id}\`, data);`,
        });
        // delete(id)
        serviceClass.addMethod({
            name: 'delete',
            parameters: [{ name: 'id', type: 'number' }],
            returnType: `Observable<void>`,
            statements: `return this.http.delete<void>(\`\${this.apiUrl}/\${id}\`);`,
        });
    }
    /**
     * TEACHING NOTE: Generate TypeScript interfaces
     * These are the types for your models
     *
     * Example:
     * export interface Product {
     *   id: number;
     *   name: string;
     *   price: number;
     * }
     */
    async generateModels(dmmf, config) {
        const project = new ts_morph_1.Project();
        const modelsPath = path.join(process.cwd(), config.angular.outputPath, 'models', 'index.ts');
        await fs.ensureDir(path.dirname(modelsPath));
        const sourceFile = project.createSourceFile(modelsPath, '', {
            overwrite: true,
        });
        for (const model of dmmf.datamodel.models) {
            const interfaceDecl = sourceFile.addInterface({
                name: model.name,
                isExported: true,
            });
            // Add properties for each field
            for (const field of model.fields) {
                interfaceDecl.addProperty({
                    name: field.name,
                    type: this.mapPrismaTypeToTs(field.type),
                    hasQuestionToken: !field.isRequired, // Optional if not required
                });
            }
        }
        await sourceFile.save();
        this.log(chalk_1.default.gray(`  🔧 Generated TypeScript models: models/index.ts`));
    }
    /**
     * TEACHING NOTE: Map Prisma types to TypeScript types
     * Prisma uses: String, Int, Float, Boolean, DateTime
     * TypeScript uses: string, number, boolean, Date
     */
    mapPrismaTypeToTs(prismaType) {
        const typeMap = {
            String: 'string',
            Int: 'number',
            Float: 'number',
            Boolean: 'boolean',
            DateTime: 'Date',
            Json: 'any',
        };
        return typeMap[prismaType] || 'any';
    }
    /**
     * TEACHING NOTE: Generate Zod validation schema from Prisma model
     * This creates runtime validation for API requests
     */
    generateZodSchema(model) {
        const fields = [];
        for (const field of model.fields) {
            // Skip id, created_at, updated_at - these are auto-generated
            if (field.name === 'id' || field.name === 'created_at' || field.name === 'updated_at') {
                continue;
            }
            // Skip relations - handle those separately
            if (field.kind === 'object') {
                continue;
            }
            let zodType = this.mapPrismaTypeToZod(field.type);
            // Add constraints based on field attributes
            if (field.type === 'String') {
                // Add minimum length for required strings
                if (field.isRequired) {
                    zodType += '.min(1, { message: "Cannot be empty" })';
                }
                // Check for max length from @db.VarChar
                const match = field.documentation?.match(/@db\.VarChar\((\d+)\)/);
                if (match) {
                    zodType += `.max(${match[1]})`;
                }
            }
            if (field.type === 'Int' && field.name.includes('stock')) {
                zodType += '.nonnegative({ message: "Stock cannot be negative" })';
            }
            if (field.type === 'Decimal' || field.type === 'Float') {
                if (field.name.includes('price')) {
                    zodType += '.positive({ message: "Price must be greater than 0" })';
                }
            }
            // Make optional if not required
            if (!field.isRequired) {
                zodType += '.optional()';
            }
            fields.push(`    ${field.name}: ${zodType}`);
        }
        return `z.object({
${fields.join(',\n')}
  })`;
    }
    /**
     * Map Prisma types to Zod validators
     */
    mapPrismaTypeToZod(prismaType) {
        const typeMap = {
            String: 'z.string()',
            Int: 'z.number().int()',
            Float: 'z.number()',
            Decimal: 'z.number()',
            Boolean: 'z.boolean()',
            DateTime: 'z.string().datetime()',
            Json: 'z.any()',
        };
        return typeMap[prismaType] || 'z.any()';
    }
    /**
     * Generate test script for API validation
     */
    async generateTestScript(dmmf, config) {
        const port = config.api.port || 3000;
        const models = dmmf.datamodel.models;
        let testScript = `#!/bin/bash

echo "========================================="
echo "DataBridge API Test Suite"
echo "Generated: ${new Date().toISOString()}"
echo "========================================="
echo ""

echo "Waiting for server to be ready..."
sleep 2

# Test Health Check
echo "Test: Health Check"
curl -s http://localhost:${port}/health
echo -e "\\n✅ Pass\\n"

`;
        // Generate tests for each model
        for (const model of models) {
            const modelName = model.name;
            const routeName = modelName.toLowerCase();
            // Find first required string field and first required number field for test data
            const requiredFields = model.fields.filter((f) => f.isRequired && f.kind !== 'object' && !['id', 'created_at', 'updated_at'].includes(f.name));
            const stringField = requiredFields.find((f) => f.type === 'String');
            const numberField = requiredFields.find((f) => f.type === 'Int' || f.type === 'Float' || f.type === 'Decimal');
            testScript += `# ========== ${modelName} Tests ==========

echo "Test: GET /${routeName} (List all)"
curl -s http://localhost:${port}/${routeName} | head -c 200
echo -e "...\\n✅ Pass\\n"

`;
            // Valid creation test
            if (stringField || numberField) {
                const testData = {};
                for (const field of requiredFields) {
                    if (field.type === 'String') {
                        testData[field.name] = `Test ${modelName}`;
                    }
                    else if (field.type === 'Int' || field.type === 'Float' || field.type === 'Decimal') {
                        testData[field.name] = field.name.includes('price') ? 99.99 : 10;
                    }
                    else if (field.type === 'Boolean') {
                        testData[field.name] = true;
                    }
                }
                testScript += `echo "Test: POST /${routeName} (Create valid)"
curl -s -X POST http://localhost:${port}/${routeName} \\
  -H "Content-Type: application/json" \\
  -d '${JSON.stringify(testData)}'
echo -e "\\n✅ Pass\\n"

`;
            }
            // Validation tests
            if (stringField) {
                testScript += `echo "Test: POST /${routeName} (Missing required field)"
RESPONSE=$(curl -s -w "\\nHTTP_CODE:%{http_code}" -X POST http://localhost:${port}/${routeName} \\
  -H "Content-Type: application/json" \\
  -d '{}')
if echo "$RESPONSE" | grep -q "Validation failed"; then
  echo "✅ Pass - Validation caught missing field"
else
  echo "❌ Fail - Should catch missing required field"
  echo "$RESPONSE"
fi
echo ""

echo "Test: POST /${routeName} (Empty ${stringField.name})"
RESPONSE=$(curl -s -w "\\nHTTP_CODE:%{http_code}" -X POST http://localhost:${port}/${routeName} \\
  -H "Content-Type: application/json" \\
  -d '{"${stringField.name}":""}')
if echo "$RESPONSE" | grep -q "Validation failed"; then
  echo "✅ Pass - Validation caught empty string"
else
  echo "❌ Fail - Should reject empty ${stringField.name}"
  echo "$RESPONSE"
fi
echo ""

`;
            }
            if (numberField && (numberField.name.includes('price') || numberField.name.includes('amount'))) {
                testScript += `echo "Test: POST /${routeName} (Negative ${numberField.name})"
RESPONSE=$(curl -s -w "\\nHTTP_CODE:%{http_code}" -X POST http://localhost:${port}/${routeName} \\
  -H "Content-Type: application/json" \\
  -d '{"${numberField.name}":-10}')
if echo "$RESPONSE" | grep -q "Validation failed"; then
  echo "✅ Pass - Validation caught negative ${numberField.name}"
else
  echo "❌ Fail - Should reject negative ${numberField.name}"
  echo "$RESPONSE"
fi
echo ""

`;
            }
        }
        testScript += `echo "========================================="
echo "Test Suite Complete"
echo "========================================="
`;
        // Write test script
        const testPath = path.join(process.cwd(), 'test-api.sh');
        await fs.writeFile(testPath, testScript);
        await fs.chmod(testPath, 0o755); // Make executable
        this.log(chalk_1.default.gray(`  🔧 Generated test script: test-api.sh`));
    }
    /**
     * Generate logger utility for production-ready logging
     */
    async generateLoggerUtility(config) {
        const utilsDir = path.join(process.cwd(), config.api.outputPath, 'utils');
        await fs.ensureDir(utilsDir);
        const loggerPath = path.join(utilsDir, 'logger.ts');
        // Templates are in dist/templates after build
        const templatePath = path.join(__dirname, '..', '..', 'templates', 'logger.ts');
        // Copy logger template to API utils directory
        await fs.copy(templatePath, loggerPath);
        this.log(chalk_1.default.gray(`  📝 Generated logger utility: utils/logger.ts`));
    }
    /**
     * Generate server file with logger integration
     */
    async generateServerFile(dmmf, config) {
        const serverDir = path.join(process.cwd(), config.api.outputPath);
        await fs.ensureDir(serverDir);
        const serverPath = path.join(serverDir, 'server.ts');
        // Templates are in dist/templates after build
        const templatePath = path.join(__dirname, '..', '..', 'templates', 'server.ts');
        // Read template
        let template = await fs.readFile(templatePath, 'utf-8');
        // Generate route imports
        const routeImports = dmmf.datamodel.models
            .map((model) => {
            const modelName = model.name;
            const routeName = modelName.toLowerCase();
            return `import ${routeName}Routes from './routes/${routeName}';`;
        })
            .join('\n');
        // Generate route registrations
        const routeRegistrations = dmmf.datamodel.models
            .map((model) => {
            const routeName = model.name.toLowerCase();
            return `  await fastify.register(${routeName}Routes);`;
        })
            .join('\n');
        // Generate route list for console output
        const routeList = dmmf.datamodel.models
            .map((model) => {
            const routeName = model.name.toLowerCase();
            return `  console.log('  GET    /${routeName}');
  console.log('  GET    /${routeName}/:id');
  console.log('  POST   /${routeName}');
  console.log('  PATCH  /${routeName}/:id');
  console.log('  DELETE /${routeName}/:id');`;
        })
            .join('\n');
        // Replace placeholders
        template = template.replace('{{ROUTE_IMPORTS}}', routeImports);
        template = template.replace('{{ROUTE_REGISTRATIONS}}', routeRegistrations);
        template = template.replace('{{ROUTE_LIST}}', routeList);
        template = template.replace('{{PORT}}', config.api.port || '3000');
        // Write server file
        await fs.writeFile(serverPath, template);
        this.log(chalk_1.default.gray(`  🚀 Generated server: server.ts`));
    }
    /**
     * Copy bundled dependencies from CLI package to generated project
     * This eliminates the need for users to run npm install
     */
    async copyBundledDependencies() {
        this.log(chalk_1.default.gray('  📦 Copying bundled dependencies...'));
        const cliRoot = path.join(__dirname, '..', '..');
        const bundleSource = path.join(cliRoot, 'bundled-deps', 'node_modules');
        const targetPath = path.join(process.cwd(), 'node_modules');
        if (!(await fs.pathExists(bundleSource))) {
            this.log(chalk_1.default.yellow('  ⚠️  No bundled dependencies found. Run: pnpm run bundle:deps'));
            return;
        }
        // Copy node_modules from bundle
        await fs.ensureDir(targetPath);
        await fs.copy(bundleSource, targetPath, {
            overwrite: true,
            dereference: true // Follow symlinks
        });
        this.log(chalk_1.default.gray('  ✅ Dependencies copied (zero npm install required!)'));
    }
    /**
     * Generate package.json with locked dependency versions
     */
    async generatePackageJson(config) {
        this.log(chalk_1.default.gray('  📄 Generating package.json...'));
        const packageJson = {
            name: config.projectName || 'databridge-api',
            version: '1.0.0',
            description: 'Generated by DataBridge CLI',
            main: 'apps/api/src/server.ts',
            scripts: {
                dev: 'tsx apps/api/src/server.ts',
                start: 'tsx apps/api/src/server.ts',
                test: 'bash test-api.sh',
                'prisma:generate': 'prisma generate',
                'prisma:migrate': 'prisma migrate dev'
            },
            keywords: ['databridge', 'api', 'fastify', 'prisma'],
            author: '',
            license: 'ISC',
            devDependencies: {
                '@fastify/cors': '11.1.0',
                fastify: '5.6.2',
                'pino-pretty': '13.1.2',
                prisma: '5.22.0',
                tsx: '4.20.6'
            },
            dependencies: {
                '@prisma/client': '5.22.0',
                'fastify-plugin': '5.1.0',
                pino: '9.6.0',
                zod: '3.25.76'
            },
            engines: {
                node: '>=20.0.0'
            },
            databridge: {
                version: '0.1.0',
                generatedAt: new Date().toISOString(),
                bundledDependencies: true
            }
        };
        const packagePath = path.join(process.cwd(), 'package.json');
        await fs.writeJson(packagePath, packageJson, { spaces: 2 });
        this.log(chalk_1.default.gray('  ✅ package.json created with locked versions'));
    }
    /**
     * Generate Prisma plugin for Fastify
     */
    async generatePrismaPlugin(config) {
        const pluginsDir = path.join(process.cwd(), config.api.outputPath, 'plugins');
        await fs.ensureDir(pluginsDir);
        const pluginPath = path.join(pluginsDir, 'prisma.ts');
        const pluginContent = `import { FastifyInstance } from 'fastify';
import { PrismaClient } from '@prisma/client';
import fastifyPlugin from 'fastify-plugin';

/**
 * Prisma plugin for Fastify
 * Makes Prisma client available globally in all routes
 */
export const prisma = new PrismaClient({
  log: process.env.NODE_ENV === 'development' 
    ? ['query', 'info', 'warn', 'error']
    : ['error']
});

async function prismaPlugin(fastify: FastifyInstance) {
  // Make prisma available in routes via fastify.prisma
  fastify.decorate('prisma', prisma);

  // Cleanup on server close
  fastify.addHook('onClose', async () => {
    await prisma.$disconnect();
  });
}

export default fastifyPlugin(prismaPlugin);
`;
        await fs.writeFile(pluginPath, pluginContent);
        this.log(chalk_1.default.gray('  🔧 Generated Prisma plugin: plugins/prisma.ts'));
    }
    /**
     * Run prisma generate to create Prisma Client
     */
    async runPrismaGenerate() {
        this.log(chalk_1.default.gray('\n  🔧 Running prisma generate...'));
        const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
        const localPrisma = path.join(process.cwd(), 'node_modules', '.bin', 'prisma');
        try {
            // Use local Prisma from bundled dependencies
            const prismaCmd = process.platform === 'win32'
                ? `"${localPrisma}.cmd"`
                : localPrisma;
            execSync(`${prismaCmd} generate`, {
                cwd: process.cwd(),
                stdio: 'pipe',
                env: { ...process.env }
            });
            this.log(chalk_1.default.green('  ✅ Prisma Client generated successfully'));
        }
        catch (error) {
            this.log(chalk_1.default.yellow(`  ⚠️  Prisma generate failed: ${error.message}`));
            this.log(chalk_1.default.gray('     You may need to run: npx prisma generate'));
        }
    }
}
exports.default = Generate;
//# sourceMappingURL=generate.js.map