/**
 * App-facing time source.
 *
 * Use this port anywhere deterministic time matters, such as use-case tests,
 * scheduled work, idempotency windows, or audit timestamps.
 */
export interface ClockPort {
  /**
   * Return the current time according to this clock.
   */
  now(): Date;
}

/**
 * Mutable clock implementation used by tests.
 */
export interface FrozenClockPort extends ClockPort {
  /**
   * Replace the current time.
   */
  setNow(value: Date | string | number): void;
  /**
   * Move the current time forward by the given number of milliseconds.
   */
  advance(milliseconds: number): void;
}

function toDate(value: Date | string | number): Date {
  return value instanceof Date ? new Date(value.getTime()) : new Date(value);
}

/**
 * Create a clock backed by `new Date()`.
 *
 * Use this as the default production `ClockPort` when the app does not need a
 * provider-specific time source.
 *
 * @returns A clock that reads the current system time.
 */
export function createSystemClock(): ClockPort {
  return {
    now: () => new Date(),
  };
}

/**
 * Create a mutable clock for deterministic tests.
 *
 * @example
 * ```ts
 * const clock = createFrozenClock("2026-01-01T00:00:00.000Z");
 * clock.advance(1000);
 * ```
 *
 * @param initial - Initial time for the clock. Defaults to Unix epoch.
 * @returns A clock whose time can be replaced or advanced.
 */
export function createFrozenClock(
  initial: Date | string | number = new Date(0),
): FrozenClockPort {
  let current = toDate(initial);

  return {
    now: () => new Date(current.getTime()),
    setNow: (value) => {
      current = toDate(value);
    },
    advance: (milliseconds) => {
      current = new Date(current.getTime() + milliseconds);
    },
  };
}
