import { Injectable } from '@nestjs/common';
import { type Prisma } from '@prisma/client';
import { PrismaService } from '@modules/shared';
import { type NotificationEntity, type NotificationStatus } from '../../domain/entities/notification.entity';
import {
  type INotificationRepository,
  type CreateNotificationDto,
} from '../../domain/repositories/notification.repository';

@Injectable()
export class PrismaNotificationRepository implements INotificationRepository {
  constructor(private readonly prisma: PrismaService) {}

  async create(dto: CreateNotificationDto): Promise<NotificationEntity> {
    const record = await this.prisma.notificationLog.create({
      data: {
        userId: dto.userId,
        channel: dto.channel,
        templateKey: dto.templateKey,
        subject: dto.subject,
        body: dto.body,
        metadata: (dto.metadata ?? undefined) as Prisma.InputJsonValue | undefined,
        status: 'PENDING',
      },
    });
    return this.toEntity(record);
  }

  async updateStatus(id: string, status: NotificationStatus, errorDetail?: string): Promise<void> {
    await this.prisma.notificationLog.update({
      where: { id },
      data: {
        status,
        errorDetail: errorDetail ?? null,
        sentAt: status === 'SENT' || status === 'DELIVERED' ? new Date() : undefined,
      },
    });
  }

  async findByUserId(userId: string, limit = 50): Promise<NotificationEntity[]> {
    const records = await this.prisma.notificationLog.findMany({
      where: { userId },
      orderBy: { createdAt: 'desc' },
      take: limit,
    });
    return records.map((r) => this.toEntity(r));
  }

  async findUnreadByUserId(userId: string, limit = 50): Promise<NotificationEntity[]> {
    const records = await this.prisma.notificationLog.findMany({
      where: { userId, readAt: null },
      orderBy: { createdAt: 'desc' },
      take: limit,
    });
    return records.map((r) => this.toEntity(r));
  }

  async countUnreadByUserId(userId: string): Promise<number> {
    return this.prisma.notificationLog.count({
      where: { userId, readAt: null },
    });
  }

  async markAsRead(id: string, userId: string): Promise<void> {
    await this.prisma.notificationLog.updateMany({
      where: { id, userId, readAt: null },
      data: { readAt: new Date() },
    });
  }

  async markAllAsRead(userId: string): Promise<number> {
    const result = await this.prisma.notificationLog.updateMany({
      where: { userId, readAt: null },
      data: { readAt: new Date() },
    });
    return result.count;
  }

  private toEntity(record: {
    id: string;
    userId: string;
    channel: string;
    templateKey: string;
    subject: string | null;
    body: string;
    metadata: unknown;
    status: string;
    errorDetail: string | null;
    sentAt: Date | null;
    readAt: Date | null;
    createdAt: Date;
  }): NotificationEntity {
    return {
      id: record.id,
      userId: record.userId,
      channel: record.channel as NotificationEntity['channel'],
      templateKey: record.templateKey,
      subject: record.subject,
      body: record.body,
      metadata: record.metadata as Record<string, unknown> | null,
      status: record.status as NotificationStatus,
      errorDetail: record.errorDetail,
      sentAt: record.sentAt,
      readAt: record.readAt,
      createdAt: record.createdAt,
    };
  }
}
