export type TransactionPaidPayload = {
  transaction_id: string;
  merchant_id: string;
  outlet_id: string;
  terminal_id: string;
  device_id: string | null | undefined;
  amount: number;
  currency: string;
  partner_reference: string;
  paid_at?: string;
};

type TransactionPaidInternalEvent = {
  id: string;
  event_type: "transaction.paid";
  transaction_id: string;
  payload_json: TransactionPaidPayload;
  created_at: string;
};

export type TransactionPaidEvent = TransactionPaidInternalEvent;

import { randomUUID } from "node:crypto";

const transactionPaidEvents = new Map<string, TransactionPaidInternalEvent[]>();
const transactionPaidIndex = new Map<string, string>();
const transactionPaidSubscribers = new Set<(event: TransactionPaidEvent) => void>();

function nowIso() {
  return new Date().toISOString();
}

function cloneInternalEvent(event: TransactionPaidInternalEvent): TransactionPaidInternalEvent {
  return {
    ...event,
    payload_json: { ...event.payload_json }
  };
}

export function subscribeTransactionPaid(handler: (event: TransactionPaidEvent) => void) {
  transactionPaidSubscribers.add(handler);
  return () => transactionPaidSubscribers.delete(handler);
}

export function emitTransactionPaid(payload: TransactionPaidPayload): TransactionPaidEvent {
  const existingId = transactionPaidIndex.get(payload.transaction_id);
  if (existingId) {
    const events = transactionPaidEvents.get(payload.transaction_id) || [];
    const existing = events.find((event) => event.id === existingId);
    if (existing) {
      return cloneInternalEvent(existing);
    }
  }

  const event: TransactionPaidInternalEvent = {
    id: randomUUID(),
    event_type: "transaction.paid",
    transaction_id: payload.transaction_id,
    payload_json: payload,
    created_at: nowIso()
  };

  const bucket = transactionPaidEvents.get(payload.transaction_id) || [];
  bucket.push(event);
  transactionPaidEvents.set(payload.transaction_id, bucket);
  transactionPaidIndex.set(payload.transaction_id, event.id);

  const frozen = cloneInternalEvent(event);
  for (const listener of Array.from(transactionPaidSubscribers)) {
    listener(frozen);
  }

  return frozen;
}

export function getTransactionPaidEvents(transactionId?: string): TransactionPaidEvent[] {
  if (transactionId) {
    return (transactionPaidEvents.get(transactionId) || []).map(cloneInternalEvent);
  }

  return Array.from(transactionPaidEvents.values()).flatMap((bucket) => bucket.map(cloneInternalEvent));
}

export function getTransactionPaidEventByTransactionId(transactionId: string): TransactionPaidEvent | null {
  const events = transactionPaidEvents.get(transactionId) || [];
  return events.length > 0 ? cloneInternalEvent(events[events.length - 1]) : null;
}
