import { RovingTabindex } from "../../utils/keyboard";

interface AccordionConfig {
  buttonSelector: string;
}

const defaultConfig: AccordionConfig = {
  buttonSelector: "[data-accordion-toggle]",
};

export default class Accordion {
  private element: HTMLElement;
  private config: AccordionConfig;
  private rovingTabindex!: RovingTabindex;
  private buttons!: HTMLElement[];

  constructor(element: HTMLElement, config?: Partial<AccordionConfig>) {
    this.element = element;
    this.config = { ...defaultConfig, ...config };

    this.onFocus = this.onFocus.bind(this);
    this.onClick = this.onClick.bind(this);

    (this.element as any).ODS_Accordion = this;

    this.init();

    return this;
  }

  private onFocus(e: FocusEvent): void {
    if (!this.rovingTabindex.isActive) {
      this.rovingTabindex.init();
    }

    const button = e.currentTarget as HTMLElement;

    if (
      window.location.hash.substring(1) === button.getAttribute("id") &&
      button.getAttribute("aria-expanded") === "false"
    ) {
      button.click();
    }
  }

  private toggleAccordionItem(button: HTMLElement): void {
    const sectionId = button.getAttribute("aria-controls");
    const section = sectionId
      ? this.element.querySelector<HTMLElement>(`#${sectionId}`)
      : null;

    if (!section) return;

    // Get current state
    const isExpanded = button.getAttribute("aria-expanded") === "true";
    const newState = !isExpanded;

    // Update button aria-expanded
    button.setAttribute("aria-expanded", String(newState));

    // Toggle section visibility
    if (newState) {
      section.removeAttribute("hidden");
      this.scrollTriggerIntoModalView(button);
    } else {
      section.setAttribute("hidden", "");
    }
  }

  private scrollTriggerIntoModalView(button: HTMLElement): void {
    const modalDialog = button.closest<HTMLElement>(".modal__dialog");

    if (!modalDialog) return;

    const scrollToTrigger = () => {
      const dialogRect = modalDialog.getBoundingClientRect();
      const buttonRect = button.getBoundingClientRect();
      const stickyHeaderOffset = this.getStickyHeaderOffset(modalDialog);
      const gap = 8;
      const top =
        buttonRect.top -
        dialogRect.top +
        modalDialog.scrollTop -
        stickyHeaderOffset -
        gap;

      modalDialog.scrollTo({
        top: Math.max(0, top),
        behavior: "smooth",
      });
    };

    if (typeof window.requestAnimationFrame === "function") {
      window.requestAnimationFrame(scrollToTrigger);
      return;
    }

    setTimeout(scrollToTrigger, 0);
  }

  private getStickyHeaderOffset(modalDialog: HTMLElement): number {
    const modalHeader =
      modalDialog.querySelector<HTMLElement>(".modal__header");

    if (!modalHeader) return 0;

    const dialogRect = modalDialog.getBoundingClientRect();
    const headerRect = modalHeader.getBoundingClientRect();
    return Math.max(0, headerRect.bottom - dialogRect.top);
  }

  private init(): void {
    this.buttons = Array.from(
      this.element.querySelectorAll(this.config.buttonSelector),
    );

    if (!this.rovingTabindex) {
      this.rovingTabindex = new RovingTabindex(this.buttons, {
        direction: "vertical",
      });
    } else {
      this.rovingTabindex.update(this.buttons);
    }

    this.buttons.forEach((button) => {
      button.addEventListener("focus", this.onFocus);
      button.addEventListener("click", this.onClick);
    });
  }

  private onClick(e: Event): void {
    const button = e.currentTarget as HTMLElement;
    this.toggleAccordionItem(button);
  }

  destroy(): void {
    this.rovingTabindex.destroy();

    this.buttons.forEach((button) => {
      button.removeEventListener("focus", this.onFocus);
      button.removeEventListener("click", this.onClick);
    });
  }

  update(): void {
    this.destroy();
    this.init();
  }

  static getInstance(el: HTMLElement): Accordion | null {
    return el && (el as any).ODS_Accordion ? (el as any).ODS_Accordion : null;
  }
}
