/**
 * @vitest-environment jsdom
 */

import { Swiper } from "swiper";
import Carousel from "../Carousel.static";

// Mock Swiper
vi.mock("swiper", () => ({
  Swiper: vi.fn().mockImplementation(function () {
    let enabled = true;
    return {
      slideNext: vi.fn(),
      slidePrev: vi.fn(),
      destroy: vi.fn(),
      update: vi.fn(),
      on: vi.fn(),
      enable: vi.fn(() => {
        enabled = true;
      }),
      disable: vi.fn(() => {
        enabled = false;
      }),
      get enabled() {
        return enabled;
      },
      activeIndex: 0,
      isBeginning: true,
      isEnd: false,
      slides: [{}, {}, {}],
      params: {
        slidesPerView: 1,
      },
      scrollbar: {
        updateSize: vi.fn(),
        el: document.createElement("div"),
      },
    };
  }),
}));

// Mock Swiper modules
vi.mock("swiper/modules", () => ({
  Navigation: {},
  Pagination: {},
  Scrollbar: {},
  A11y: {},
  Keyboard: {},
  Mousewheel: {},
}));

describe("Carousel Static - External Controls", () => {
  let carouselElement;
  let carouselInstance;
  let mockSwiperInstance;

  beforeEach(() => {
    // Clear all mocks
    vi.clearAllMocks();

    // Create mock Swiper instance
    let enabled = true;

    mockSwiperInstance = {
      slideNext: vi.fn(),
      slidePrev: vi.fn(),
      destroy: vi.fn(),
      update: vi.fn(),
      on: vi.fn(),
      enable: vi.fn(() => {
        enabled = true;
      }),
      disable: vi.fn(() => {
        enabled = false;
      }),
      get enabled() {
        return enabled;
      },
      activeIndex: 0,
      isBeginning: true,
      isEnd: false,
      slides: [{}, {}, {}],
      params: {
        slidesPerView: 1,
      },
      scrollbar: {
        updateSize: vi.fn(),
        el: document.createElement("div"),
      },
    };

    Swiper.mockImplementation(function () {
      return mockSwiperInstance;
    });

    // Set up DOM
    document.body.innerHTML = `
      <div class="carousel" data-carousel-id="test-carousel" id="test-carousel">
        <div class="carousel__viewport">
          <div class="carousel__track">
            <div class="carousel__slide">Slide 1</div>
            <div class="carousel__slide">Slide 2</div>
            <div class="carousel__slide">Slide 3</div>
          </div>
        </div>
        <div class="carousel__pagination"></div>
      </div>
      <button data-carousel-controls="test-carousel" data-carousel-action="prev" id="prev-btn">Previous</button>
      <button data-carousel-controls="test-carousel" data-carousel-action="next" id="next-btn">Next</button>
    `;

    carouselElement = document.querySelector(".carousel");

    // Mock requestAnimationFrame to execute synchronously
    global.requestAnimationFrame = vi.fn((cb) => cb());

    carouselInstance = new Carousel(carouselElement);
  });

  afterEach(() => {
    document.body.innerHTML = "";
    // Restore requestAnimationFrame
    global.requestAnimationFrame = undefined;
  });

  describe("External Controls Initialization", () => {
    it("should disable a11y scrollOnFocus to prevent focus-triggered slide jumps", () => {
      const swiperConfig = Swiper.mock.calls[0]?.[1];

      expect(swiperConfig?.a11y?.scrollOnFocus).toBe(false);
    });

    it("should find and initialize external controls", () => {
      const prevButton = document.getElementById("prev-btn");
      const nextButton = document.getElementById("next-btn");

      expect(prevButton.hasAttribute("data-carousel-initialized")).toBe(true);
      expect(nextButton.hasAttribute("data-carousel-initialized")).toBe(true);
      expect(prevButton.getAttribute("aria-label")).toBe(
        "Predchádzajúci snímok",
      );
      expect(nextButton.getAttribute("aria-label")).toBe("Nasledujúci snímok");
      expect(prevButton.getAttribute("type")).toBe("button");
      expect(nextButton.getAttribute("type")).toBe("button");
    });

    it("should not initialize controls without carousel ID", () => {
      // Create carousel without ID
      document.body.innerHTML = `
        <div class="carousel">
          <div class="carousel__viewport">
            <div class="carousel__track">
              <div class="carousel__slide">Slide 1</div>
            </div>
          </div>
        </div>
        <button data-carousel-controls="missing-carousel" data-carousel-action="next">Next</button>
      `;

      const element = document.querySelector(".carousel");
      new Carousel(element);
      const button = document.querySelector("button");

      expect(button.hasAttribute("data-carousel-initialized")).toBe(false);
    });

    it("should skip already initialized controls", () => {
      // Mark button as already initialized
      const prevButton = document.getElementById("prev-btn");
      prevButton.setAttribute("data-carousel-initialized", "true");

      // Create new carousel instance
      new Carousel(carouselElement);

      // Should not double-initialize
      expect(prevButton.getAttribute("data-carousel-initialized")).toBe("true");
    });
  });

  describe("External Controls Navigation", () => {
    it("should call slideNext when next button is clicked", () => {
      const nextButton = document.getElementById("next-btn");
      nextButton.click();

      expect(mockSwiperInstance.slideNext).toHaveBeenCalledTimes(1);
    });

    it("should call slidePrev when prev button is clicked", () => {
      // Ensure button is not disabled by setting carousel state
      mockSwiperInstance.isBeginning = false;
      mockSwiperInstance.isEnd = false;
      carouselInstance.updateExternalControlsState();

      const prevButton = document.getElementById("prev-btn");
      prevButton.click();

      expect(mockSwiperInstance.slidePrev).toHaveBeenCalledTimes(1);
    });

    it("should prevent default behavior on button click", () => {
      const nextButton = document.getElementById("next-btn");
      const mockEvent = new Event("click");
      mockEvent.preventDefault = vi.fn();

      nextButton.dispatchEvent(mockEvent);

      expect(mockEvent.preventDefault).toHaveBeenCalled();
    });

    it("should not navigate when button is disabled", () => {
      const nextButton = document.getElementById("next-btn");
      nextButton.setAttribute("disabled", "true");

      nextButton.click();

      expect(mockSwiperInstance.slideNext).not.toHaveBeenCalled();
    });
  });

  describe("External Controls State Management", () => {
    it("should disable prev button when at beginning", () => {
      mockSwiperInstance.isBeginning = true;
      mockSwiperInstance.isEnd = false;

      carouselInstance.updateExternalControlsState();

      const prevButton = document.getElementById("prev-btn");
      const nextButton = document.getElementById("next-btn");

      expect(prevButton.hasAttribute("disabled")).toBe(true);
      expect(prevButton.getAttribute("aria-disabled")).toBe("true");

      expect(nextButton.hasAttribute("disabled")).toBe(false);
      expect(nextButton.getAttribute("aria-disabled")).toBe("false");
    });

    it("should disable next button when at end", () => {
      mockSwiperInstance.isBeginning = false;
      mockSwiperInstance.isEnd = true;
      mockSwiperInstance.activeIndex = 2; // Set to last slide so activeIndex + slidesPerView >= slidesCount

      carouselInstance.updateExternalControlsState();

      const prevButton = document.getElementById("prev-btn");
      const nextButton = document.getElementById("next-btn");

      expect(nextButton.hasAttribute("disabled")).toBe(true);
      expect(nextButton.getAttribute("aria-disabled")).toBe("true");

      expect(prevButton.hasAttribute("disabled")).toBe(false);
      expect(prevButton.getAttribute("aria-disabled")).toBe("false");
    });

    it("should enable both buttons when in middle", () => {
      mockSwiperInstance.isBeginning = false;
      mockSwiperInstance.isEnd = false;

      carouselInstance.updateExternalControlsState();

      const prevButton = document.getElementById("prev-btn");
      const nextButton = document.getElementById("next-btn");

      expect(prevButton.hasAttribute("disabled")).toBe(false);
      expect(nextButton.hasAttribute("disabled")).toBe(false);
      expect(prevButton.getAttribute("aria-disabled")).toBe("false");
      expect(nextButton.getAttribute("aria-disabled")).toBe("false");
    });

    it("should update controls state on slide change transition end", () => {
      const updateStatesSpy = vi.spyOn(
        carouselInstance,
        "updateExternalControlsState",
      );

      // Get the slideChangeTransitionEnd callback from Swiper initialization
      const swiperOnCallArgs = mockSwiperInstance.on.mock.calls.find(
        (call) => call[0] === "slideChangeTransitionEnd",
      );

      if (swiperOnCallArgs) {
        const callback = swiperOnCallArgs[1];
        callback();
        expect(updateStatesSpy).toHaveBeenCalled();
      }
    });

    it("should keep external controls disabled while scrollbar drag is active", () => {
      mockSwiperInstance.isBeginning = false;
      mockSwiperInstance.isEnd = true;

      carouselInstance.updateExternalControlsState();

      const nextButton = document.getElementById("next-btn");
      expect(nextButton).toHaveAttribute("disabled", "true");

      const swiperConfig = Swiper.mock.calls[0]?.[1];
      const slideChange = swiperConfig?.on?.slideChange;
      const scrollbarDragStart = swiperConfig?.on?.scrollbarDragStart;

      scrollbarDragStart?.();

      expect(nextButton).toHaveAttribute("disabled", "true");
      expect(nextButton).toHaveAttribute("aria-disabled", "true");

      mockSwiperInstance.isBeginning = false;
      mockSwiperInstance.isEnd = false;

      slideChange?.();

      expect(nextButton).toHaveAttribute("disabled", "true");
      expect(nextButton).toHaveAttribute("aria-disabled", "true");
    });

    it("should sync external controls after scrollbar drag end", () => {
      mockSwiperInstance.isBeginning = false;
      mockSwiperInstance.isEnd = true;

      carouselInstance.updateExternalControlsState();

      const nextButton = document.getElementById("next-btn");
      expect(nextButton).toHaveAttribute("disabled", "true");

      const swiperConfig = Swiper.mock.calls[0]?.[1];
      const scrollbarDragStart = swiperConfig?.on?.scrollbarDragStart;
      const scrollbarDragEnd = swiperConfig?.on?.scrollbarDragEnd;

      mockSwiperInstance.isBeginning = false;
      mockSwiperInstance.isEnd = false;

      scrollbarDragStart?.();
      scrollbarDragEnd?.();

      expect(nextButton).not.toHaveAttribute("disabled");
      expect(nextButton).toHaveAttribute("aria-disabled", "false");
    });
  });

  describe("Multiple Control Sets", () => {
    beforeEach(() => {
      document.body.innerHTML = `
        <div class="carousel" data-carousel-id="test-carousel">
          <div class="carousel__viewport">
            <div class="carousel__track">
              <div class="carousel__slide">Slide 1</div>
              <div class="carousel__slide">Slide 2</div>
            </div>
          </div>
        </div>
        <button data-carousel-controls="test-carousel" data-carousel-action="prev" id="prev-btn-1">Prev 1</button>
        <button data-carousel-controls="test-carousel" data-carousel-action="next" id="next-btn-1">Next 1</button>
        <button data-carousel-controls="test-carousel" data-carousel-action="prev" id="prev-btn-2">Prev 2</button>
        <button data-carousel-controls="test-carousel" data-carousel-action="next" id="next-btn-2">Next 2</button>
      `;

      carouselElement = document.querySelector(".carousel");

      // Mock requestAnimationFrame to execute synchronously
      global.requestAnimationFrame = vi.fn((cb) => cb());

      carouselInstance = new Carousel(carouselElement);
    });

    it("should initialize multiple control sets", () => {
      const buttons = document.querySelectorAll(
        '[data-carousel-controls="test-carousel"]',
      );

      buttons.forEach((button) => {
        expect(button.hasAttribute("data-carousel-initialized")).toBe(true);
      });
    });

    it("should update state for all control sets", () => {
      mockSwiperInstance.isBeginning = true;
      mockSwiperInstance.isEnd = false;

      carouselInstance.updateExternalControlsState();

      const prevButtons = document.querySelectorAll(
        '[data-carousel-action="prev"]',
      );
      const nextButtons = document.querySelectorAll(
        '[data-carousel-action="next"]',
      );

      prevButtons.forEach((button) => {
        expect(button.hasAttribute("disabled")).toBe(true);
        expect(button.getAttribute("aria-disabled")).toBe("true");
      });

      nextButtons.forEach((button) => {
        expect(button.hasAttribute("disabled")).toBe(false);
        expect(button.getAttribute("aria-disabled")).toBe("false");
      });
    });
  });

  describe("Cleanup", () => {
    it("should clean up external controls on destroy", () => {
      const prevButton = document.getElementById("prev-btn");
      const nextButton = document.getElementById("next-btn");

      // Verify controls are initialized
      expect(prevButton.hasAttribute("data-carousel-initialized")).toBe(true);
      expect(nextButton.hasAttribute("data-carousel-initialized")).toBe(true);

      // Destroy carousel
      carouselInstance.destroy();

      // Verify cleanup
      expect(prevButton.hasAttribute("data-carousel-initialized")).toBe(false);
      expect(nextButton.hasAttribute("data-carousel-initialized")).toBe(false);
      expect(mockSwiperInstance.destroy).toHaveBeenCalled();
    });

    it("should remove event listeners on destroy", () => {
      const nextButton = document.getElementById("next-btn");

      // Store reference to click handler
      const clickHandler = nextButton._carouselClickHandler;
      expect(clickHandler).toBeDefined();

      carouselInstance.destroy();

      // Handler reference should be removed
      expect(nextButton._carouselClickHandler).toBeUndefined();
    });
  });

  describe("Static Methods", () => {
    it("should get instance by ID", () => {
      const foundInstance = Carousel.getInstanceById("test-carousel");
      expect(foundInstance).toBe(carouselInstance);
    });

    it("should get instance by data-carousel-id", () => {
      document.body.innerHTML = `
        <div class="carousel" data-carousel-id="data-id-carousel">
          <div class="carousel__viewport">
            <div class="carousel__track">
              <div class="carousel__slide">Slide 1</div>
            </div>
          </div>
        </div>
      `;

      const element = document.querySelector(".carousel");
      const instance = new Carousel(element);
      const foundInstance = Carousel.getInstanceById("data-id-carousel");

      expect(foundInstance).toBe(instance);
    });

    it("should return null for non-existent carousel", () => {
      const foundInstance = Carousel.getInstanceById("non-existent");
      expect(foundInstance).toBe(null);
    });
  });

  describe("Edge Cases", () => {
    it("should handle controls without valid action", () => {
      document.body.innerHTML = `
        <div class="carousel" data-carousel-id="test-carousel">
          <div class="carousel__viewport">
            <div class="carousel__track">
              <div class="carousel__slide">Slide 1</div>
            </div>
          </div>
        </div>
        <button data-carousel-controls="test-carousel" data-carousel-action="invalid">Invalid</button>
      `;

      const element = document.querySelector(".carousel");
      new Carousel(element);

      const button = document.querySelector("button");
      button.click();

      // Should not crash and should not call slideNext/slidePrev
      expect(mockSwiperInstance.slideNext).not.toHaveBeenCalled();
      expect(mockSwiperInstance.slidePrev).not.toHaveBeenCalled();
    });

    it("should handle updateExternalControlsState without instance", () => {
      carouselInstance.instance = null;

      // Should not crash
      expect(() => {
        carouselInstance.updateExternalControlsState();
      }).not.toThrow();
    });

    it("should handle updateExternalControlsState without carouselId", () => {
      carouselInstance.carouselId = null;

      // Should not crash
      expect(() => {
        carouselInstance.updateExternalControlsState();
      }).not.toThrow();
    });
  });
});

