/**
 * Prisma Schema Builder Utility
 * Reusable utility for generating Prisma schemas from workflow models
 * Can be used by any generator (backend, database, etc.)
 */

export interface PrismaSchemaOptions {
  provider?: string;
  outputPath?: string;
  includeGenerator?: boolean;
  includeDatasource?: boolean;
}

export class PrismaSchemaBuilder {
  /**
   * Generate a complete Prisma schema from extended workflow models
   */
  static generateSchema(workflow: any, options: PrismaSchemaOptions = {}): string {
    const {
      provider = 'postgresql',
      includeGenerator = true,
      includeDatasource = true,
    } = options;

    let schema = '// Prisma Schema\n// Generated from Bangstack workflow\n\n';

    // Add generator block
    if (includeGenerator) {
      schema += `generator client {
  provider = "prisma-client-js"
}

`;
    }

    // Add datasource block
    if (includeDatasource) {
      schema += `datasource db {
  provider = "${provider}"
  url      = env("DATABASE_URL")
}

`;
    }

    // Generate models from workflow
    if (!workflow.models || Object.keys(workflow.models).length === 0) {
      return schema;
    }

    for (const [modelName, modelDef] of Object.entries(workflow.models as Record<string, any>)) {
      schema += this.generateModel(modelName, modelDef, workflow);
    }

    return schema;
  }

  /**
   * Generate a single Prisma model from workflow model definition
   */
  private static generateModel(modelName: string, modelDef: any, workflow: any): string {
    let model = `model ${modelName} {\n`;

    // Auto-generate ID field if not present
    const hasId = Object.entries(modelDef.fields).some(([name, field]: [string, any]) =>
      name === 'id' || field.primary_key
    );

    if (!hasId) {
      model += '  id        String   @id @default(uuid())\n';
    }

    // Collect foreign key fields to match with relationships
    const foreignKeyFields: Map<string, any> = new Map();
    for (const [fieldName, fieldDef] of Object.entries(modelDef.fields as Record<string, any>)) {
      if (fieldDef.foreign_key) {
        foreignKeyFields.set(fieldName, fieldDef);
      }
    }

    // Generate fields
    for (const [fieldName, fieldDef] of Object.entries(modelDef.fields as Record<string, any>)) {
      // Skip system timestamp fields - we'll add them later
      if (fieldDef.system_field && (fieldName === 'created_at' || fieldName === 'updated_at')) {
        continue;
      }

      // Skip foreign key fields - they're handled separately with relations
      if (fieldDef.foreign_key) {
        continue;
      }

      model += this.generateField(fieldName, fieldDef);
    }

    // Generate foreign key fields with their relations
    for (const [fkFieldName, fkFieldDef] of foreignKeyFields) {
      const relatedModel = fkFieldDef.foreign_key;
      const prismaType = this.mapFieldTypeToPrisma(fkFieldDef.type);
      const isOptional = !fkFieldDef.required;

      // Generate FK field
      model += `  ${fkFieldName} ${prismaType}${isOptional ? '?' : ''}\n`;

      // Generate relation field
      const relationFieldName = this.toCamelCase(relatedModel);
      model += `  ${relationFieldName} ${relatedModel}${isOptional ? '?' : ''} @relation(fields: [${fkFieldName}], references: [id])\n`;
    }

    // Generate forward relationships (one-to-many arrays)
    if (workflow.relationships) {
      for (const rel of workflow.relationships) {
        if (rel.from === modelName) {
          // Forward relationship: User → Project (User.projects)
          const fieldName = this.toCamelCase(rel.to) + 's'; // Pluralize
          model += `  ${fieldName} ${rel.to}[]\n`;
        }
      }
    }

    // Add timestamps if enabled (default: true)
    if (modelDef.timestamps !== false) {
      const hasCreatedAt = Object.keys(modelDef.fields).includes('created_at');
      const hasUpdatedAt = Object.keys(modelDef.fields).includes('updated_at');

      if (!hasCreatedAt) {
        model += '  created_at DateTime @default(now())\n';
      }
      if (!hasUpdatedAt) {
        model += '  updated_at DateTime @updatedAt\n';
      }
    }

    // Add soft delete field if enabled
    if (modelDef.soft_delete) {
      model += '  deleted_at DateTime?\n';
    }

    // Add indexes
    if (modelDef.indexes && modelDef.indexes.length > 0) {
      model += '\n';
      for (const index of modelDef.indexes) {
        const indexFields = index.fields.join(', ');

        // Skip unique indexes if the field already has @unique decorator
        if (index.unique && index.fields.length === 1) {
          const fieldName = index.fields[0];
          const field = modelDef.fields[fieldName];
          if (field && field.unique) {
            continue; // Skip - field already has @unique
          }
        }

        if (index.unique) {
          model += `  @@unique([${indexFields}])\n`;
        } else {
          model += `  @@index([${indexFields}])\n`;
        }
      }
    }

    model += '}\n\n';
    return model;
  }

  /**
   * Generate a Prisma field from workflow field definition
   */
  private static generateField(fieldName: string, fieldDef: any): string {
    const prismaType = this.mapFieldTypeToPrisma(fieldDef.type);
    const isOptional = !fieldDef.required;
    let line = `  ${fieldName} ${prismaType}${isOptional ? '?' : ''}`;

    const modifiers: string[] = [];

    // Handle primary key
    if (fieldDef.primary_key || fieldName === 'id') {
      modifiers.push('@id');
      if (fieldDef.type === 'uuid' || prismaType === 'String') {
        modifiers.push('@default(uuid())');
      } else if (prismaType === 'Int') {
        modifiers.push('@default(autoincrement())');
      }
    }

    // Handle unique
    if (fieldDef.unique) {
      modifiers.push('@unique');
    }

    // Handle default values
    if (fieldDef.default !== undefined && !fieldDef.primary_key) {
      if (fieldDef.type === 'timestamp' || fieldDef.type === 'date' || fieldDef.type === 'datetime') {
        modifiers.push('@default(now())');
      } else if (typeof fieldDef.default === 'string') {
        modifiers.push(`@default("${fieldDef.default}")`);
      } else if (typeof fieldDef.default === 'boolean' || typeof fieldDef.default === 'number') {
        modifiers.push(`@default(${fieldDef.default})`);
      }
    }

    if (modifiers.length > 0) {
      line += ' ' + modifiers.join(' ');
    }

    line += '\n';
    return line;
  }

  /**
   * Map workflow field types to Prisma types
   */
  private static mapFieldTypeToPrisma(type: string): string {
    const typeMap: Record<string, string> = {
      uuid: 'String',
      string: 'String',
      text: 'String',
      email: 'String',
      password: 'String',
      integer: 'Int',
      number: 'Int',
      float: 'Float',
      decimal: 'Decimal',
      boolean: 'Boolean',
      date: 'DateTime',
      datetime: 'DateTime',
      timestamp: 'DateTime',
      json: 'Json',
      enum: 'String',
    };

    return typeMap[type.toLowerCase()] || 'String';
  }

  private static toCamelCase(str: string): string {
    return str.charAt(0).toLowerCase() + str.slice(1);
  }
}
