import { NotificationStatus } from '../entities/notification.entity';

/**
 * Valid status transitions:
 *   PENDING -> SENT
 *   PENDING -> FAILED
 *   SENT    -> READ
 *   FAILED  -> PENDING  (retry)
 */
const ALLOWED_TRANSITIONS: Record<NotificationStatus, NotificationStatus[]> = {
  [NotificationStatus.PENDING]: [NotificationStatus.SENT, NotificationStatus.FAILED],
  [NotificationStatus.SENT]: [NotificationStatus.READ],
  [NotificationStatus.FAILED]: [NotificationStatus.PENDING],
  [NotificationStatus.READ]: [],
};

/**
 * Value Object for Notification Status.
 * Encapsulates status validation and enforces valid state transitions.
 */
export class NotificationStatusVO {
  private constructor(private readonly _value: NotificationStatus) {}

  static create(status: NotificationStatus): NotificationStatusVO {
    return new NotificationStatusVO(status);
  }

  static pending(): NotificationStatusVO {
    return new NotificationStatusVO(NotificationStatus.PENDING);
  }

  get value(): NotificationStatus {
    return this._value;
  }

  get isPending(): boolean {
    return this._value === NotificationStatus.PENDING;
  }

  get isSent(): boolean {
    return this._value === NotificationStatus.SENT;
  }

  get isFailed(): boolean {
    return this._value === NotificationStatus.FAILED;
  }

  get isRead(): boolean {
    return this._value === NotificationStatus.READ;
  }

  get isTerminal(): boolean {
    return this._value === NotificationStatus.READ;
  }

  /**
   * Transition to a new status, enforcing valid transitions.
   * @throws Error if the transition is not allowed.
   */
  transitionTo(newStatus: NotificationStatus): NotificationStatusVO {
    const allowed = ALLOWED_TRANSITIONS[this._value];
    if (!allowed.includes(newStatus)) {
      throw new Error(
        `Invalid status transition: "${this._value}" -> "${newStatus}". ` +
        `Allowed transitions from "${this._value}": [${allowed.join(', ')}]`,
      );
    }
    return new NotificationStatusVO(newStatus);
  }

  /**
   * Check if transition to the given status is valid without throwing.
   */
  canTransitionTo(newStatus: NotificationStatus): boolean {
    return ALLOWED_TRANSITIONS[this._value].includes(newStatus);
  }

  equals(other: NotificationStatusVO): boolean {
    return this._value === other._value;
  }

  toString(): string {
    return this._value;
  }
}
