import { Swiper } from "swiper";
import {
  A11y,
  Autoplay,
  Keyboard,
  Navigation,
  Pagination,
} from "swiper/modules";
import type { SwiperOptions } from "swiper/types";

import {
  CLASS_ACTIVE,
  CLASS_AUTOPLAY,
  CLASS_DOT,
  CLASS_DOT_CIRCLE,
  CLASS_DOT_SVG,
  CLASS_SHOWING_INVERTED_SLIDE,
  CLASS_SLIDE,
  CLASS_TRACK,
  SELECTOR_DOTS,
  SELECTOR_VIEWPORT,
} from "./constants";

export const defaultConfig: SwiperOptions = {
  pagination: {
    el: SELECTOR_DOTS,
    clickable: true,
    bulletClass: CLASS_DOT,
    bulletActiveClass: CLASS_ACTIVE,
    renderBullet: (index, className) => {
      return `<button aria-label="Stránka ${index + 1}" role="tab" class="${className}" type="button"></button>`;
    },
  },
  slidesPerView: 1,
  loop: true,
  a11y: {
    enabled: true,
    prevSlideMessage: "Predchádzajúci snímok",
    nextSlideMessage: "Nasledujúci snímok",
    paginationBulletMessage: "Prejsť na snímok {index}",
    containerMessage: "Carousel so snímkami",
    containerRoleDescriptionMessage: "carousel",
    itemRoleDescriptionMessage: "snímok",
    firstSlideMessage: "Prvý snímok",
    lastSlideMessage: "Posledný snímok",
    slideLabelMessage: "Snímok",
  },
  cssMode: false,
  direction: "horizontal",
  wrapperClass: CLASS_TRACK,
  slideClass: CLASS_SLIDE,
  slideActiveClass: CLASS_ACTIVE,
  autoplay: false,
};

export default class CarouselPromotions {
  element: HTMLElement;
  config: SwiperOptions;
  viewport!: HTMLElement;
  instance!: Swiper;
  interval: number = 0;
  autoplayTimer?: NodeJS.Timeout;
  private resizeRafId?: number;
  private boundWindowResizeHandler: () => void;
  private boundPaginationClickHandler: () => void;

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

    this.handleSlideChange = this.handleSlideChange.bind(this);
    this.scrollToNextSlide = this.scrollToNextSlide.bind(this);
    this.autoplayAnimationEnd = this.autoplayAnimationEnd.bind(this);
    this.boundWindowResizeHandler = this.handleWindowResize.bind(this);
    this.boundPaginationClickHandler = this.handlePaginationClick.bind(this);

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

