import { renderHook, act } from '@testing-library/react';
import { useToolbarOverflow } from '../useToolbarOverflow';

// Provide a real rAF that runs synchronously in tests
let rafCallback: FrameRequestCallback | null = null;
const originalRAF = window.requestAnimationFrame;
const originalCAF = window.cancelAnimationFrame;

beforeEach(() => {
  window.requestAnimationFrame = (cb: FrameRequestCallback) => {
    rafCallback = cb;
    return 1;
  };
  window.cancelAnimationFrame = jest.fn();
});

afterEach(() => {
  window.requestAnimationFrame = originalRAF;
  window.cancelAnimationFrame = originalCAF;
  rafCallback = null;
});

function flushRAF() {
  if (rafCallback) {
    rafCallback(performance.now());
    rafCallback = null;
  }
}

/**
 * Helper: create an HTMLDivElement with mock children that have
 * `data-toolbar-id` and `data-toolbar-priority` attributes plus
 * a mocked `getBoundingClientRect`.
 */
function createMockContainer(
  clientWidth: number,
  children: { id: string; priority: number; width: number }[],
): HTMLDivElement {
  const container = document.createElement('div');
  Object.defineProperty(container, 'clientWidth', { value: clientWidth, writable: true });

  for (const child of children) {
    const el = document.createElement('button');
    el.setAttribute('data-toolbar-id', child.id);
    el.setAttribute('data-toolbar-priority', String(child.priority));
    el.getBoundingClientRect = () =>
      ({ width: child.width, height: 32, top: 0, left: 0, right: child.width, bottom: 32, x: 0, y: 0, toJSON: () => ({}) }) as DOMRect;
    container.appendChild(el);
  }

  return container;
}

