"use strict";
/**
 * Prisma Database Generator
 * Generates Prisma schema and migration files 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.PrismaGenerator = void 0;
const path = __importStar(require("path"));
const seed_data_generator_1 = require("./seed-data-generator");
const prisma_schema_builder_1 = require("./prisma-schema-builder");
class PrismaGenerator {
    id = 'prisma-database';
    name = 'Prisma Database Generator';
    description = 'Generates Prisma schema and migrations';
    database = 'prisma';
    generate(context) {
        const { workflow, extendedWorkflow, outputDirectory } = context;
        const files = [];
        // Use extended workflow if available, otherwise fall back to legacy
        if (extendedWorkflow) {
            return this.generateFromExtendedWorkflow(extendedWorkflow, outputDirectory);
        }
        // Legacy generation from stack.tables
        const databaseComponents = this.getDatabaseComponents(workflow);
        if (databaseComponents.length === 0) {
            return { files: [] };
        }
        // Generate for each database component
        for (const [componentName, component] of databaseComponents) {
            // Determine database provider from technology
            const provider = this.extractProvider(component.technology);
            // Generate Prisma schema
            files.push(this.generatePrismaSchema(componentName, component, provider, outputDirectory));
            // Generate migration script
            files.push(this.generateMigrationScript(componentName, outputDirectory));
            // Generate seed script with table data
            files.push(this.generateSeedScript(componentName, outputDirectory, component.tables));
            // Generate package.json with Prisma dependencies
            files.push(this.generatePackageJson(componentName, outputDirectory));
            // Generate .env.example
            files.push(this.generateEnvExample(componentName, provider, outputDirectory));
            // Generate README
            files.push(this.generateReadme(componentName, provider, outputDirectory));
        }
        return { files };
    }
    /**
     * Generate from extended workflow schema with models and relationships
     */
    generateFromExtendedWorkflow(workflow, outputDir) {
        const files = [];
        if (!workflow.models || Object.keys(workflow.models).length === 0) {
            return { files: [], warnings: ['No models defined in workflow'] };
        }
        // Determine database provider from technology section
        const provider = workflow.technology?.database?.type || 'postgresql';
        const componentName = 'database'; // Default component name
        // Generate Prisma schema from models
        files.push(this.generatePrismaSchemaFromModels(workflow, provider, outputDir));
        // Generate migration script
        files.push(this.generateMigrationScript(componentName, outputDir));
        // Generate seed script (no tables for extended workflow yet)
        files.push(this.generateSeedScript(componentName, outputDir));
        // Generate package.json
        files.push(this.generatePackageJson(componentName, outputDir));
        // Generate .env.example
        files.push(this.generateEnvExample(componentName, provider, outputDir));
        // Generate README
        files.push(this.generateReadme(componentName, provider, outputDir));
        return { files };
    }
    getDatabaseComponents(workflow) {
        const components = [];
        for (const [name, component] of Object.entries(workflow.stack)) {
            const comp = component;
            if (comp.type === 'database' && this.supportsTechnology(comp.technology)) {
                components.push([name, comp]);
            }
        }
        return components;
    }
    supportsTechnology(technology) {
        // Support both "postgresql/prisma" and "postgresql" (default to Prisma)
        return (technology.includes('prisma') ||
            technology === 'postgresql' ||
            technology === 'mysql' ||
            technology === 'sqlite' ||
            technology === 'mongodb' ||
            technology === 'sqlserver');
    }
    extractProvider(technology) {
        // Extract database type from "postgresql/prisma" or just "postgresql"
        const baseDb = technology.split('/')[0];
        // Map to Prisma provider names
        const providerMap = {
            postgresql: 'postgresql',
            postgres: 'postgresql',
            mysql: 'mysql',
            sqlite: 'sqlite',
            mongodb: 'mongodb',
            sqlserver: 'sqlserver',
        };
        return providerMap[baseDb] || 'postgresql';
    }
    generatePrismaSchema(componentName, component, provider, outputDir) {
        let schema = `// Prisma Schema
// Generated from workflow definition

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "${provider}"
  url      = env("DATABASE_URL")
}

`;
        // Generate models from tables
        if (component.tables) {
            for (const table of component.tables) {
                schema += this.generateModel(table);
            }
        }
        return {
            path: path.join(outputDir, componentName, 'prisma', 'schema.prisma'),
            content: schema,
            overwrite: true,
        };
    }
    generateModel(table) {
        const modelName = this.toPascalCase(table.name);
        let model = `model ${modelName} {\n`;
        const indexedFields = [];
        // Generate fields
        if (table.required_fields) {
            for (const field of table.required_fields) {
                model += this.generateField(field);
                // Collect indexed fields for model-level @@index
                if (field.indexed) {
                    indexedFields.push(field.name);
                }
            }
        }
        // Add timestamps if not explicitly defined
        const hasCreatedAt = table.required_fields?.some((f) => f.name === 'createdAt');
        const hasUpdatedAt = table.required_fields?.some((f) => f.name === 'updatedAt');
        if (!hasCreatedAt) {
            model += '  createdAt DateTime @default(now())\n';
        }
        if (!hasUpdatedAt) {
            model += '  updatedAt DateTime @updatedAt\n';
        }
        // Add model-level indexes for fields marked with indexed: true
        if (indexedFields.length > 0) {
            model += '\n';
            for (const fieldName of indexedFields) {
                model += `  @@index([${fieldName}])\n`;
            }
        }
        model += '}\n\n';
        return model;
    }
    generateField(field) {
        const fieldName = field.name;
        const fieldType = this.mapTypeToPrisma(field.type);
        let line = `  ${fieldName} ${fieldType}`;
        // Add modifiers
        const modifiers = [];
        if (field.primary_key) {
            modifiers.push('@id');
            if (field.type === 'uuid') {
                modifiers.push('@default(uuid())');
            }
            else if (field.type === 'integer' || field.type === 'number') {
                modifiers.push('@default(autoincrement())');
            }
        }
        if (field.unique) {
            modifiers.push('@unique');
        }
        // Note: indexed fields are handled at model level with @@index in generateModel()
        // Prisma doesn't support field-level @index, only model-level @@index
        if (field.default !== undefined) {
            if (typeof field.default === 'string') {
                modifiers.push(`@default("${field.default}")`);
            }
            else if (typeof field.default === 'boolean') {
                modifiers.push(`@default(${field.default})`);
            }
            else if (typeof field.default === 'number') {
                modifiers.push(`@default(${field.default})`);
            }
        }
        if (field.foreign_key) {
            // Parse foreign_key: "users.id"
            const [refTable, refField] = field.foreign_key.split('.');
            const _refModel = this.toPascalCase(refTable);
            // Add relation field
            modifiers.push(`@relation(fields: [${fieldName}], references: [${refField}])`);
            // Add relation on next line
            const relationName = this.toPascalCase(refTable);
            line += ` ${modifiers.join(' ')}\n  ${relationName.toLowerCase()} ${relationName}`;
            return line + '\n';
        }
        if (modifiers.length > 0) {
            line += ` ${modifiers.join(' ')}`;
        }
        line += '\n';
        return line;
    }
    mapTypeToPrisma(type) {
        const typeMap = {
            uuid: 'String',
            string: 'String',
            text: 'String',
            integer: 'Int',
            number: 'Int',
            float: 'Float',
            decimal: 'Decimal',
            boolean: 'Boolean',
            date: 'DateTime',
            datetime: 'DateTime',
            timestamp: 'DateTime',
            json: 'Json',
        };
        return typeMap[type.toLowerCase()] || 'String';
    }
    generateMigrationScript(componentName, outputDir) {
        const script = `#!/bin/bash
# Migration script for ${componentName}

echo "Running Prisma migrations..."

# Generate Prisma Client
npx prisma generate

# Run migrations
npx prisma migrate dev --name init

echo "✓ Migrations complete!"
`;
        return {
            path: path.join(outputDir, componentName, 'scripts', 'migrate.sh'),
            content: script,
            overwrite: true,
        };
    }
    generateSeedScript(componentName, outputDir, tables) {
        let script;
        // If we have table schemas, generate smart seed data
        if (tables && tables.length > 0) {
            const tableSchemas = this.convertToTableSchemas(tables);
            const seedGenerator = new seed_data_generator_1.SeedDataGenerator({ recordsPerTable: 5 });
            script = seedGenerator.generateSeedData(tableSchemas);
        }
        else {
            // Fallback to empty template
            script = `import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

async function main() {
  console.log('🌱 Seeding database...');

  // Add your seed data here
  // Example:
  // await prisma.user.create({
  //   data: {
  //     email: 'test@example.com',
  //     name: 'Test User',
  //   },
  // });

  console.log('✅ Seeding complete!');
}

main()
  .catch((e) => {
    console.error('❌ Seeding failed:', e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });
`;
        }
        return {
            path: path.join(outputDir, componentName, 'prisma', 'seed.ts'),
            content: script,
            overwrite: true,
        };
    }
    /**
     * Convert workflow table definitions to TableSchema format
     */
    convertToTableSchemas(tables) {
        return tables.map(table => ({
            name: table.name,
            fields: (table.required_fields || []).map((field) => ({
                name: field.name,
                type: field.type,
                primaryKey: field.primary_key,
                unique: field.unique,
                foreignKey: field.foreign_key,
                default: field.default,
                optional: false,
            })),
        }));
    }
    generatePackageJson(componentName, outputDir) {
        const pkg = {
            name: `${componentName}-database`,
            version: '1.0.0',
            scripts: {
                'db:generate': 'prisma generate',
                'db:migrate': 'prisma migrate dev',
                'db:push': 'prisma db push',
                'db:seed': 'ts-node prisma/seed.ts',
                'db:studio': 'prisma studio',
            },
            dependencies: {
                '@prisma/client': '^5.0.0',
            },
            devDependencies: {
                prisma: '^5.0.0',
                'ts-node': '^10.9.0',
                '@types/node': '^20.0.0',
                typescript: '^5.0.0',
            },
            prisma: {
                seed: 'ts-node prisma/seed.ts',
            },
        };
        return {
            path: path.join(outputDir, componentName, 'package.json'),
            content: JSON.stringify(pkg, null, 2),
            overwrite: false,
        };
    }
    generateEnvExample(componentName, provider, outputDir) {
        const examples = {
            postgresql: 'postgresql://user:password@localhost:5432/mydb',
            mysql: 'mysql://user:password@localhost:3306/mydb',
            sqlite: 'file:./dev.db',
            mongodb: 'mongodb://localhost:27017/mydb',
            sqlserver: 'sqlserver://localhost:1433;database=mydb;user=sa;password=password',
        };
        const exampleUrl = examples[provider] || examples.postgresql;
        const content = `# Database
DATABASE_URL="${exampleUrl}"
`;
        return {
            path: path.join(outputDir, componentName, '.env.example'),
            content,
            overwrite: false,
        };
    }
    generateReadme(componentName, provider, outputDir) {
        const content = `# ${componentName} Database

Generated with Bangstack using Prisma ORM.

## Setup

1. Install dependencies:
   \`\`\`bash
   npm install
   \`\`\`

2. Configure your database:
   \`\`\`bash
   cp .env.example .env
   # Edit .env with your database credentials
   \`\`\`

3. Run migrations:
   \`\`\`bash
   npm run db:migrate
   \`\`\`

4. (Optional) Seed the database:
   \`\`\`bash
   npm run db:seed
   \`\`\`

## Available Scripts

- \`npm run db:generate\` - Generate Prisma Client
- \`npm run db:migrate\` - Run database migrations
- \`npm run db:push\` - Push schema changes without migrations
- \`npm run db:seed\` - Seed the database
- \`npm run db:studio\` - Open Prisma Studio (database GUI)

## Database Provider

This project uses **${provider}** as the database provider.

## Schema

The Prisma schema is located at \`prisma/schema.prisma\`.

## Documentation

- [Prisma Documentation](https://www.prisma.io/docs)
- [Prisma CLI Reference](https://www.prisma.io/docs/reference/api-reference/command-reference)
`;
        return {
            path: path.join(outputDir, componentName, 'README.md'),
            content,
            overwrite: false,
        };
    }
    toPascalCase(str) {
        return str
            .split(/[-_]/)
            .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
            .join('');
    }
    /**
     * Generate Prisma schema from extended workflow models using the reusable builder
     */
    generatePrismaSchemaFromModels(workflow, provider, outputDir) {
        const schema = prisma_schema_builder_1.PrismaSchemaBuilder.generateSchema(workflow, {
            provider,
            includeGenerator: true,
            includeDatasource: true,
        });
        return {
            path: path.join(outputDir, 'database', 'prisma', 'schema.prisma'),
            content: schema,
            overwrite: true,
        };
    }
}
exports.PrismaGenerator = PrismaGenerator;
//# sourceMappingURL=prisma-generator.js.map