import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static values = {
    dismissAfter: Number,
    showDelay: { type: Number, default: 0 },
  };

  connect() {
    this.closing = false;
    this.showTimer = window.setTimeout(() => {
      this.showTimer = undefined;
      void this.transition(true);
    }, this.showDelayValue);

    if (this.hasDismissAfterValue) {
      this.dismissTimer = window.setTimeout(() => {
        this.dismissTimer = undefined;
        this.close();
      }, this.dismissAfterValue);
    }
  }

  disconnect() {
    this.clearTimers();
  }

  close() {
    if (this.closing) {
      return;
    }

    this.closing = true;
    this.clearTimers();
    void this.transition(false).finally(() => this.element.remove());
  }

  async transition(show) {
    const prefix = show ? "Enter" : "Leave";
    const transitionClasses = this.element.dataset[`transition${prefix}`] || (show ? "enter" : "leave");
    const fromClasses = this.element.dataset[`transition${prefix}From`] || (show ? "enter-from" : "leave-from");
    const toClasses = this.element.dataset[`transition${prefix}To`] || (show ? "enter-to" : "leave-to");
    const toggleClass = this.element.dataset.toggleClass || "hidden";

    this.element.classList.add(...transitionClasses.split(" "));
    this.element.classList.add(...fromClasses.split(" "));
    this.element.classList.remove(...toClasses.split(" "));
    if (show) {
      this.element.classList.remove(...toggleClass.split(" "));
    }

    await this.nextFrame();

    this.element.classList.remove(...fromClasses.split(" "));
    this.element.classList.add(...toClasses.split(" "));
    try {
      await Promise.all(this.element.getAnimations().map((animation) => animation.finished));
    } catch (error) {
      if (error?.name !== "AbortError") {
        throw error;
      }
    }

    this.element.classList.remove(...transitionClasses.split(" "));
    if (!show) {
      this.element.classList.add(...toggleClass.split(" "));
    }
  }

  nextFrame() {
    return new Promise((resolve) => {
      requestAnimationFrame(() => requestAnimationFrame(resolve));
    });
  }

  clearTimers() {
    window.clearTimeout(this.showTimer);
    window.clearTimeout(this.dismissTimer);
    this.showTimer = undefined;
    this.dismissTimer = undefined;
  }
}
