import { logger } from "../observability/logger.js";
import { producer } from "../../config/kafka.js";
import { outboxRepository } from "./outbox.repository.js";
import type { OutboxMessage } from "./outbox.schema.js";

const POLL_INTERVAL_MS = 5000;
const BATCH_SIZE = 100;
const MAX_RETRY = 5;

export class OutboxPublisher {
  private intervalId: ReturnType<typeof setInterval> | null = null;
  private isPolling = false;

  async start(): Promise<void> {
    logger.info("OutboxPublisher started");
    this.intervalId = setInterval(() => {
      void this.poll();
    }, POLL_INTERVAL_MS);
  }

  async stop(): Promise<void> {
    if (this.intervalId) {
      clearInterval(this.intervalId);
      this.intervalId = null;
    }
    logger.info("OutboxPublisher stopped");
  }

  private async poll(): Promise<void> {
    if (this.isPolling) return;
    this.isPolling = true;
    try {
      const messages = await outboxRepository.findPending(BATCH_SIZE);
      for (const message of messages) {
        await this.dispatch(message);
      }
    } catch (error) {
      logger.error({ error }, "Outbox poll failed");
    } finally {
      this.isPolling = false;
    }
  }

  private async dispatch(message: OutboxMessage): Promise<void> {
    try {
      await producer.send({
        topic: message.topic,
        messages: [
          {
            key: message.aggregateId,
            value: message.payload,
            headers: {
              eventId: message.id,
              eventType: message.eventType,
              aggregateType: message.aggregateType,
            },
          },
        ],
      });
      await outboxRepository.markPublished(message.id);
      logger.info(
        { id: message.id, eventType: message.eventType, topic: message.topic },
        "Outbox message published",
      );
    } catch (error) {
      const errorMessage =
        error instanceof Error ? error.message : String(error);
      logger.error(
        { error, id: message.id, eventType: message.eventType },
        "Outbox publish failed",
      );
      if (message.retryCount + 1 >= MAX_RETRY) {
        await outboxRepository.markFailed(message.id, errorMessage);
      } else {
        await outboxRepository.incrementRetry(message.id, errorMessage);
      }
    }
  }
}

export const outboxPublisher = new OutboxPublisher();
