// @vitest-environment jsdom
import { render, act } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import GradientBlinds from './GradientBlinds';

// Mock ogl
vi.mock('ogl', () => {
  return {
    Renderer: class {
      gl = {
        // Use a real canvas element so appendChild works
        canvas: document.createElement('canvas'),
        drawingBufferWidth: 100,
        drawingBufferHeight: 100,
      };
      setSize() {}
      render() {}
    },
    Program: class {
        remove() {}
    },
    Mesh: class {
        remove() {}
    },
    Triangle: class {
        remove() {}
    },
  };
});

// Mock ResizeObserver
globalThis.ResizeObserver = class ResizeObserver {
  observe() {}
  unobserve() {}
  disconnect() {}
};

describe('GradientBlinds', () => {
  let rafSpy: any;
  let cancelRafSpy: any;
  let ioCallback: (entries: IntersectionObserverEntry[]) => void;
  let observeSpy: any;
  let disconnectSpy: any;

  beforeEach(() => {
    rafSpy = vi.spyOn(window, 'requestAnimationFrame').mockImplementation((_cb: any) => {
      return 123;
    });
    cancelRafSpy = vi.spyOn(window, 'cancelAnimationFrame').mockImplementation((_id: number) => {
    });

    // Mock IntersectionObserver
    observeSpy = vi.fn();
    disconnectSpy = vi.fn();
    (globalThis as any).IntersectionObserver = class IntersectionObserver {
      constructor(cb: any) {
        ioCallback = cb;
      }
      observe = observeSpy;
      unobserve = vi.fn();
      disconnect = disconnectSpy;
    };
  });

  afterEach(() => {
    vi.clearAllMocks();
  });

  it('starts animation loop on mount', () => {
    const { unmount } = render(<GradientBlinds />);
    expect(rafSpy).toHaveBeenCalled();
    unmount();
    expect(cancelRafSpy).toHaveBeenCalled();
  });

  it('pauses animation loop when off-screen and resumes when on-screen', () => {
    const { unmount } = render(<GradientBlinds />);

    // Initial start
    expect(rafSpy).toHaveBeenCalledTimes(1);

    // Reset spies to check for subsequent calls
    rafSpy.mockClear();
    cancelRafSpy.mockClear();

    // Simulate off-screen
    act(() => {
      if (ioCallback) {
        ioCallback([{ isIntersecting: false } as IntersectionObserverEntry]);
      }
    });

    // Should cancel animation frame
    expect(cancelRafSpy).toHaveBeenCalled();

    // Should NOT request new frame (loop stopped)
    expect(rafSpy).not.toHaveBeenCalled();

    // Reset spies
    cancelRafSpy.mockClear();

    // Simulate on-screen
    act(() => {
      if (ioCallback) {
        ioCallback([{ isIntersecting: true } as IntersectionObserverEntry]);
      }
    });

    // Should restart animation loop
    expect(rafSpy).toHaveBeenCalled();

    unmount();
    expect(disconnectSpy).toHaveBeenCalled();
  });
});
