import { describe, expect, it } from "vitest";

import {
  act,
  defineToolcraft,
  fireEvent,
  renderControlsPanelWithSchema,
  screen,
  vi,
} from "../testing/controls-panel-test-utils";

function createOverflowSchema() {
  return defineToolcraft({
    canvas: { enabled: false },
    panels: {
      controls: {
        sections: [
          {
            controls: {
              density: {
                defaultValue: 50,
                label: "Density",
                max: 100,
                min: 0,
                target: "field.density",
                type: "slider",
              },
            },
            title: "Field",
          },
          {
            controls: {
              enabled: {
                defaultValue: true,
                label: "Enabled",
                target: "wind.enabled",
                type: "switch",
              },
            },
            title: "Wind",
          },
          {
            actionGroup: "secondary",
            controls: {
              export: {
                actions: [
                  {
                    command: "controls.apply",
                    label: "Export",
                    value: "export",
                    variant: "default",
                  },
                ],
                target: "panel.actions",
                type: "panelActions",
              },
            },
            title: "Export",
          },
        ],
        title: "Grass Studio",
      },
    },
  });
}

function createRect({
  height,
  left = 0,
  top,
  width = 300,
}: {
  height: number;
  left?: number;
  top: number;
  width?: number;
}): DOMRect {
  return {
    bottom: top + height,
    height,
    left,
    right: left + width,
    toJSON: () => ({}),
    top,
    width,
    x: left,
    y: top,
  };
}

function prepareNavigationPanel(container: HTMLElement) {
  const root = container.querySelector<HTMLElement>(
    "[data-toolcraft-controls-panel-shell]",
  );
  const viewport = container.querySelector<HTMLElement>(
    '[data-slot="toolcraft-panel-content"]',
  );
  const panel = container.querySelector<HTMLElement>(
    '[data-panel-id="properties"]',
  );
  const sectionAnchors = Array.from(
    container.querySelectorAll<HTMLElement>(
      "[data-toolcraft-controls-section-anchor]",
    ),
  );

  expect(root).toBeTruthy();
  expect(panel).toBeTruthy();
  expect(viewport).toBeTruthy();
  expect(sectionAnchors).toHaveLength(4);

  vi.spyOn(root!, "getBoundingClientRect").mockReturnValue(
    createRect({ height: 360, left: 400, top: 10 }),
  );
  vi.spyOn(panel!, "getBoundingClientRect").mockReturnValue(
    createRect({ height: 360, left: 400, top: 10 }),
  );
  vi.spyOn(viewport!, "getBoundingClientRect").mockReturnValue(
    createRect({ height: 300, left: 400, top: 60 }),
  );
  sectionAnchors.forEach((section, index) => {
    vi.spyOn(section, "getBoundingClientRect").mockReturnValue(
      createRect({ height: 180, left: 400, top: 60 + index * 180 }),
    );
  });

  return { panel: panel!, viewport: viewport! };
}

function setViewportSize(
  viewport: HTMLElement,
  {
    clientHeight,
    scrollHeight,
  }: { clientHeight: number; scrollHeight: number },
): void {
  Object.defineProperties(viewport, {
    clientHeight: { configurable: true, value: clientHeight },
    scrollHeight: { configurable: true, value: scrollHeight },
    scrollTop: { configurable: true, value: 0, writable: true },
  });
}

function refreshNavigationLayout(): void {
  act(() => {
    window.dispatchEvent(new Event("resize"));
    vi.advanceTimersByTime(20);
  });
}

describe("ControlsPanel section navigation overflow eligibility", () => {
  it("never mounts navigation when every section fits the content viewport", () => {
    vi.useFakeTimers();

    try {
      const { container } = renderControlsPanelWithSchema(createOverflowSchema());
      const { panel, viewport } = prepareNavigationPanel(container);

      setViewportSize(viewport, { clientHeight: 500, scrollHeight: 500 });
      refreshNavigationLayout();
      fireEvent.pointerMove(panel, { clientX: 408, clientY: 120 });
      act(() => vi.advanceTimersByTime(600));

      expect(
        screen.queryByRole("navigation", { name: "Panel sections" }),
      ).toBeNull();
    } finally {
      vi.useRealTimers();
    }
  });

  it("requires a fresh dwell after overflow disappears during reveal intent", () => {
    vi.useFakeTimers();

    try {
      const { container } = renderControlsPanelWithSchema(createOverflowSchema());
      const { panel, viewport } = prepareNavigationPanel(container);

      setViewportSize(viewport, { clientHeight: 300, scrollHeight: 900 });
      refreshNavigationLayout();
      fireEvent.pointerMove(panel, { clientX: 408, clientY: 120 });
      act(() => vi.advanceTimersByTime(150));

      setViewportSize(viewport, { clientHeight: 900, scrollHeight: 900 });
      refreshNavigationLayout();
      setViewportSize(viewport, { clientHeight: 300, scrollHeight: 900 });
      refreshNavigationLayout();
      act(() => vi.advanceTimersByTime(500));

      expect(
        screen.queryByRole("navigation", { name: "Panel sections" }),
      ).toBeNull();

      fireEvent.pointerMove(panel, { clientX: 413, clientY: 120 });
      fireEvent.pointerMove(panel, { clientX: 408, clientY: 120 });
      act(() => vi.advanceTimersByTime(299));
      expect(
        screen.queryByRole("navigation", { name: "Panel sections" }),
      ).toBeNull();
      act(() => vi.advanceTimersByTime(1));
      expect(
        screen.getByRole("navigation", { name: "Panel sections" }),
      ).toBeTruthy();
    } finally {
      vi.useRealTimers();
    }
  });

  it("unmounts an open popup when overflow disappears", () => {
    vi.useFakeTimers();

    try {
      const { container } = renderControlsPanelWithSchema(createOverflowSchema());
      const { panel, viewport } = prepareNavigationPanel(container);

      setViewportSize(viewport, { clientHeight: 300, scrollHeight: 900 });
      refreshNavigationLayout();
      fireEvent.pointerMove(panel, { clientX: 408, clientY: 120 });
      act(() => vi.advanceTimersByTime(300));
      expect(
        screen.getByRole("navigation", { name: "Panel sections" }),
      ).toBeTruthy();

      setViewportSize(viewport, { clientHeight: 900, scrollHeight: 900 });
      refreshNavigationLayout();
      expect(
        screen.queryByRole("navigation", { name: "Panel sections" }),
      ).toBeNull();

      setViewportSize(viewport, { clientHeight: 300, scrollHeight: 900 });
      refreshNavigationLayout();
      act(() => vi.advanceTimersByTime(500));
      expect(
        screen.queryByRole("navigation", { name: "Panel sections" }),
      ).toBeNull();

      fireEvent.pointerMove(panel, { clientX: 413, clientY: 120 });
      fireEvent.pointerMove(panel, { clientX: 408, clientY: 120 });
      act(() => vi.advanceTimersByTime(300));
      expect(
        screen.getByRole("navigation", { name: "Panel sections" }),
      ).toBeTruthy();
    } finally {
      vi.useRealTimers();
    }
  });
});
