import React, { act } from 'react';
import { render, screen } from '@testing-library/react';

import { mockResizeObserver } from '../../test-utils/window-mock';

import { useContainerSize } from './useContainerSize';

mockResizeObserver();

describe('useContainerSize', () => {
  let resizeObserverCallback: ResizeObserverCallback | null = null;
  const OriginalResizeObserver = global.ResizeObserver;

  beforeEach(() => {
    resizeObserverCallback = null;

    // Capture the ResizeObserver callback for testing
    global.ResizeObserver = jest.fn().mockImplementation((callback: ResizeObserverCallback) => {
      resizeObserverCallback = callback;
      const instance = new OriginalResizeObserver(callback);
      return instance;
    });
  });

  afterEach(() => {
    global.ResizeObserver = OriginalResizeObserver;
  });

  // Helper to create a test component
  function TestComponent({
    breakpoint,
    onStateChange,
  }: {
    breakpoint: number;
    onStateChange: (state: boolean) => void;
  }) {
    const [ref, isAboveBreakpoint] = useContainerSize(breakpoint);

    // Call the callback whenever state changes
    React.useEffect(() => {
      onStateChange(isAboveBreakpoint);
    }, [isAboveBreakpoint, onStateChange]);

    return (
      <div ref={ref} data-testid="container">
        Test
      </div>
    );
  }

  // Helper to simulate container resize
  function simulateResize(width: number, options?: { noBorderBoxSize?: boolean }) {
    const container = screen.getByTestId('container');

    act(() => {
      if (resizeObserverCallback) {
        const entry = {
          target: container,
          borderBoxSize: options?.noBorderBoxSize
            ? undefined
            : [{ inlineSize: width, blockSize: 100 }],
          contentRect: {
            width,
            height: 100,
            x: 0,
            y: 0,
            top: 0,
            right: width,
            bottom: 100,
            left: 0,
            toJSON: () => ({}),
          },
          contentBoxSize: [{ inlineSize: width, blockSize: 100 }],
          devicePixelContentBoxSize: [{ inlineSize: width, blockSize: 100 }],
        } as ResizeObserverEntry;

        resizeObserverCallback([entry], {} as ResizeObserver);
      }
    });
  }

  it('returns initial state as false', () => {
    const onStateChange = jest.fn();
    render(<TestComponent breakpoint={480} onStateChange={onStateChange} />);

    // Initial call with false
    expect(onStateChange).toHaveBeenCalledWith(false);
  });

  it('updates to true when container width is above breakpoint', () => {
    const onStateChange = jest.fn();
    render(<TestComponent breakpoint={480} onStateChange={onStateChange} />);

    simulateResize(600);
    expect(onStateChange).toHaveBeenCalledWith(true);
  });

  it('remains false when container width is below breakpoint', () => {
    const onStateChange = jest.fn();
    render(<TestComponent breakpoint={480} onStateChange={onStateChange} />);

    simulateResize(400);
    expect(onStateChange).toHaveBeenCalledWith(false);
    expect(onStateChange).not.toHaveBeenCalledWith(true);
  });

  it('returns true when container width equals breakpoint', () => {
    const onStateChange = jest.fn();
    render(<TestComponent breakpoint={480} onStateChange={onStateChange} />);

    simulateResize(480);
    expect(onStateChange).toHaveBeenCalledWith(true);
  });

  it('updates state when container crosses breakpoint from below to above', () => {
    const onStateChange = jest.fn();
    render(<TestComponent breakpoint={480} onStateChange={onStateChange} />);

    simulateResize(400);
    expect(onStateChange).toHaveBeenCalledWith(false);

    simulateResize(600);
    expect(onStateChange).toHaveBeenCalledWith(true);
  });
});
