/**
 * Prisma Client Singleton
 *
 * Manages database connections with proper singleton pattern,
 * connection pooling, logging, and error handling.
 */

import { PrismaClient } from '@prisma/client';
import { logger } from '../utils/logger';

/**
 * Prisma Client extended with logging configuration
 */
class PrismaClientExtended extends PrismaClient {
  constructor() {
    super({
      log: [
        {
          emit: 'event',
          level: 'query',
        },
        {
          emit: 'event',
          level: 'error',
        },
        {
          emit: 'event',
          level: 'warn',
        },
      ],
      errorFormat: 'pretty',
    });

    // Log queries in development
    if (process.env.NODE_ENV === 'development') {
      this.$on('query' as never, (e: any) => {
        logger.debug({
          query: e.query,
          params: e.params,
          duration: `${e.duration}ms`,
        }, 'Database Query');
      });
    }

    // Log errors
    this.$on('error' as never, (e: any) => {
      logger.error({
        message: e.message,
        target: e.target,
      }, 'Database Error');
    });

    // Log warnings
    this.$on('warn' as never, (e: any) => {
      logger.warn({
        message: e.message,
      }, 'Database Warning');
    });
  }

  /**
   * Graceful shutdown helper
   */
  async disconnect(): Promise<void> {
    try {
      await this.$disconnect();
      logger.info('Database connection closed');
    } catch (error) {
      logger.error({ error }, 'Error closing database connection');
      throw error;
    }
  }

  /**
   * Health check
   */
  async healthCheck(): Promise<boolean> {
    try {
      await this.$queryRaw`SELECT 1`;
      return true;
    } catch (error) {
      logger.error({ error }, 'Database health check failed');
      return false;
    }
  }

  /**
   * Batch operations helper
   */
  async batchCreate<T extends { create: any[] }>(
    model: any,
    data: T,
    batchSize: number = 100
  ): Promise<void> {
    const chunks = this.chunkArray(data.create, batchSize);

    for (const chunk of chunks) {
      await model.createMany({
        data: chunk,
        skipDuplicates: true,
      });
    }
  }

  /**
   * Helper to chunk arrays
   */
  private chunkArray<T>(array: T[], size: number): T[][] {
    const chunks: T[][] = [];
    for (let i = 0; i < array.length; i += size) {
      chunks.push(array.slice(i, i + size));
    }
    return chunks;
  }
}

/**
 * Global Prisma client instance (singleton pattern)
 */
const globalForPrisma = global as unknown as {
  prisma: PrismaClientExtended | undefined;
};

export const prisma =
  globalForPrisma.prisma ??
  new PrismaClientExtended();

if (process.env.NODE_ENV !== 'production') {
  globalForPrisma.prisma = prisma;
}

/**
 * Disconnect Prisma client (useful for testing and graceful shutdown)
 */
export async function disconnectPrisma(): Promise<void> {
  await prisma.disconnect();
}

/**
 * Check database health
 */
export async function checkDatabaseHealth(): Promise<boolean> {
  return await prisma.healthCheck();
}

export default prisma;
