/**
 * SessionLifetime Value Object
 *
 * Represents the lifetime of an authentication session with expiry tracking.
 * Handles validation of session expiry dates with a configurable buffer window.
 */
import type { IValueObject } from '@core/shared/domain';

export interface SessionLifetimeProps {
  expiry: Date | null;
  bufferMinutes: number;
}

export class SessionLifetime implements IValueObject<SessionLifetimeProps> {
  private readonly expiry: Date | null;
  private readonly bufferMinutes: number;

  constructor(expiry: Date | null, bufferMinutes: number = 5) {
    if (expiry !== null) {
      if (isNaN(expiry.getTime())) {
        throw new Error('Invalid expiry date provided');
      }

      // Allow dates within buffer window to support checking expiry of recently expired sessions
      const bufferMs = bufferMinutes * 60 * 1000;
      const expiryWithBuffer = expiry.getTime() + bufferMs;
      if (expiryWithBuffer < Date.now()) {
        throw new Error('Expiry date cannot be in the past');
      }
    }

    this.expiry = expiry;
    this.bufferMinutes = bufferMinutes;
  }

  /**
   * Check if the session is expired.
   * Considers the buffer time - sessions within the buffer window are treated as expired.
   *
   * @returns true if expired or expiring soon (within buffer), false otherwise
   */
  isExpired(): boolean {
    if (this.expiry === null) {
      return false;
    }

    const bufferMs = this.bufferMinutes * 60 * 1000;
    const expiryWithBuffer = this.expiry.getTime() - bufferMs;
    return Date.now() >= expiryWithBuffer;
  }

  /**
   * Check if the session is expiring soon (within buffer window).
   *
   * @returns true if expiring within buffer window, false otherwise
   */
  isExpiringSoon(): boolean {
    if (this.expiry === null) {
      return false;
    }

    const bufferMs = this.bufferMinutes * 60 * 1000;
    const now = Date.now();
    const expiryTime = this.expiry.getTime();
    const expiryWithBuffer = expiryTime - bufferMs;

    return now >= expiryWithBuffer && now < expiryTime;
  }

  /**
   * Get the expiry date.
   *
   * @returns The expiry date or null if no expiration
   */
  getExpiry(): Date | null {
    return this.expiry;
  }

  /**
   * Get remaining time until expiry in milliseconds.
   *
   * @returns Milliseconds until expiry, or Infinity if no expiration
   */
  getRemainingTime(): number {
    if (this.expiry === null) {
      return Infinity;
    }
 
    const remaining = this.expiry.getTime() - Date.now();
    return Math.max(0, remaining);
  }

  get props(): SessionLifetimeProps {
    return {
      expiry: this.expiry,
      bufferMinutes: this.bufferMinutes,
    };
  }

  equals(other: IValueObject<SessionLifetimeProps>): boolean {
    const a = this.props;
    const b = other.props;
    const aExpiry = a.expiry?.getTime() ?? null;
    const bExpiry = b.expiry?.getTime() ?? null;
    return aExpiry === bExpiry && a.bufferMinutes === b.bufferMinutes;
  }
}