/**
 * Drives the open/close lifecycle of a slide-out panel inside a component's
 * shadow root, with a tracked close timer instead of an `animationend`
 * listener so the panel cannot wedge open if it's unmounted mid-animation
 * (pop-in/pop-out, dock change, host disconnect).
 *
 * @internal
 */
export class AnimatedPanelToggle {
  private timer?: ReturnType<typeof setTimeout>;

  constructor(
    private readonly host: HTMLElement,
    private readonly selector: string,
    private readonly closeDurationMs: number,
    private readonly getOpen: () => boolean,
    private readonly setOpen: (v: boolean) => void,
  ) {}

  private queryPanel(): HTMLElement | null {
    return this.host.shadowRoot?.querySelector(this.selector) as HTMLElement | null;
  }

  toggle(): void {
    if (this.getOpen()) {
      // Capture the panel reference now so the timer callback strips
      // `closing` from the same node, regardless of any later remounts.
      // Leftover `closing` is what breaks the next open: with
      // `animation-fill-mode: forwards` the slide-out keyframe pins the panel
      // to opacity 0, so a remount looks like a no-op.
      const panel = this.queryPanel();
      panel?.classList.add('closing');
      if (this.timer != null) clearTimeout(this.timer);
      this.timer = setTimeout(() => {
        this.timer = undefined;
        panel?.classList.remove('closing');
        this.setOpen(false);
      }, this.closeDurationMs);
    } else {
      // Re-opened mid-close: cancel the pending flip and strip the closing
      // class so the panel snaps back to the open state.
      if (this.timer != null) {
        clearTimeout(this.timer);
        this.timer = undefined;
        this.queryPanel()?.classList.remove('closing');
      }
      this.setOpen(true);
    }
  }

  /**
   * Synchronously finalise an in-flight close — call from
   * `disconnectedCallback` so the closed state lands in the store before any
   * session ref is cleared.
   */
  finalize(): void {
    if (this.timer == null) return;
    clearTimeout(this.timer);
    this.timer = undefined;
    this.setOpen(false);
  }
}
