import { Injectable } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { filter, Observable, Subject } from 'rxjs';
import { logger } from '@isa/core/logging';
import { ShoppingCart } from './models';
import { isEqual } from 'lodash';

export const ShoppingCartEvent = {
  Created: 'ShoppingCartCreated',
  ItemAdded: 'ShoppingCartItemAdded',
  ItemRemoved: 'ShoppingCartItemRemoved',
  ItemUpdated: 'ShoppingCartItemUpdated',
  Completed: 'ShoppingCartCompleted',
} as const;

export type ShoppingCartEvent =
  (typeof ShoppingCartEvent)[keyof typeof ShoppingCartEvent];

export type ShoppingCartEventSource =
  | 'ShoppingCartService'
  | 'DomainCheckoutService';

export interface ShoppingCartEventPayload {
  event: ShoppingCartEvent;
  shoppingCart: ShoppingCart;
  source: ShoppingCartEventSource;
}

// TODO: Replace with a centralized event-bus system in the future
// This class currently uses RxJS Subject for event management, but should be
// migrated to a unified event-bus architecture that can handle events across
// the entire application (not just shopping cart events).
// Consider using a pattern like:
// - Global EventBus service with typed event channels
// - Event namespacing (e.g., 'checkout.cart.itemAdded')
// - Better event replay/history capabilities
// - Integration with NgRx effects or similar state management patterns
@Injectable({ providedIn: 'root' })
export class ShoppingCartEvents {
  #logger = logger(() => ({ service: 'ShoppingCartEvents' }));
  #subject = new Subject<ShoppingCartEventPayload>();

  #lastEvent: ShoppingCartEventPayload | null = null;

  readonly events$ = this.#subject.asObservable();

  readonly events = toSignal(this.events$);

  pub(
    event: ShoppingCartEvent,
    shoppingCart: ShoppingCart,
    source: ShoppingCartEventSource,
  ): void {
    this.#logger.info('Publishing shopping cart event', () => ({
      event,
      shoppingCartId: shoppingCart.id,
      source,
      itemCount: shoppingCart.items?.length ?? 0,
    }));

    if (isEqual(this.#lastEvent?.shoppingCart, shoppingCart)) {
      this.#logger.debug('Duplicate event detected, skipping publish', () => ({
        event,
        shoppingCartId: shoppingCart.id,
        source,
      }));
      return;
    }

    this.#subject.next({ event, shoppingCart, source });
    this.#lastEvent = { event, shoppingCart, source };
  }

  sub(
    event: ShoppingCartEvent | ShoppingCartEvent[],
    shoppingCartId: number,
    source?: ShoppingCartEventSource,
  ): Observable<ShoppingCartEventPayload> {
    const coercedEvents = Array.isArray(event) ? event : [event];

    this.#logger.debug('Subscribing to shopping cart events', () => ({
      events: coercedEvents,
      shoppingCartId,
      source: source ?? 'any',
    }));

    return this.events$.pipe(
      filter(
        (payload) =>
          payload.shoppingCart.id === shoppingCartId &&
          coercedEvents.includes(payload.event) &&
          (source === undefined || payload.source === source),
      ),
    );
  }
}