describe('useToolbarOverflow', () => {
  it('returns empty overflow when everything fits', () => {
    const container = createMockContainer(500, [
      { id: 'a', priority: 1, width: 40 },
      { id: 'b', priority: 2, width: 40 },
      { id: 'c', priority: 3, width: 40 },
    ]);

    const ref = { current: container };
    const { result } = renderHook(() => useToolbarOverflow(ref));

    act(() => { flushRAF(); });

    expect(result.current.showOverflowMenu).toBe(false);
    expect(result.current.overflowedIds.size).toBe(0);
  });

  it('overflows lowest-priority items first when container is too narrow', () => {
    // 3 items × 100px + 2 gaps × 8px = 316px total, but container is only 200px
    const container = createMockContainer(200, [
      { id: 'a', priority: 1, width: 100 },
      { id: 'b', priority: 2, width: 100 },
      { id: 'c', priority: 3, width: 100 },
    ]);

    const ref = { current: container };
    const { result } = renderHook(() => useToolbarOverflow(ref));

    act(() => { flushRAF(); });

    expect(result.current.showOverflowMenu).toBe(true);
    // Priority 1 should overflow first
    expect(result.current.overflowedIds.has('a')).toBe(true);
  });

  it('overflows multiple items when container is very narrow', () => {
    // Total content: 3×100 + 2×8 = 316px, container only 100px
    const container = createMockContainer(100, [
      { id: 'a', priority: 1, width: 100 },
      { id: 'b', priority: 2, width: 100 },
      { id: 'c', priority: 3, width: 100 },
    ]);

    const ref = { current: container };
    const { result } = renderHook(() => useToolbarOverflow(ref));

    act(() => { flushRAF(); });

    expect(result.current.showOverflowMenu).toBe(true);
    expect(result.current.overflowedIds.has('a')).toBe(true);
    expect(result.current.overflowedIds.has('b')).toBe(true);
  });

  it('ignores children without data-toolbar-id', () => {
    const container = createMockContainer(500, [
      { id: 'a', priority: 1, width: 40 },
    ]);
    // Add a child without data-toolbar-id
    const plain = document.createElement('div');
    plain.getBoundingClientRect = () =>
      ({ width: 1000 } as DOMRect);
    container.appendChild(plain);

    const ref = { current: container };
    const { result } = renderHook(() => useToolbarOverflow(ref));

    act(() => { flushRAF(); });

    // The large child without an id should be ignored
    expect(result.current.showOverflowMenu).toBe(false);
  });

  it('defaults priority to 999 when attribute is missing', () => {
    const container = createMockContainer(150, []);

    // Add two items manually: one with priority, one without
    const el1 = document.createElement('button');
    el1.setAttribute('data-toolbar-id', 'low');
    el1.setAttribute('data-toolbar-priority', '1');
    el1.getBoundingClientRect = () => ({ width: 80 } as DOMRect);
    container.appendChild(el1);

    const el2 = document.createElement('button');
    el2.setAttribute('data-toolbar-id', 'high');
    // No priority attribute → defaults to 999
    el2.getBoundingClientRect = () => ({ width: 80 } as DOMRect);
    container.appendChild(el2);

    const ref = { current: container };
    const { result } = renderHook(() => useToolbarOverflow(ref));

    act(() => { flushRAF(); });

    // The low-priority item (priority 1) should be overflowed first;
    // the item without priority (999) should stay visible
    expect(result.current.overflowedIds.has('low')).toBe(true);
    expect(result.current.overflowedIds.has('high')).toBe(false);
  });

  it('returns no overflow when ref is null', () => {
    const ref = { current: null };
    const { result } = renderHook(() => useToolbarOverflow(ref));

    expect(result.current.showOverflowMenu).toBe(false);
    expect(result.current.overflowedIds.size).toBe(0);
  });

  it('returns compactMode false when no toolbarRef is provided', () => {
    const container = createMockContainer(500, [
      { id: 'a', priority: 1, width: 40 },
    ]);
    const ref = { current: container };
    const { result } = renderHook(() => useToolbarOverflow(ref));

    act(() => { flushRAF(); });

    expect(result.current.compactMode).toBe(false);
  });

  it('returns compactMode false when toolbar is wide enough', () => {
    const container = createMockContainer(400, [
      { id: 'a', priority: 1, width: 40 },
      { id: 'b', priority: 2, width: 40 },
    ]);
    // Toolbar is 800px — above the 660px threshold
    const toolbar = document.createElement('div');
    Object.defineProperty(toolbar, 'clientWidth', { value: 800 });

    const containerRef = { current: container };
    const toolbarRef = { current: toolbar };
    const { result } = renderHook(() => useToolbarOverflow(containerRef, toolbarRef));

    act(() => { flushRAF(); });

    expect(result.current.compactMode).toBe(false);
  });

  it('activates compactMode when toolbar width is below threshold', () => {
    const container = createMockContainer(200, [
      { id: 'a', priority: 1, width: 40 },
      { id: 'b', priority: 2, width: 40 },
      { id: 'c', priority: 3, width: 40 },
    ]);
    // Toolbar is 500px — below the 660px threshold
    const toolbar = document.createElement('div');
    Object.defineProperty(toolbar, 'clientWidth', { value: 500 });

    const containerRef = { current: container };
    const toolbarRef = { current: toolbar };
    const { result } = renderHook(() => useToolbarOverflow(containerRef, toolbarRef));

    act(() => { flushRAF(); });

    expect(result.current.compactMode).toBe(true);
    expect(result.current.showOverflowMenu).toBe(true);
    // All items should be overflowed in compact mode
    expect(result.current.overflowedIds.has('a')).toBe(true);
    expect(result.current.overflowedIds.has('b')).toBe(true);
    expect(result.current.overflowedIds.has('c')).toBe(true);
  });

  it('forces all items into overflow in compact mode regardless of priority', () => {
    // Even high-priority items (priority 3) get overflowed
    const container = createMockContainer(300, [
      { id: 'x', priority: 3, width: 30 },
      { id: 'y', priority: 3, width: 30 },
    ]);
    const toolbar = document.createElement('div');
    Object.defineProperty(toolbar, 'clientWidth', { value: 600 });

    const containerRef = { current: container };
    const toolbarRef = { current: toolbar };
    const { result } = renderHook(() => useToolbarOverflow(containerRef, toolbarRef));

    act(() => { flushRAF(); });

    expect(result.current.compactMode).toBe(true);
    expect(result.current.overflowedIds.size).toBe(2);
    expect(result.current.overflowedIds.has('x')).toBe(true);
    expect(result.current.overflowedIds.has('y')).toBe(true);
  });
});
