import { DateTime } from 'luxon';
import { floorDateTime } from '../../internal/datetime.mjs';

const TIMER_INTERVAL = 100;
class Ticker {
  constructor(tickInterval = { second: 5 }, zone) {
    this.subscribers = [];
    this.currentDateTime = DateTime.fromMillis(0);
    this.nextUpdateAt = DateTime.fromMillis(0);
    this.tickInterval = tickInterval;
    this.zone = zone;
  }
  subscribe(fn) {
    this.subscribers.push(fn);
    return {
      unsubscribe: () => {
        this.unsubscribe(fn);
      }
    };
  }
  unsubscribe(fn) {
    this.subscribers = this.subscribers.filter((subscriber) => subscriber !== fn);
  }
  start() {
    if (this.timer) {
      return;
    }
    this.stop();
    this.timer = setInterval(this.tick.bind(this), TIMER_INTERVAL);
    this.tick(true);
  }
  stop() {
    if (!this.timer) {
      return;
    }
    clearInterval(this.timer);
    this.timer = void 0;
    this.nextUpdateAt = DateTime.fromMillis(0);
    this.notifySubscribers(this.currentDateTime, "major");
  }
  setZone(zone) {
    this.zone = zone;
  }
  setTickInterval(tickInterval) {
    this.tickInterval = tickInterval;
  }
  /**
   * Tick the clock
   *
   * A major-rounded tick is forced when the clock is started to ensure
   * that the timer control updates its value as soon as 'play' is clicked,
   * rather than waiting for the next major tick.
   *
   * @param forceMajor
   */
  tick(forceMajor = false) {
    const now = DateTime.local({ zone: this.zone }).set({ millisecond: 0 });
    if (now >= this.nextUpdateAt) {
      this.nextUpdateAt = floorDateTime(now.plus({ second: 1 }), "second", 1);
      const majorStepUnit = Object.keys(this.tickInterval)[0];
      const majorStepValue = this.tickInterval[majorStepUnit];
      let minorFlooredNow = floorDateTime(now, "second", 1);
      const majorFlooredNow = floorDateTime(now, majorStepUnit, majorStepValue);
      if (forceMajor) {
        minorFlooredNow = majorFlooredNow;
      }
      if (!minorFlooredNow.equals(this.currentDateTime)) {
        this.currentDateTime = minorFlooredNow;
        this.notifySubscribers(this.currentDateTime, minorFlooredNow.equals(majorFlooredNow) ? "major" : "minor");
      }
    }
  }
  notifySubscribers(dateTime, tickType) {
    if (dateTime.isValid) {
      this.subscribers.forEach((subscriber) => subscriber(dateTime.toISO(), tickType));
    }
  }
}

export { Ticker };
