/**
 * Notification Service
 *
 * Handles creating and sending notifications to users
 */

import { Injectable, Logger, Inject } from '@nestjs/common';
import { v4 as uuidv4 } from 'uuid';
import {
  Notification,
  NotificationType,
  NotificationPriority,
} from '@domain/entities/notification.entity';
import {
  NotificationRepository,
  NOTIFICATION_REPOSITORY,
  NotificationFilters,
} from '@domain/ports/out/notification.repository';

export interface CreateNotificationInput {
  userId: string;
  organizationId: string;
  type: NotificationType;
  priority: NotificationPriority;
  title: string;
  message: string;
  metadata?: Record<string, any>;
  actionUrl?: string;
}

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

  constructor(
    @Inject(NOTIFICATION_REPOSITORY)
    private readonly notificationRepository: NotificationRepository
  ) {}

  /**
   * Create and send a notification
   */
  async createNotification(input: CreateNotificationInput): Promise<Notification> {
    try {
      const notification = Notification.create({
        id: uuidv4(),
        ...input,
      });

      await this.notificationRepository.save(notification);

      this.logger.log(
        `Notification created: ${input.type} for user ${input.userId} - ${input.title}`
      );

      return notification;
    } catch (error: any) {
      this.logger.error(
        `Failed to create notification: ${error?.message || 'Unknown error'}`,
        error?.stack
      );
      throw error;
    }
  }

  /**
   * Get notifications with filters
   */
  async getNotifications(filters: NotificationFilters): Promise<{
    notifications: Notification[];
    total: number;
  }> {
    const [notifications, total] = await Promise.all([
      this.notificationRepository.findByFilters(filters),
      this.notificationRepository.count(filters),
    ]);

    return { notifications, total };
  }

  /**
   * Get notification by ID
   */
  async getNotificationById(id: string): Promise<Notification | null> {
    return this.notificationRepository.findById(id);
  }

  /**
   * Get unread notifications for a user
   */
  async getUnreadNotifications(userId: string, limit: number = 50): Promise<Notification[]> {
    return this.notificationRepository.findUnreadByUser(userId, limit);
  }

  /**
   * Get unread count for a user
   */
  async getUnreadCount(userId: string): Promise<number> {
    return this.notificationRepository.countUnreadByUser(userId);
  }

  /**
   * Get recent notifications for a user
   */
  async getRecentNotifications(userId: string, limit: number = 50): Promise<Notification[]> {
    return this.notificationRepository.findRecentByUser(userId, limit);
  }

  /**
   * Mark notification as read
   */
  async markAsRead(id: string): Promise<void> {
    await this.notificationRepository.markAsRead(id);
    this.logger.log(`Notification marked as read: ${id}`);
  }

  /**
   * Mark all notifications as read for a user
   */
  async markAllAsRead(userId: string): Promise<void> {
    await this.notificationRepository.markAllAsReadForUser(userId);
    this.logger.log(`All notifications marked as read for user: ${userId}`);
  }

  /**
   * Delete notification
   */
  async deleteNotification(id: string): Promise<void> {
    await this.notificationRepository.delete(id);
    this.logger.log(`Notification deleted: ${id}`);
  }

  /**
   * Cleanup old read notifications
   */
  async cleanupOldNotifications(olderThanDays: number = 30): Promise<number> {
    const deleted = await this.notificationRepository.deleteOldReadNotifications(olderThanDays);
    this.logger.log(`Cleaned up ${deleted} old read notifications`);
    return deleted;
  }

  /**
   * Helper methods for creating specific notification types
   */

  async notifyBookingCreated(
    userId: string,
    organizationId: string,
    bookingNumber: string,
    bookingId: string
  ): Promise<Notification> {
    return this.createNotification({
      userId,
      organizationId,
      type: NotificationType.BOOKING_CREATED,
      priority: NotificationPriority.MEDIUM,
      title: 'Booking Created',
      message: `Your booking ${bookingNumber} has been created successfully.`,
      metadata: { bookingId, bookingNumber },
      actionUrl: `/bookings/${bookingId}`,
    });
  }

  async notifyBookingUpdated(
    userId: string,
    organizationId: string,
    bookingNumber: string,
    bookingId: string,
    status: string
  ): Promise<Notification> {
    return this.createNotification({
      userId,
      organizationId,
      type: NotificationType.BOOKING_UPDATED,
      priority: NotificationPriority.MEDIUM,
      title: 'Booking Updated',
      message: `Booking ${bookingNumber} status changed to ${status}.`,
      metadata: { bookingId, bookingNumber, status },
      actionUrl: `/bookings/${bookingId}`,
    });
  }

  async notifyBookingConfirmed(
    userId: string,
    organizationId: string,
    bookingNumber: string,
    bookingId: string
  ): Promise<Notification> {
    return this.createNotification({
      userId,
      organizationId,
      type: NotificationType.BOOKING_CONFIRMED,
      priority: NotificationPriority.HIGH,
      title: 'Booking Confirmed',
      message: `Your booking ${bookingNumber} has been confirmed by the carrier.`,
      metadata: { bookingId, bookingNumber },
      actionUrl: `/bookings/${bookingId}`,
    });
  }

  async notifyDocumentUploaded(
    userId: string,
    organizationId: string,
    documentName: string,
    bookingId: string
  ): Promise<Notification> {
    return this.createNotification({
      userId,
      organizationId,
      type: NotificationType.DOCUMENT_UPLOADED,
      priority: NotificationPriority.LOW,
      title: 'Document Uploaded',
      message: `Document "${documentName}" has been uploaded for your booking.`,
      metadata: { documentName, bookingId },
      actionUrl: `/bookings/${bookingId}`,
    });
  }
}
