import { Injectable, OnModuleInit } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { AppLoggerService } from '../../infrastructure/logging/app-logger.service';
import { AppQueueService } from '../../infrastructure/queue/app-queue.service';
import { NotificationsService } from '../notifications/notifications.service';
import { OutboxEvent, OutboxEventDocument } from './schemas/outbox-event.schema';

@Injectable()
export class OutboxService implements OnModuleInit {
  private static readonly PROCESS_EVENT_JOB = 'process_outbox_event';

  constructor(
    @InjectModel(OutboxEvent.name) private readonly outboxEventModel: Model<OutboxEventDocument>,
    private readonly notificationsService: NotificationsService,
    private readonly queueService: AppQueueService,
    private readonly logger: AppLoggerService,
  ) {}

  onModuleInit(): void {
    this.queueService.registerProcessor(OutboxService.PROCESS_EVENT_JOB, async (payload) => {
      await this.processEvent(String(payload.eventId ?? ''));
    });
  }

  async enqueueFollowNotification(actorId: string, recipientId: string, referenceId?: string): Promise<void> {
    const event = await this.outboxEventModel.create({
      eventType: 'follow_notification',
      payload: {
        actorId,
        recipientId,
        referenceId: referenceId ?? '',
      },
      status: 'pending',
    });

    await this.queueService.enqueue(OutboxService.PROCESS_EVENT_JOB, { eventId: event.id });
  }

  async processEvent(eventId: string): Promise<void> {
    const event = await this.outboxEventModel.findById(eventId).exec();
    if (!event || event.status === 'processed') {
      return;
    }

    try {
      if (event.eventType === 'follow_notification') {
        await this.notificationsService.createFollowNotification(
          String(event.payload.actorId ?? ''),
          String(event.payload.recipientId ?? ''),
          String(event.payload.referenceId ?? ''),
        );
      }

      event.status = 'processed';
      event.processedAt = new Date();
      event.lastError = '';
    } catch (error) {
      event.status = 'failed';
      event.lastError = error instanceof Error ? error.message : 'unknown outbox error';
      this.logger.warn(
        {
          eventId: event.id,
          eventType: event.eventType,
          error: event.lastError,
        },
        OutboxService.name,
      );
    } finally {
      event.attempts += 1;
      await event.save();
    }
  }
}