describe("Carousel Static - Auto-Disable Feature", () => {
  let carouselElement;
  let carouselInstance;
  let mockSwiperInstance;

  beforeEach(() => {
    vi.clearAllMocks();

    // Create mock Swiper instance with slides array
    mockSwiperInstance = {
      slideNext: vi.fn(),
      slidePrev: vi.fn(),
      destroy: vi.fn(),
      update: vi.fn(),
      on: vi.fn(),
      enable: vi.fn(),
      disable: vi.fn(),
      activeIndex: 0,
      isBeginning: true,
      isEnd: false,
      slides: [], // Will be populated in tests
      params: {
        slidesPerView: 1.2,
      },
    };

    Swiper.mockImplementation(function () {
      return mockSwiperInstance;
    });

    document.body.innerHTML = `
      <div class="carousel" data-carousel-id="test-carousel" id="test-carousel">
        <div class="carousel__viewport">
          <div class="carousel__track">
            <div class="carousel__slide">Slide 1</div>
            <div class="carousel__slide">Slide 2</div>
            <div class="carousel__slide">Slide 3</div>
          </div>
        </div>
        <div class="carousel__pagination"></div>
      </div>
    `;

    carouselElement = document.querySelector(".carousel");
    global.requestAnimationFrame = vi.fn((cb) => cb());
  });

  afterEach(() => {
    document.body.innerHTML = "";
    global.requestAnimationFrame = undefined;
  });

  describe("Auto-disable when content fits", () => {
    it("should enable carousel when more slides than slidesPerView", () => {
      // 3 slides, slidesPerView 1.2
      mockSwiperInstance.slides = [
        document.createElement("div"),
        document.createElement("div"),
        document.createElement("div"),
      ];
      mockSwiperInstance.params.slidesPerView = 1.2;

      carouselInstance = new Carousel(carouselElement);

      expect(mockSwiperInstance.enable).toHaveBeenCalled();
    });

    it("should disable carousel when slides match slidesPerView", () => {
      // 2 slides, slidesPerView 2
      mockSwiperInstance.slides = [
        document.createElement("div"),
        document.createElement("div"),
      ];
      mockSwiperInstance.params.slidesPerView = 2;

      carouselInstance = new Carousel(carouselElement);

      expect(mockSwiperInstance.disable).toHaveBeenCalled();
    });

    it("should disable carousel when slides less than slidesPerView", () => {
      // 1 slide, slidesPerView 3
      mockSwiperInstance.slides = [document.createElement("div")];
      mockSwiperInstance.params.slidesPerView = 3;

      carouselInstance = new Carousel(carouselElement);

      expect(mockSwiperInstance.disable).toHaveBeenCalled();
    });

    it("should enable carousel when slides exceed slidesPerView by more than 1", () => {
      // 5 slides, slidesPerView 2
      mockSwiperInstance.slides = [
        document.createElement("div"),
        document.createElement("div"),
        document.createElement("div"),
        document.createElement("div"),
        document.createElement("div"),
      ];
      mockSwiperInstance.params.slidesPerView = 2;

      carouselInstance = new Carousel(carouselElement);

      expect(mockSwiperInstance.enable).toHaveBeenCalled();
    });
  });

  describe("Update state on resize/breakpoint change", () => {
    it("should update carousel enabled state on resize event", () => {
      mockSwiperInstance.slides = [
        document.createElement("div"),
        document.createElement("div"),
      ];
      mockSwiperInstance.params.slidesPerView = 2;

      carouselInstance = new Carousel(carouselElement);

      // Clear previous calls
      mockSwiperInstance.disable.mockClear();
      mockSwiperInstance.enable.mockClear();

      // Simulate resize event with different breakpoint
      mockSwiperInstance.slides = [
        document.createElement("div"),
        document.createElement("div"),
        document.createElement("div"),
        document.createElement("div"),
      ];
      mockSwiperInstance.params.slidesPerView = 3;

      // Get the resize callback
      const resizeCallback = mockSwiperInstance.on.mock.calls.find(
        (call) => call[0] === "resize",
      );
      if (resizeCallback) {
        resizeCallback[1]();
        expect(mockSwiperInstance.enable).toHaveBeenCalled();
      }
    });

    it("should handle resize with decimal slidesPerView", () => {
      mockSwiperInstance.slides = [
        document.createElement("div"),
        document.createElement("div"),
        document.createElement("div"),
        document.createElement("div"),
      ];
      mockSwiperInstance.params.slidesPerView = 2.5;

      carouselInstance = new Carousel(carouselElement);

      // 4 slides > 2.5, should be enabled
      expect(mockSwiperInstance.enable).toHaveBeenCalled();
    });
  });

  describe("Edge cases for auto-disable", () => {
    it("should handle carousel with no slides", () => {
      mockSwiperInstance.slides = [];
      mockSwiperInstance.params.slidesPerView = 1;

      carouselInstance = new Carousel(carouselElement);

      expect(mockSwiperInstance.disable).toHaveBeenCalled();
    });

    it("should handle slidesPerView as string number", () => {
      mockSwiperInstance.slides = [
        document.createElement("div"),
        document.createElement("div"),
      ];
      mockSwiperInstance.params.slidesPerView = "2";

      carouselInstance = new Carousel(carouselElement);

      expect(mockSwiperInstance.disable).toHaveBeenCalled();
    });

    it("should default to slidesPerView 1 if not specified", () => {
      mockSwiperInstance.slides = [
        document.createElement("div"),
        document.createElement("div"),
      ];
      mockSwiperInstance.params.slidesPerView = undefined;

      carouselInstance = new Carousel(carouselElement);

      // 2 slides > 1 (default), should be enabled
      expect(mockSwiperInstance.enable).toHaveBeenCalled();
    });

    it("should not crash if updateCarouselEnabledState called without instance", () => {
      carouselInstance = new Carousel(carouselElement);
      carouselInstance.instance = null;

      expect(() => {
        carouselInstance.updateCarouselEnabledState();
      }).not.toThrow();
    });

    it("should not apply negative viewport margin on mobile when slides fit", () => {
      const originalInnerWidth = window.innerWidth;
      Object.defineProperty(window, "innerWidth", {
        configurable: true,
        writable: true,
        value: 375,
      });

      mockSwiperInstance.slides = [document.createElement("div")];
      mockSwiperInstance.params.slidesPerView = 1;

      carouselInstance = new Carousel(carouselElement);

      const viewport = carouselElement.querySelector(".carousel__viewport");
      expect(viewport.style.getPropertyValue("margin-right")).toBe("");

      Object.defineProperty(window, "innerWidth", {
        configurable: true,
        writable: true,
        value: originalInnerWidth,
      });
    });

    it("should not apply negative viewport margin on desktop when slides fit", () => {
      const originalInnerWidth = window.innerWidth;
      Object.defineProperty(window, "innerWidth", {
        configurable: true,
        writable: true,
        value: 1240,
      });

      mockSwiperInstance.slides = [document.createElement("div")];
      mockSwiperInstance.params.slidesPerView = 1;

      carouselInstance = new Carousel(carouselElement);

      const viewport = carouselElement.querySelector(".carousel__viewport");
      expect(viewport.style.getPropertyValue("margin-right")).toBe("");

      Object.defineProperty(window, "innerWidth", {
        configurable: true,
        writable: true,
        value: originalInnerWidth,
      });
    });

    it("should disable bleed-right when slides fit wrapper despite right inset", () => {
      const originalInnerWidth = window.innerWidth;
      Object.defineProperty(window, "innerWidth", {
        configurable: true,
        writable: true,
        value: 1200,
      });

      mockSwiperInstance.slides = [
        document.createElement("div"),
        document.createElement("div"),
      ];
      mockSwiperInstance.params.slidesPerView = 2;

      document.body.innerHTML = `
        <div class="carousel carousel--bleed-right" data-carousel-id="test-carousel" id="test-carousel">
          <div class="carousel__viewport-wrapper">
            <div class="carousel__viewport">
              <div class="carousel__track">
                <div class="carousel__slide">Slide 1</div>
                <div class="carousel__slide">Slide 2</div>
              </div>
            </div>
          </div>
          <div class="carousel__pagination"></div>
        </div>
      `;

      carouselElement = document.querySelector(".carousel");
      const viewportWrapper = carouselElement.querySelector(
        ".carousel__viewport-wrapper",
      );
      const track = carouselElement.querySelector(".carousel__track");

      Object.defineProperty(viewportWrapper, "clientWidth", {
        configurable: true,
        value: 900,
      });
      Object.defineProperty(track, "scrollWidth", {
        configurable: true,
        value: 900,
      });
      Object.defineProperty(viewportWrapper, "getBoundingClientRect", {
        configurable: true,
        value: () => ({
          top: 0,
          right: 1000,
          bottom: 200,
          left: 100,
          width: 900,
          height: 200,
          x: 100,
          y: 0,
          toJSON: () => ({}),
        }),
      });

      carouselInstance = new Carousel(carouselElement);

      expect(mockSwiperInstance.params.slidesOffsetBefore).toBe(0);
      expect(mockSwiperInstance.params.slidesOffsetAfter).toBe(0);

      Object.defineProperty(window, "innerWidth", {
        configurable: true,
        writable: true,
        value: originalInnerWidth,
      });
    });

    it("should keep wrapper-based bleed geometry without offsets", () => {
      const originalInnerWidth = window.innerWidth;
      Object.defineProperty(window, "innerWidth", {
        configurable: true,
        writable: true,
        value: 1000,
      });

      mockSwiperInstance.slides = [
        document.createElement("div"),
        document.createElement("div"),
        document.createElement("div"),
      ];
      mockSwiperInstance.params.slidesPerView = 1.2;
      mockSwiperInstance.params.spaceBetween = 20;

      document.body.innerHTML = `
        <div class="carousel carousel--bleed-right" data-carousel-id="test-carousel" id="test-carousel">
          <div class="carousel__viewport-wrapper">
            <div class="carousel__viewport">
              <div class="carousel__track">
                <div class="carousel__slide">Slide 1</div>
                <div class="carousel__slide">Slide 2</div>
                <div class="carousel__slide">Slide 3</div>
              </div>
            </div>
          </div>
          <div class="carousel__pagination"></div>
        </div>
      `;

      carouselElement = document.querySelector(".carousel");
      const viewportWrapper = carouselElement.querySelector(
        ".carousel__viewport-wrapper",
      );
      const track = carouselElement.querySelector(".carousel__track");

      Object.defineProperty(viewportWrapper, "clientWidth", {
        configurable: true,
        value: 800,
      });
      Object.defineProperty(track, "scrollWidth", {
        configurable: true,
        value: 1200,
      });

      Object.defineProperty(viewportWrapper, "getBoundingClientRect", {
        configurable: true,
        value: () => ({
          top: 0,
          right: 920,
          bottom: 200,
          left: 120,
          width: 800,
          height: 200,
          x: 120,
          y: 0,
          toJSON: () => ({}),
        }),
      });

      carouselInstance = new Carousel(carouselElement);

      expect(mockSwiperInstance.params.slidesOffsetBefore).toBe(0);
      expect(mockSwiperInstance.params.slidesOffsetAfter).toBe(0);
      expect(mockSwiperInstance.params.width).toBe(800);
      expect(
        carouselElement.style.getPropertyValue(
          "--carousel-bleed-viewport-width",
        ),
      ).toBe("880px");
      expect(
        carouselElement.style.getPropertyValue("--carousel-bleed-margin-left"),
      ).toBe("120px");

      Object.defineProperty(window, "innerWidth", {
        configurable: true,
        writable: true,
        value: originalInnerWidth,
      });
    });

    it("should calculate bleed-right scrollbar drag from logical viewport, not shell width", () => {
      mockSwiperInstance.slides = [
        document.createElement("div"),
        document.createElement("div"),
        document.createElement("div"),
      ];
      mockSwiperInstance.slides.forEach((slide) => {
        Object.defineProperty(slide, "offsetWidth", {
          configurable: true,
          value: 300,
        });
      });

      mockSwiperInstance.params.slidesPerView = 1.2;
      mockSwiperInstance.params.spaceBetween = 0;
      mockSwiperInstance.width = 800;
      mockSwiperInstance.progress = 0.5;
      mockSwiperInstance.minTranslate = vi.fn(() => -300);
      mockSwiperInstance.maxTranslate = vi.fn(() => 0);

      const scrollbarEl = document.createElement("div");
      const dragEl = document.createElement("div");
      Object.defineProperty(scrollbarEl, "offsetWidth", {
        configurable: true,
        value: 200,
      });

      mockSwiperInstance.scrollbar = {
        updateSize: vi.fn(),
        el: scrollbarEl,
        dragEl,
      };

      document.body.innerHTML = `
        <div class="carousel carousel--bleed-right" data-carousel-id="test-carousel" id="test-carousel">
          <div class="carousel__viewport-wrapper">
            <div class="carousel__viewport">
              <div class="carousel__track">
                <div class="carousel__slide">Slide 1</div>
                <div class="carousel__slide">Slide 2</div>
                <div class="carousel__slide">Slide 3</div>
              </div>
            </div>
          </div>
          <div class="carousel__pagination"></div>
        </div>
      `;

      carouselElement = document.querySelector(".carousel");
      const viewportWrapper = carouselElement.querySelector(
        ".carousel__viewport-wrapper",
      );
      const track = carouselElement.querySelector(".carousel__track");

      Object.defineProperty(carouselElement, "clientWidth", {
        configurable: true,
        value: 800,
      });
      Object.defineProperty(viewportWrapper, "clientWidth", {
        configurable: true,
        value: 1000,
      });
      Object.defineProperty(track, "scrollWidth", {
        configurable: true,
        value: 900,
      });
      Object.defineProperty(viewportWrapper, "getBoundingClientRect", {
        configurable: true,
        value: () => ({
          top: 0,
          right: 1120,
          bottom: 200,
          left: 120,
          width: 1000,
          height: 200,
          x: 120,
          y: 0,
          toJSON: () => ({}),
        }),
      });

      carouselInstance = new Carousel(carouselElement);

      // Regression: drag must not become full width just because shell wrapper is wider than logical viewport.
      expect(dragEl.style.width).not.toBe("200px");
    });
  });
});
