/**
 * Prisma Schema Generator
 * Generates Prisma model definitions from schema specs
 */

import { PrismaModelDef, schemaToPrismaString } from '../../core/schema/generator';

export interface PrismaGenerationResult {
  modelCode: string;
  migrationName: string;
  affectedTables: string[];
}

/**
 * Generate Prisma schema code for multiple models
 */
export function generatePrismaModels(models: PrismaModelDef[]): string {
  return models.map(model => schemaToPrismaString(model)).join('\n\n');
}

/**
 * Validate that a model doesn't conflict with existing schema
 */
export async function validateModelAgainstSchema(
  model: PrismaModelDef,
  existingSchema: string
): Promise<{ valid: boolean; conflicts: string[] }> {
  const conflicts: string[] = [];
  
  // Check if model name already exists
  const modelRegex = new RegExp(`model\\s+${model.name}\\s*\\{`, 'i');
  if (modelRegex.test(existingSchema)) {
    conflicts.push(`Model "${model.name}" already exists in schema`);
  }
  
  // Check for conflicting field names in relations
  for (const field of model.fields) {
    if (field.type && !isPrimitiveType(field.type)) {
      // This is a relation field, check if target model exists
      const targetRegex = new RegExp(`model\\s+${field.type}\\s*\\{`, 'i');
      if (!targetRegex.test(existingSchema) && !models.some(m => m.name === field.type)) {
        // Target model doesn't exist - this might be fine if we're generating it
        // Just note it for now
      }
    }
  }
  
  return {
    valid: conflicts.length === 0,
    conflicts,
  };
}

const models: PrismaModelDef[] = []; // Will be populated during generation

function isPrimitiveType(type: string): boolean {
  const primitives = [
    'String', 'Int', 'Float', 'Boolean', 'DateTime', 
    'Json', 'Decimal', 'BigInt', 'Bytes'
  ];
  return primitives.includes(type);
}

/**
 * Generate a migration name based on the models being created
 */
export function generateMigrationName(models: PrismaModelDef[]): string {
  const timestamp = new Date().toISOString().replace(/[-:T.]/g, '').slice(0, 14);
  const modelNames = models.map(m => m.name.toLowerCase()).join('_');
  return `${timestamp}_add_${modelNames.slice(0, 50)}`;
}

/**
 * Generate a complete Prisma schema section with proper formatting
 */
export function generatePrismaSchemaSection(models: PrismaModelDef[]): PrismaGenerationResult {
  const lines: string[] = [
    '// ============================================',
    '// DARK FACTORY GENERATED MODELS',
    `// Generated: ${new Date().toISOString()}`,
    '// ============================================',
    '',
  ];
  
  for (const model of models) {
    lines.push(schemaToPrismaString(model));
    lines.push('');
  }
  
  return {
    modelCode: lines.join('\n'),
    migrationName: generateMigrationName(models),
    affectedTables: models.map(m => m.name),
  };
}

/**
 * Generate TypeScript types that mirror the Prisma models
 */
export function generateTypeScriptTypes(models: PrismaModelDef[]): string {
  const lines: string[] = [
    '/**',
    ' * Auto-generated TypeScript types',
    ' * Generated by Dark Factory',
    ' */',
    '',
  ];
  
  for (const model of models) {
    lines.push(`export interface ${model.name} {`);
    
    for (const field of model.fields) {
      const tsType = prismaToTsType(field.type, field.isArray);
      const optional = field.isOptional ? '?' : '';
      lines.push(`  ${field.name}${optional}: ${tsType};`);
    }
    
    lines.push('}');
    lines.push('');
    
    // Generate create/update input types
    lines.push(`export interface ${model.name}CreateInput {`);
    for (const field of model.fields) {
      if (['id', 'createdAt', 'updatedAt'].includes(field.name)) continue;
      if (field.name.endsWith('Id') && field.type === 'String') continue; // Skip FK fields in create
      if (!isPrimitiveType(field.type)) continue; // Skip relation fields
      
      const tsType = prismaToTsType(field.type, field.isArray);
      const optional = field.isOptional ? '?' : '';
      lines.push(`  ${field.name}${optional}: ${tsType};`);
    }
    lines.push('}');
    lines.push('');
    
    lines.push(`export interface ${model.name}UpdateInput {`);
    for (const field of model.fields) {
      if (['id', 'createdAt', 'updatedAt'].includes(field.name)) continue;
      if (!isPrimitiveType(field.type)) continue;
      
      const tsType = prismaToTsType(field.type, field.isArray);
      lines.push(`  ${field.name}?: ${tsType};`);
    }
    lines.push('}');
    lines.push('');
  }
  
  return lines.join('\n');
}

function prismaToTsType(prismaType: string, isArray: boolean): string {
  const mapping: Record<string, string> = {
    'String': 'string',
    'Int': 'number',
    'Float': 'number',
    'Decimal': 'number',
    'BigInt': 'bigint',
    'Boolean': 'boolean',
    'DateTime': 'Date',
    'Json': 'Record<string, unknown>',
    'Bytes': 'Buffer',
  };
  
  const tsType = mapping[prismaType] || prismaType;
  return isArray ? `${tsType}[]` : tsType;
}
