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

import {
  CLASS_ACTIVE,
  CLASS_PAGINATION_CIRCLE,
  CLASS_PAGINATION_ITEM,
  CLASS_PAGINATION_SVG,
  CLASS_PAUSED,
  CLASS_PLAYING,
  CLASS_SLIDE,
  CLASS_TRACK,
  SELECTOR_NEXT,
  SELECTOR_PAGINATION,
  SELECTOR_PLAY_PAUSE,
  SELECTOR_PREV,
  SELECTOR_TAB,
  SELECTOR_TABS,
  SELECTOR_VIEWPORT,
} from "./constants";

export const defaultConfig: SwiperOptions = {
  pagination: {
    clickable: true,
    bulletClass: CLASS_PAGINATION_ITEM,
    bulletActiveClass: "is-active",
    renderBullet: function (index: number, className: string) {
      return `<button type="button" class="${className}" aria-label="Prejsť na snímok ${index + 1}"></button>`;
    },
  },
  slidesPerView: 1,
  loop: false,
  a11y: {
    enabled: true,
    prevSlideMessage: "Predchádzajúci snímok",
    nextSlideMessage: "Nasledujúci snímok",
    containerMessage: "Hero carousel so snímkami",
    containerRoleDescriptionMessage: "carousel",
    itemRoleDescriptionMessage: "snímok",
    firstSlideMessage: "Prvý snímok",
    lastSlideMessage: "Posledný snímok",
    slideLabelMessage: "Snímok",
  },
  wrapperClass: CLASS_TRACK,
  slideClass: CLASS_SLIDE,
  slideActiveClass: CLASS_ACTIVE,
};

export default class CarouselHero {
  element: HTMLElement;
  config: SwiperOptions;
  viewport!: HTMLElement;
  instance!: Swiper;
  tabs: HTMLElement[] = [];
  _dotAnimationJustStarted: boolean = false;
  _isDragging: boolean = false;
  private resizeRafId?: number;

  private _boundTabClick!: (e: Event) => void;
  private _boundPrevClick!: () => void;
  private _boundNextClick!: () => void;
  private _boundPlayPauseClick!: () => void;
  private _boundWindowResize!: () => void;

  constructor(element: HTMLElement, config?: Partial<SwiperOptions>) {
    this.element = element;
    this.config = { ...defaultConfig, ...config };
    (this.element as any).ODS_CarouselHero = this;
    this._boundTabClick = this._onTabClick.bind(this);
    this._boundPrevClick = this._onUserNavigation.bind(this);
    this._boundNextClick = this._onUserNavigation.bind(this);
    this._boundPlayPauseClick = this._onPlayPauseClick.bind(this);
    this._boundWindowResize = this._onWindowResize.bind(this);
    this.init();
    return this;
  }

