/*
 * Copyright © 2024 Hexastack. All rights reserved.
 *
 * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
 * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
 * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
 */

import { Injectable } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';

import { BaseRepository } from '@/utils/generics/base-repository';

import {
  Message,
  MESSAGE_POPULATE,
  MessageFull,
  MessagePopulate,
} from '../schemas/message.schema';
import { Subscriber } from '../schemas/subscriber.schema';
import { AnyMessage } from '../schemas/types/message';

@Injectable()
export class MessageRepository extends BaseRepository<
  AnyMessage,
  MessagePopulate,
  MessageFull
> {
  constructor(
    readonly eventEmitter: EventEmitter2,
    @InjectModel(Message.name) readonly model: Model<AnyMessage>,
  ) {
    super(
      eventEmitter,
      model,
      Message as new () => AnyMessage,
      MESSAGE_POPULATE,
      MessageFull,
    );
  }

  /**
   * Pre-create hook to validate message data before saving.
   * If the message is from a end-user (i.e., has a sender), it is saved
   * as an inbox NLP sample. Throws an error if neither sender nor recipient
   * is provided.
   *
   * @param _doc - The message document to be created.
   */
  async preCreate(_doc: AnyMessage): Promise<void> {
    if (_doc) {
      if (!('sender' in _doc) && !('recipient' in _doc)) {
        throw new Error('Either sender or recipient must be provided!');
      }
    }
  }

  /**
   * Retrieves the message history for a given subscriber, with messages sent or received
   * before the specified date. Results are limited and sorted by creation date.
   *
   * @param subscriber - The subscriber whose message history is being retrieved.
   * @param until - Optional date to retrieve messages sent before (default: current date).
   * @param limit - Optional limit on the number of messages to retrieve (default: 30).
   *
   * @returns The message history until the specified date.
   */
  async findHistoryUntilDate(
    subscriber: Subscriber,
    until = new Date(),
    limit: number = 30,
  ) {
    return await this.find(
      {
        $or: [{ recipient: subscriber.id }, { sender: subscriber.id }],
        createdAt: { $lt: until },
      },
      { skip: 0, limit, sort: ['createdAt', 'desc'] },
    );
  }

  /**
   * Retrieves the message history for a given subscriber, with messages sent or received
   * after the specified date. Results are limited and sorted by creation date.
   *
   * @param subscriber The subscriber whose message history is being retrieved.
   * @param since Optional date to retrieve messages sent after (default: current date).
   * @param limit Optional limit on the number of messages to retrieve (default: 30).
   *
   * @returns The message history since the specified date.
   */
  async findHistorySinceDate(
    subscriber: Subscriber,
    since = new Date(),
    limit: number = 30,
  ) {
    return await this.find(
      {
        $or: [{ recipient: subscriber.id }, { sender: subscriber.id }],
        createdAt: { $gt: since },
      },
      { skip: 0, limit, sort: ['createdAt', 'asc'] },
    );
  }
}
