import { Injectable, Logger, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
import { Prisma, PrismaClient } from '@prisma/client';
import { RealtimeEventsService } from '../realtime/realtime-events.service';

@Injectable()
export class PrismaService
  extends PrismaClient
  implements OnModuleInit, OnModuleDestroy
{
  private readonly logger = new Logger(PrismaService.name);
  private readonly writeActions = new Set<string>([
    'create',
    'update',
    'upsert',
    'delete',
    'createMany',
    'updateMany',
    'deleteMany',
  ]);

  private static isTruthy(value?: string): boolean {
    if (!value) {
      return false;
    }

    const normalized = value.trim().toLowerCase();
    return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';
  }

  private static resolveLogConfig(): Prisma.LogLevel[] {
    const includeQueryLogs = PrismaService.isTruthy(process.env.PRISMA_LOG_QUERIES);

    if (includeQueryLogs) {
      return ['query', 'warn', 'error'];
    }

    return ['warn', 'error'];
  }

  constructor(private readonly realtimeEvents: RealtimeEventsService) {
    super({
      log: PrismaService.resolveLogConfig(),
    });

    const realtimeMiddleware: Prisma.Middleware = async (params, next) => {
      const result = await next(params);

      if (params.model && this.writeActions.has(params.action)) {
        this.realtimeEvents.notifyDatabaseWrite();
      }

      return result;
    };

    this.$use(realtimeMiddleware);
  }

  async onModuleInit() {
    const maxAttempts = 10;
    const delayMs = 3000;

    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
      try {
        this.logger.log(
          `Försöker ansluta till databasen (försök ${attempt}/${maxAttempts})...`,
        );

        await this.$connect();

        if (PrismaService.isTruthy(process.env.PRISMA_LOG_QUERIES)) {
          this.logger.log('Prisma query logging är aktiverad (PRISMA_LOG_QUERIES=1).');
        }

        this.logger.log('Databasanslutning etablerad.');
        return;
      } catch (error) {
        const message =
          error instanceof Error ? error.message : 'Okänt fel';

        this.logger.warn(
          `Databasanslutning misslyckades på försök ${attempt}/${maxAttempts}: ${message}`,
        );

        if (attempt === maxAttempts) {
          this.logger.error(
            'Kunde inte ansluta till databasen efter maximalt antal försök.',
          );
          throw error;
        }

        await new Promise((resolve) => setTimeout(resolve, delayMs));
      }
    }
  }

  async onModuleDestroy() {
    await this.$disconnect();
  }
}
