import { Injectable, Logger, NotFoundException, ForbiddenException } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../../database/prisma.service';
import { EventsGateway } from '../events/events.gateway';

/**
 * Notifications Service — In-App Bildirim Yönetimi
 *
 * Render sonuçları, düşük kredi uyarıları ve sistem bildirimleri
 * için veritabanı + WebSocket push altyapısı.
 *
 * Flow:
 * 1. createNotification() → DB'ye yaz + WebSocket push (anlık)
 * 2. Frontend: WebSocket dinle + API ile liste çek
 * 3. Kullanıcı: okundu işaretle / sil
 */

export type NotificationType =
  | 'render_complete'
  | 'render_failed'
  | 'credit_low'
  | 'credit_added'
  | 'system';

export interface CreateNotificationPayload {
  userId: string;
  type: NotificationType;
  title: string;
  message?: string;
  metadata?: Record<string, unknown>;
}

@Injectable()
export class NotificationsService {
  private readonly logger = new Logger(NotificationsService.name);

  constructor(
    private readonly db: PrismaService,
    private readonly eventsGateway: EventsGateway,
  ) {}

  /**
   * Bildirim oluştur — DB'ye yaz + WebSocket ile anlık push.
   */
  async createNotification(payload: CreateNotificationPayload) {
    const notification = await this.db.notification.create({
      data: {
        userId: payload.userId,
        type: payload.type,
        title: payload.title,
        message: payload.message || null,
        metadata: payload.metadata
          ? (payload.metadata as Prisma.InputJsonValue)
          : Prisma.JsonNull,
        isRead: false,
      },
    });

    // WebSocket ile kullanıcıya anlık bildirim gönder
    this.eventsGateway.emitNotification(payload.userId, {
      id: notification.id,
      type: notification.type,
      title: notification.title,
      message: notification.message,
      metadata: notification.metadata,
      isRead: false,
      createdAt: notification.createdAt.toISOString(),
    });

    this.logger.debug(
      `Bildirim oluşturuldu: [${payload.type}] "${payload.title}" → User: ${payload.userId}`,
    );

    return notification;
  }

  /**
   * Kullanıcının bildirimlerini getir (pagination).
   */
  async getUserNotifications(
    userId: string,
    page = 1,
    limit = 20,
  ) {
    const skip = (page - 1) * limit;

    const [notifications, total] = await Promise.all([
      this.db.notification.findMany({
        where: { userId },
        orderBy: { createdAt: 'desc' },
        skip,
        take: limit,
        select: {
          id: true,
          type: true,
          title: true,
          message: true,
          isRead: true,
          metadata: true,
          createdAt: true,
          readAt: true,
        },
      }),
      this.db.notification.count({ where: { userId } }),
    ]);

    return {
      data: notifications,
      meta: {
        total,
        page,
        limit,
        totalPages: Math.ceil(total / limit),
      },
    };
  }

  /**
   * Okunmamış bildirim sayısı.
   */
  async getUnreadCount(userId: string): Promise<{ count: number }> {
    const count = await this.db.notification.count({
      where: { userId, isRead: false },
    });
    return { count };
  }

  /**
   * Tekil bildirimi okundu olarak işaretle.
   */
  async markAsRead(notificationId: string, userId: string) {
    const notification = await this.db.notification.findUnique({
      where: { id: notificationId },
    });

    if (!notification) {
      throw new NotFoundException('Bildirim bulunamadı');
    }

    if (notification.userId !== userId) {
      throw new ForbiddenException('Bu bildirime erişim izniniz yok');
    }

    return this.db.notification.update({
      where: { id: notificationId },
      data: {
        isRead: true,
        readAt: new Date(),
      },
    });
  }

  /**
   * Kullanıcının tüm bildirimlerini okundu olarak işaretle.
   */
  async markAllAsRead(userId: string) {
    const result = await this.db.notification.updateMany({
      where: { userId, isRead: false },
      data: {
        isRead: true,
        readAt: new Date(),
      },
    });

    this.logger.debug(`${result.count} bildirim okundu işaretlendi — User: ${userId}`);

    return { updated: result.count };
  }

  /**
   * Bildirimi sil.
   */
  async deleteNotification(notificationId: string, userId: string) {
    const notification = await this.db.notification.findUnique({
      where: { id: notificationId },
    });

    if (!notification) {
      throw new NotFoundException('Bildirim bulunamadı');
    }

    if (notification.userId !== userId) {
      throw new ForbiddenException('Bu bildirime erişim izniniz yok');
    }

    await this.db.notification.delete({
      where: { id: notificationId },
    });

    return { deleted: true };
  }

  /**
   * Eski bildirimleri temizle (30 günden eski, okunmuş olanlar).
   * Cron job veya admin endpoint'i ile çağrılabilir.
   */
  async cleanupOldNotifications(): Promise<{ deleted: number }> {
    const thirtyDaysAgo = new Date();
    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

    const result = await this.db.notification.deleteMany({
      where: {
        isRead: true,
        createdAt: { lt: thirtyDaysAgo },
      },
    });

    this.logger.log(`Eski bildirim temizliği: ${result.count} adet silindi`);
    return { deleted: result.count };
  }
}