  init() {
    this.getElements();
    this.setupConfig();
    this.createSwiper();
    this.setupEventListeners();
    this.renderPaginationDots();
    this.updateStates();
    this.updatePlayPauseIcon();

    if (this.hasAutoplay() && this.isAutoplayRunning()) {
      this.startDotAnimation();
    }

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

  private _onWindowResize() {
    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.updateStates();
      this.updatePlayPauseIcon();
    });
  }

  getElements() {
    this.viewport = this.element.querySelector(SELECTOR_VIEWPORT)!;
    const tabsContainer = this.element.querySelector(SELECTOR_TABS);
    if (tabsContainer) {
      this.tabs = Array.from(tabsContainer.querySelectorAll(SELECTOR_TAB));
    }
  }

  setupConfig() {
    const interval = this.element.hasAttribute("data-interval")
      ? parseInt(this.element.getAttribute("data-interval")!) || 0
      : 0;

    if (typeof this.config.a11y === "object" && this.config.a11y !== null) {
      this.config.a11y = {
        ...this.config.a11y,
        slideRole: this.tabs.length > 0 ? "tabpanel" : "group",
      };
    }

    if (this.element.hasAttribute("data-swiper-options")) {
      try {
        const customOptions = JSON.parse(
          this.element.getAttribute("data-swiper-options")!,
        );
        this.config = { ...this.config, ...customOptions };
      } catch (error) {
        console.warn("Invalid swiper options:", error);
      }
    }

    // Use rewind instead of loop to avoid DOM reordering at wrap points.
    // Loop clones make tab navigation jump through extra slides after a wrap.
    this.config.loop = false;
    this.config.rewind = true;

    // Setup autoplay if interval is specified
    if (interval >= 1000) {
      this.config.autoplay = {
        delay: interval,
        disableOnInteraction: false,
        pauseOnMouseEnter: false,
        waitForTransition: true,
        stopOnLastSlide: false,
      } as NonNullable<SwiperOptions["autoplay"]>;
      this.element.classList.add(CLASS_PLAYING);
    }
  }

  createSwiper() {
    const slides = this.viewport.querySelectorAll(`.${CLASS_SLIDE}`);
    const hasPagination = slides.length > 1;

    this.instance = new Swiper(this.viewport, {
      ...this.config,
      modules: [Navigation, Pagination, A11y, Keyboard, Autoplay],
      navigation: {
        nextEl: this.element.querySelector(SELECTOR_NEXT) as HTMLElement,
        prevEl: this.element.querySelector(SELECTOR_PREV) as HTMLElement,
      },
      pagination: hasPagination
        ? {
            ...(this.config.pagination as object),
            el: this.element.querySelector(SELECTOR_PAGINATION) as HTMLElement,
          }
        : false,
      on: {
        slideChange: () => {
          this.updateStates();
          if (this.isAutoplayRunning()) {
            this.restartDotAnimation();
          } else {
            this.stopDotAnimation();
          }
        },
        autoplayStart: () => {
          this._onAutoplayStart();
        },
        autoplayStop: () => {
          this._onAutoplayStop();
        },
        touchStart: () => {
          this._isDragging = true;
          this._stopAutoplayIfRunning();
        },
        touchEnd: () => {
          this._isDragging = false;
        },
      },
    });

    this.updatePlayPauseIcon();
  }

  setupEventListeners() {
    this.tabs.forEach((tab) => {
      tab.removeEventListener("click", this._boundTabClick);
      tab.addEventListener("click", this._boundTabClick);
    });

    const playPauseButton = this.element.querySelector(SELECTOR_PLAY_PAUSE);
    if (playPauseButton) {
      playPauseButton.removeEventListener("click", this._boundPlayPauseClick);
      playPauseButton.addEventListener("click", this._boundPlayPauseClick);
    }

    const prevButton = this.element.querySelector(SELECTOR_PREV);
    const nextButton = this.element.querySelector(SELECTOR_NEXT);
    if (prevButton) {
      prevButton.removeEventListener("click", this._boundPrevClick);
      prevButton.addEventListener("click", this._boundPrevClick);
    }
    if (nextButton) {
      nextButton.removeEventListener("click", this._boundNextClick);
      nextButton.addEventListener("click", this._boundNextClick);
    }
  }

  private _onUserNavigation() {
    this._stopAutoplayIfRunning();
    this.stopDotAnimation();
  }

  _onTabClick(e: Event) {
    e.preventDefault();
    const tab = e.currentTarget as HTMLElement;
    const index = this.tabs.indexOf(tab);
    if (index !== -1 && index !== this.instance?.realIndex) {
      this._onUserNavigation();
      this.goToSlide(index);
    }
  }

  private _onPlayPauseClick() {
    this.toggleAutoplay();
  }

  // ====== Autoplay helpers ======
  private isAutoplayRunning(): boolean {
    return !!(this.instance?.autoplay && this.instance.autoplay.running);
  }

  private _stopAutoplayIfRunning() {
    if (this.isAutoplayRunning()) {
      this.instance.autoplay.stop();
    }
  }

  private _onAutoplayStart() {
    this.element.classList.add(CLASS_PLAYING);
    this.element.classList.remove(CLASS_PAUSED);
    this.updatePlayPauseIcon();
    this.startDotAnimation();
  }

  private _onAutoplayStop() {
    this.element.classList.remove(CLASS_PLAYING);
    this.element.classList.add(CLASS_PAUSED);
    this.updatePlayPauseIcon();
    this.stopDotAnimation();
  }

  toggleAutoplay() {
    if (!this.instance || !this.instance.autoplay) return;

    if (this.isAutoplayRunning()) {
      this.instance.autoplay.stop();
    } else {
      // Update states to sync tabs and pagination with current slide position
      this.updateStates();

      // Small delay to ensure slide position is stable before starting autoplay
      setTimeout(() => {
        if (this.instance && this.instance.autoplay) {
          this.instance.autoplay.start();
        }
      }, 10);
    }
  }

  goToSlide(index: number) {
    if (!this.instance || this.instance.realIndex === index) return;

    if (this.instance.params.loop) {
      this.instance.slideToLoop(index);
      return;
    }

    this.instance.slideTo(index);
  }

  updateStates() {
    if (!this.instance) return;

    // Use realIndex for loop mode, fallback to activeIndex
    const realIndex = this.instance.realIndex ?? this.instance.activeIndex ?? 0;

    // Update tab states
    this.tabs.forEach((tab, index) => {
      const isActive = index === realIndex;
      tab.classList.toggle(CLASS_ACTIVE, isActive);
      tab.setAttribute("aria-selected", String(isActive));
      tab.setAttribute("tabindex", isActive ? "0" : "-1");
    });

    this.scrollActiveTabIntoView(realIndex);

    // Manually update pagination bullets
    this.updatePaginationBullets(realIndex);
  }

  private updatePaginationBullets(activeIndex: number) {
    const paginationEl = this.element.querySelector(SELECTOR_PAGINATION);
    if (!paginationEl) return;

    const bullets = paginationEl.querySelectorAll(`.${CLASS_PAGINATION_ITEM}`);

    bullets.forEach((bullet, index) => {
      const isActive = index === activeIndex;
      bullet.classList.toggle("is-active", isActive);

      // For carousels without autoplay, manually show/hide the SVG
      if (!this.hasAutoplay()) {
        const svg = bullet.querySelector(
          `.${CLASS_PAGINATION_SVG}`,
        ) as HTMLElement;
        if (svg) {
          svg.style.display = isActive ? "block" : "none";
        }
      }
    });
  }
  scrollActiveTabIntoView(activeIndex: number) {
    if (!this.tabs.length || activeIndex < 0 || activeIndex >= this.tabs.length)
      return;

    const activeTab = this.tabs[activeIndex];
    const tabsContainer = this.element.querySelector(
      SELECTOR_TABS,
    ) as HTMLElement;
    if (!tabsContainer || !activeTab) return;

    const containerRect = tabsContainer.getBoundingClientRect();
    const tabRect = activeTab.getBoundingClientRect();
    const fadeWidth = 56;
    const containerVisibleWidth = containerRect.width - fadeWidth;

    if (
      tabRect.right > containerRect.left + containerVisibleWidth ||
      tabRect.left < containerRect.left
    ) {
      const scrollLeft =
        tabRect.left -
        containerRect.left -
        containerVisibleWidth / 2 +
        tabRect.width / 2;
      tabsContainer.scrollBy({ left: scrollLeft, behavior: "smooth" });
    }
  }

  updatePlayPauseIcon() {
    const playPauseButton = this.element.querySelector(SELECTOR_PLAY_PAUSE);
    if (!playPauseButton) return;

    const useElement = playPauseButton.querySelector("use");
    if (useElement) {
      const isPlaying = this.isAutoplayRunning();
      const newIcon = isPlaying ? "pause" : "play";
      const currentHref = useElement.getAttribute("xlink:href") || "";
      if (!currentHref.endsWith(`#${newIcon}`)) {
        const newHref = currentHref.replace(/#[\w-]+$/, `#${newIcon}`);
        useElement.setAttribute("xlink:href", newHref);
      }
    }
  }

  renderPaginationDots() {
    const paginationButtons = this._getPaginationButtons();

    paginationButtons.forEach((button) => {
      const svg = this.createSvgDot();
      button.innerHTML = "";
      button.appendChild(svg);
    });
  }

  startDotAnimation(force: boolean = false) {
    if (!this.hasAutoplay()) return;
    if (!force && !this.isAutoplayRunning()) return;
    if (this._dotAnimationJustStarted) return;

    this._dotAnimationJustStarted = true;

    const activeButton = this._getActiveBulletButton();
    if (!activeButton) {
      this._dotAnimationJustStarted = false;
      return;
    }

    const svg = activeButton.querySelector(
      `.${CLASS_PAGINATION_SVG}`,
    ) as SVGSVGElement | null;
    if (!svg) {
      this._dotAnimationJustStarted = false;
      return;
    }

    const circle = svg.querySelector("circle") as SVGCircleElement | null;
    if (!circle) {
      this._dotAnimationJustStarted = false;
      return;
    }

    const duration = this.getAnimationDuration();
    circle.style.animation = "none";
    // Force reflow
    void activeButton.offsetHeight;
    circle.style.animation = `countdown linear ${duration}ms 1 forwards`;
    this._dotAnimationJustStarted = false;
  }
  restartDotAnimation() {
    this.stopDotAnimation();
    setTimeout(() => {
      this.startDotAnimation();
    }, 50);
  }

  stopDotAnimation() {
    const paginationButtons = this._getPaginationButtons();
    paginationButtons.forEach((button) => {
      const svg = button.querySelector(
        `.${CLASS_PAGINATION_SVG}`,
      ) as SVGSVGElement | null;
      if (!svg) return;
      const circle = svg.querySelector("circle") as SVGCircleElement | null;
      if (!circle) return;
      circle.style.animation = "none";
    });
    this._dotAnimationJustStarted = false;
  }

  private _getPaginationButtons(): HTMLElement[] {
    return Array.from(
      this.element.querySelectorAll(`${SELECTOR_PAGINATION} > *`),
    ) as HTMLElement[];
  }

  private _getActiveBulletClass(): string {
    const pagination = (this.instance?.params as any)?.pagination;
    if (
      pagination &&
      typeof pagination === "object" &&
      pagination.bulletActiveClass
    ) {
      return String(pagination.bulletActiveClass);
    }
    const cfgPagination = this.config.pagination as any;
    if (
      cfgPagination &&
      typeof cfgPagination === "object" &&
      cfgPagination.bulletActiveClass
    ) {
      return String(cfgPagination.bulletActiveClass);
    }
    return CLASS_ACTIVE;
  }

  private _getActiveBulletButton(): HTMLElement | null {
    const activeClass = this._getActiveBulletClass();
    return this.element.querySelector(
      `${SELECTOR_PAGINATION} .${activeClass}`,
    ) as HTMLElement | null;
  }

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

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

    svg.appendChild(circle);
    return svg;
  }

  // ====== Autoplay config helpers ======
  hasAutoplay(): boolean {
    return !!(
      this.config.autoplay &&
      typeof this.config.autoplay === "object" &&
      (this.config.autoplay as any).delay >= 1000
    );
  }

  getAnimationDuration(): number {
    return (this.config.autoplay as any)?.delay || 0;
  }

  destroy() {
    this.tabs.forEach((tab) => {
      tab.removeEventListener("click", this._boundTabClick);
    });

    const playPauseButton = this.element.querySelector(SELECTOR_PLAY_PAUSE);
    if (playPauseButton) {
      playPauseButton.removeEventListener("click", this._boundPlayPauseClick);
    }

    const prevButton = this.element.querySelector(SELECTOR_PREV);
    const nextButton = this.element.querySelector(SELECTOR_NEXT);
    if (prevButton) {
      prevButton.removeEventListener("click", this._boundPrevClick);
    }
    if (nextButton) {
      nextButton.removeEventListener("click", this._boundNextClick);
    }

    window.removeEventListener("resize", this._boundWindowResize);

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

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

    delete (this.element as any).ODS_CarouselHero;
  }

  static getInstance(el: HTMLElement): CarouselHero | null {
    return (el as any).ODS_CarouselHero || null;
  }
}
