import {
  Injectable,
  OnModuleInit,
  OnModuleDestroy,
  Logger,
} from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

// Models that support soft delete
const SOFT_DELETE_MODELS = ['user', 'role', 'tenant', 'project'];

// Type for Prisma model delegate with common operations
interface PrismaDelegate {
  delete: (args: { where: Record<string, unknown> }) => Promise<unknown>;
  findMany: (args?: Record<string, unknown>) => Promise<unknown[]>;
  update: (args: {
    where: Record<string, unknown>;
    data: Record<string, unknown>;
  }) => Promise<unknown>;
}

@Injectable()
export class PrismaService
  extends PrismaClient
  implements OnModuleInit, OnModuleDestroy
{
  private readonly logger = new Logger(PrismaService.name);

  constructor() {
    super({
      log: [
        { emit: 'event', level: 'query' },
        { emit: 'event', level: 'error' },
        { emit: 'event', level: 'warn' },
      ],
    });
  }

  async onModuleInit() {
    this.logger.log(
      `Connecting to database... URL: ${process.env.DATABASE_URL?.split('@')[1]}`,
    ); // Mask password
    try {
      await this.$connect();
      this.logger.log('✅ Database connected successfully');
    } catch (error) {
      this.logger.error(
        `❌ Database connection failed: ${error.message}`,
        error.stack,
      );
      throw error;
    }
  }

  async onModuleDestroy() {
    await this.$disconnect();
    this.logger.log('🔌 Database disconnected');
  }

  /**
   * Check if model has soft delete (deletedAt field)
   */
  hasSoftDelete(model: string | undefined): boolean {
    return model ? SOFT_DELETE_MODELS.includes(model.toLowerCase()) : false;
  }

  /**
   * Hard delete - actually remove from database
   */
  hardDelete<T>(model: string, where: Record<string, unknown>): Promise<T> {
    const delegate = this.getModelDelegate(model);
    return delegate.delete({ where }) as Promise<T>;
  }

  /**
   * Find including soft deleted records
   */
  findWithDeleted<T>(
    model: string,
    args?: Record<string, unknown>,
  ): Promise<T[]> {
    const delegate = this.getModelDelegate(model);
    return delegate.findMany(args) as Promise<T[]>;
  }

  /**
   * Restore a soft deleted record
   */
  restore<T>(model: string, where: Record<string, unknown>): Promise<T> {
    const delegate = this.getModelDelegate(model);
    return delegate.update({
      where,
      data: { deletedAt: null },
    }) as Promise<T>;
  }

  /**
   * Soft delete - set deletedAt to current date
   */
  softDelete<T>(model: string, where: Record<string, unknown>): Promise<T> {
    const delegate = this.getModelDelegate(model);
    return delegate.update({
      where,
      data: { deletedAt: new Date() },
    }) as Promise<T>;
  }

  /**
   * Find many excluding soft deleted records
   */
  findManyActive<T>(
    model: string,
    args?: Record<string, unknown>,
  ): Promise<T[]> {
    const delegate = this.getModelDelegate(model);
    const whereWithDeleted = {
      ...args,
      where: {
        ...(args?.where as Record<string, unknown> | undefined),
        deletedAt: null,
      },
    };
    return delegate.findMany(whereWithDeleted) as Promise<T[]>;
  }

  /**
   * Get Prisma model delegate by name
   */
  private getModelDelegate(model: string): PrismaDelegate {
    const modelKey = model.charAt(0).toLowerCase() + model.slice(1);

    return (this as any)[modelKey] as PrismaDelegate;
  }
}
