import {
  Inject,
  Injectable,
  Logger,
  OnModuleInit,
  Optional,
} from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
import { PrismaClientOptions } from '@prisma/client/runtime/library';

import { PRISMA_SERVICE_OPTIONS } from './constants';
import { PrismaServiceOptions } from './prisma-module.options';

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  private readonly logger = new Logger('Prisma');

  constructor(
    @Optional()
    @Inject(PRISMA_SERVICE_OPTIONS)
    private readonly prismaServiceOptions: PrismaServiceOptions = {},
  ) {
    super({
      ...prismaServiceOptions.prismaOptions,
      // Make sure queries are always emitted as events, so that we can log them.
      log: [
        { emit: 'event', level: 'query' },
        ...(prismaServiceOptions.prismaOptions?.log
          ? prismaServiceOptions.prismaOptions.log
          : (['info', 'warn', 'error'] as PrismaClientOptions['log'])),
      ],
    });

    if (this.prismaServiceOptions.middlewares) {
      this.prismaServiceOptions.middlewares.forEach((middleware) =>
        this.$use(middleware),
      );
    }

    /* 
      Sadly typescript does not recognize the Prisma Client configuration passed in the super call.
      As Prisma defines the events generically, based on the configuration, the event type, typescript inferred, is wrong. 
     */
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    //@ts-ignore
    this.$on('query', (e: QueryEvent) => {
      const { timestamp, query, params, duration } = e;
      const message = `${timestamp.toISOString()} ${query} ${params} ${duration}`;
      // this.logger.log(message);
    });
  }

  async onModuleInit() {
    if (this.prismaServiceOptions.explicitConnect) {
      await this.$connect();
    }
  }

  // async enableShutdownHooks(app: INestApplication) {
  //   this.$on('beforeExit', async () => await app.close());
  // }
}
