import {
  OrderNotFoundError,
  type OrderEventPublisher,
  type OrderServicePort,
  type OrderState,
} from '../../orders/index.js';
import type { PaymentRepository } from '../domain/ports.js';
import type { PaymentEvent, PaymentTransaction } from '../domain/payment.js';

export type PaymentOutcome =
  | { kind: 'processed'; transaction: PaymentTransaction }
  | { kind: 'duplicate'; transaction: PaymentTransaction };

export interface PaymentsServiceDeps {
  repository: PaymentRepository;
  orders: OrderServicePort;
  events: OrderEventPublisher;
  provider: string;
}

export class PaymentsService {
  constructor(private readonly deps: PaymentsServiceDeps) {}

  async handleWebhook(event: PaymentEvent): Promise<PaymentOutcome> {
    const existing = await this.deps.repository.findByProviderEvent(
      this.deps.provider,
      event.providerEventId,
    );
    if (existing) {
      return { kind: 'duplicate', transaction: existing };
    }

    const recorded = await this.deps.repository.recordEvent({
      provider: this.deps.provider,
      event,
    });
    if (!recorded) {
      const dup = await this.deps.repository.findByProviderEvent(
        this.deps.provider,
        event.providerEventId,
      );
      if (dup) return { kind: 'duplicate', transaction: dup };
      throw new Error('Failed to record payment event');
    }

    if (event.orderId) {
      try {
        if (event.type === 'PaymentSucceeded') {
          await this.deps.orders.transition(event.orderId, 'PAID' as OrderState, event.orderId);
          await this.deps.events.emit({
            type: 'OrderPaid',
            orderId: event.orderId,
            userId: event.orderId,
          });
        } else if (event.type === 'PaymentFailed') {
          await this.deps.orders
            .transition(event.orderId, 'CANCELLED' as OrderState, event.orderId)
            .catch(() => undefined);
          await this.deps.events.emit({
            type: 'OrderCancelled',
            orderId: event.orderId,
            userId: event.orderId,
          });
        }
      } catch (error) {
        if (error instanceof OrderNotFoundError) {
          // Order can be missing for provider events on closed carts; ignore.
        } else {
          throw error;
        }
      }
    }

    return { kind: 'processed', transaction: recorded };
  }
}