    // Defer initialization to ensure DOM is ready
    // requestAnimationFrame(() => {
    this.init();
    // });
    return this;
  }

  init() {
    this.getElements();

    if (this.element.hasAttribute("data-swiper-options")) {
      this.getCustomOptions();
    }

    if (this.element.hasAttribute("data-interval")) {
      this.interval = parseInt(this.element.getAttribute("data-interval")!);
    }

    const isIntervalValid = this.isIntervalValid();

    if (isIntervalValid) {
      this.element.classList.add(CLASS_AUTOPLAY);
      this.config.autoplay = {
        delay: this.interval,
        disableOnInteraction: false,
      };
    } else {
      this.interval = 0;
      this.config.autoplay = false;
    }

    this.instance = new Swiper(this.viewport, {
      ...this.config,
      modules: [Navigation, Pagination, A11y, Keyboard, Autoplay],
      on: {
        slideChange: this.handleSlideChange,
        init: () => {
          this.fixPaginationButtonType();
          if (this.isAutoplayEnabled) {
            this.renderSvgDotsForAnimation();
          }
        },
        update: () => {
          this.fixPaginationButtonType();
          if (this.isAutoplayEnabled) {
            this.renderSvgDotsForAnimation();
          }
        },
      },
    });

    const paginationEl = this.element.querySelector(
      SELECTOR_DOTS,
    ) as HTMLElement | null;
    if (paginationEl && isIntervalValid) {
      paginationEl.addEventListener("animationend", this.autoplayAnimationEnd);
      paginationEl.addEventListener("click", this.boundPaginationClickHandler);
    }

    window.addEventListener("resize", this.boundWindowResizeHandler);
  }

  private handlePaginationClick() {
    this.stopAutoplay();
  }

  private handleWindowResize() {
    if (this.resizeRafId) {
      cancelAnimationFrame(this.resizeRafId);
    }

    this.resizeRafId = requestAnimationFrame(() => {
      this.resizeRafId = undefined;

      if (!this.instance || !this.viewport) {
        return;
      }

      if (this.viewport.clientWidth <= 0) {
        return;
      }

      this.instance.update();
      this.fixPaginationButtonType();

      if (this.isAutoplayEnabled) {
        this.renderSvgDotsForAnimation();
      }
    });
  }

  stopAutoplay() {
    this.interval = 0;
    this.element.classList.remove(CLASS_AUTOPLAY);
    if (this.instance && this.instance.autoplay) {
      this.instance.autoplay.stop();
    }
  }

  /**
   * Handles the slide change event on the carousel.
   * Updates the inverted slide class based on slide index.
   */
  handleSlideChange() {
    this.element.classList.remove(CLASS_SHOWING_INVERTED_SLIDE);
    if (this.instance && this.instance.activeIndex !== undefined) {
      const slideIndex = this.instance.activeIndex;
      if (slideIndex % 2 === 1) {
        this.element.classList.add(CLASS_SHOWING_INVERTED_SLIDE);
      }
    }
  }

  autoplayAnimationEnd(e: AnimationEvent) {
    if (e instanceof AnimationEvent && e.animationName === "countdown") {
      this.scrollToNextSlide();
    }
  }

  get isAutoplayEnabled() {
    return this.interval > 0;
  }

  isIntervalValid(): boolean {
    const interval = parseInt(String(this.interval));
    return !isNaN(interval) && interval >= 1000;
  }

  scrollToNextSlide() {
    if (!this.instance || !this.instance.slides) {
      return;
    }
    const slidesCount = this.instance.slides.length;
    if (slidesCount <= 0) {
      return;
    }
    const currentSlideIndex = this.instance.activeIndex || 0;
    this.instance.slideTo((currentSlideIndex + 1) % slidesCount);
  }

  getElements() {
    this.viewport = this.element.querySelector(SELECTOR_VIEWPORT)!;

    // Ensure pagination container has proper role
    const paginationEl = this.element.querySelector(
      SELECTOR_DOTS,
    ) as HTMLElement | null;
    if (paginationEl && !paginationEl.hasAttribute("role")) {
      paginationEl.setAttribute("role", "tablist");
    }

    const elements = {
      pagination: {
        ...((this.config.pagination ?? {}) as object),
        el: paginationEl,
        clickable: true,
        bulletClass: CLASS_DOT,
        bulletActiveClass: CLASS_ACTIVE,
      },
    };

    this.config = {
      ...this.config,
      ...elements,
    };
  }

  getCustomOptions() {
    const passedSwiperOptions = this.element.getAttribute(
      "data-swiper-options",
    );
    if (!passedSwiperOptions) return;
    const parsedSwiperOptions = JSON.parse(passedSwiperOptions);

    this.config = {
      ...this.config,
      ...parsedSwiperOptions,
    };
  }

  fixPaginationButtonType() {
    const dots = Array.from(
      this.element.querySelectorAll(`${SELECTOR_DOTS} > *`),
    );

    dots.forEach((dot) => (dot as HTMLElement).setAttribute("type", "button"));
  }

  static createSvgDot(): SVGSVGElement {
    const svgNS = "http://www.w3.org/2000/svg";
    const svg = document.createElementNS(svgNS, "svg");
    svg.setAttribute("class", CLASS_DOT_SVG);
    svg.setAttribute("width", "12");
    svg.setAttribute("height", "12");
    svg.setAttribute("viewBox", "0 0 12 12");
    svg.setAttribute("fill", "none");
    svg.setAttribute("xmlns", svgNS);

    const circle = document.createElementNS(svgNS, "circle");
    circle.setAttribute("class", CLASS_DOT_CIRCLE);
    circle.setAttribute("r", "5");
    circle.setAttribute("cx", "6");
    circle.setAttribute("cy", "6");

    svg.appendChild(circle);

    return svg;
  }

  renderSvgDotsForAnimation() {
    const dots = Array.from(
      this.element.querySelectorAll(`${SELECTOR_DOTS} > *`),
    );

    const svg = CarouselPromotions.createSvgDot();
    (svg.querySelector("circle") as SVGCircleElement).style.animationDuration =
      `${this.interval}ms`;

    dots.forEach((dot) => {
      dot.innerHTML = "";
      dot.appendChild(svg.cloneNode(true));
    });
  }

  destroy() {
    window.removeEventListener("resize", this.boundWindowResizeHandler);

    if (this.resizeRafId) {
      cancelAnimationFrame(this.resizeRafId);
      this.resizeRafId = undefined;
    }

    const paginationEl = this.element.querySelector(
      SELECTOR_DOTS,
    ) as HTMLElement | null;
    if (paginationEl) {
      paginationEl.removeEventListener(
        "animationend",
        this.autoplayAnimationEnd,
      );
      paginationEl.removeEventListener(
        "click",
        this.boundPaginationClickHandler,
      );
    }

    if (this.instance) {
      this.instance.destroy();
    }

    if ((this.element as any).ODS_CarouselPromotions === this) {
      delete (this.element as any).ODS_CarouselPromotions;
    }
  }

  update() {
    if (this.instance) {
      this.instance.update();
    }
  }

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