import { Injectable, Logger } from '@nestjs/common';
import { NotificationChannel, NotificationStatus } from '@prisma/client';
import { PrismaService } from '../../prisma/prisma.service';
import {
  NotificationProvider,
  SmtpEmailProvider,
  InAppProvider,
} from './providers/notification-provider.interface';

export interface SendNotificationParams {
  tenantId?: string | null;
  userId?: string | null;
  channel: NotificationChannel;
  template: string;
  recipient: string;
  subject?: string;
  body: string;
  metadata?: Record<string, unknown>;
}

@Injectable()
export class NotificationsService {
  private readonly logger = new Logger(NotificationsService.name);
  private readonly providers: Map<NotificationChannel, NotificationProvider>;

  constructor(
    private readonly prisma: PrismaService,
    email: SmtpEmailProvider,
    inApp: InAppProvider,
  ) {
    this.providers = new Map<NotificationChannel, NotificationProvider>();
    this.providers.set(email.channel, email);
    this.providers.set(inApp.channel, inApp);
  }

  /**
   * Persist a notification and dispatch via the provider in a single flow.
   * On provider failure, record stays as FAILED with errorMessage.
   */
  async send(params: SendNotificationParams) {
    const notif = await this.prisma.notification.create({
      data: {
        tenantId: params.tenantId ?? null,
        userId: params.userId ?? null,
        channel: params.channel,
        template: params.template,
        subject: params.subject,
        body: params.body,
        recipient: params.recipient,
        metadata: (params.metadata as any) ?? undefined,
        status: NotificationStatus.QUEUED,
      },
    });

    const provider = this.providers.get(params.channel);
    if (!provider) {
      return this.prisma.notification.update({
        where: { id: notif.id },
        data: {
          status: NotificationStatus.FAILED,
          errorMessage: `No provider for channel ${params.channel}`,
        },
      });
    }

    try {
      const res = await provider.send({
        recipient: params.recipient,
        subject: params.subject,
        body: params.body,
        metadata: params.metadata,
      });
      return this.prisma.notification.update({
        where: { id: notif.id },
        data: {
          status: res.accepted ? NotificationStatus.SENT : NotificationStatus.FAILED,
          providerMessageId: res.providerMessageId,
          errorMessage: res.error,
          sentAt: res.accepted ? new Date() : null,
        },
      });
    } catch (err) {
      this.logger.error(`Notification ${notif.id} failed: ${(err as Error).message}`);
      return this.prisma.notification.update({
        where: { id: notif.id },
        data: { status: NotificationStatus.FAILED, errorMessage: (err as Error).message },
      });
    }
  }

  async listForUser(userId: string, limit = 50) {
    return this.prisma.notification.findMany({
      where: { userId },
      orderBy: { createdAt: 'desc' },
      take: limit,
    });
  }

  async markRead(userId: string, notificationId: string) {
    return this.prisma.notification.updateMany({
      where: { id: notificationId, userId },
      data: { status: NotificationStatus.READ, readAt: new Date() },
    });
  }
}
