import { renderHook, act } from '@testing-library/react';
import React, { RefObject, ReactNode, createElement } from 'react';
import { useProductCarousel } from '../useProductCarousel';
import { StoreContext } from '../../services/store';
import { IProduct } from '../../types';

// Mock the productCarouselGap constant
jest.mock('../../components/consts', () => ({
  productCarouselGap: 15,
}));

describe('useProductCarousel', () => {
  let mockScrollContainerRef: RefObject<HTMLDivElement>;
  let mockContainerRef: RefObject<HTMLDivElement>;
  let mockProducts: IProduct[];
  let mockStoreContext: any;

  beforeEach(() => {
    // Mock DOM elements
    const mockScrollContainer = {
      clientWidth: 400,
    } as HTMLDivElement;

    const mockContainer = {
      clientWidth: 400,
    } as HTMLDivElement;

    mockScrollContainerRef = {
      current: mockScrollContainer,
    };

    mockContainerRef = {
      current: mockContainer,
    };

    // Mock products
    mockProducts = [
      {
        id: '1',
        catalog_id: 'cat1',
        company_id: 'comp1',
        description: 'Product 1',
        image_link: 'image1.jpg',
        link: 'link1',
        title: 'Product 1',
        updated_at: '2024-01-01',
      },
      {
        id: '2',
        catalog_id: 'cat2',
        company_id: 'comp2',
        description: 'Product 2',
        image_link: 'image2.jpg',
        link: 'link2',
        title: 'Product 2',
        updated_at: '2024-01-02',
      },
      {
        id: '3',
        catalog_id: 'cat3',
        company_id: 'comp3',
        description: 'Product 3',
        image_link: 'image3.jpg',
        link: 'link3',
        title: 'Product 3',
        updated_at: '2024-01-03',
      },
      {
        id: '4',
        catalog_id: 'cat4',
        company_id: 'comp4',
        description: 'Product 4',
        image_link: 'image4.jpg',
        link: 'link4',
        title: 'Product 4',
        updated_at: '2024-01-04',
      },
      {
        id: '5',
        catalog_id: 'cat5',
        company_id: 'comp5',
        description: 'Product 5',
        image_link: 'image5.jpg',
        link: 'link5',
        title: 'Product 5',
        updated_at: '2024-01-05',
      },
    ];

    // Mock store context
    mockStoreContext = {
      getProductImageWidth: jest.fn((id: string) => {
        // Return larger widths so total content is wider than container
        const widths: { [key: string]: number } = {
          '1': 200,
          '2': 250,
          '3': 180,
          '4': 220,
          '5': 190,
        };
        return widths[id] || 0;
      }),
    };

    jest.clearAllMocks();
    jest.useFakeTimers();
  });

  afterEach(() => {
    jest.useRealTimers();
  });

  const renderHookWithContext = (props: any) => {
    const wrapper = ({ children }: { children: ReactNode }) =>
      createElement(StoreContext.Provider, { value: mockStoreContext }, children);

    return renderHook(
      () =>
        useProductCarousel({
          products: props.products || mockProducts,
          containerRef: mockContainerRef,
          scrollContainerRef: mockScrollContainerRef,
        }),
      { wrapper },
    );
  };

  describe('Initialization', () => {
    it('initializes with correct default values', () => {
      const { result } = renderHookWithContext({});

      expect(result.current.scrollPosition).toBe(0);
      expect(result.current.canScrollPrev).toBe(false);
      expect(result.current.canScrollNext).toBe(true);

      // enableTransition starts as false due to products useEffect, then becomes true
      expect(result.current.enableTransition).toBe(false);

      // After timeout, transition should be enabled
      act(() => {
        jest.advanceTimersByTime(60);
      });

      expect(result.current.enableTransition).toBe(true);
    });

    it('initializes with disabled transition and enables it after timeout', () => {
      const { result } = renderHookWithContext({});

      // Initially enableTransition should be false due to products useEffect
      expect(result.current.enableTransition).toBe(false);

      // After timeout, transition should be enabled
      act(() => {
        jest.advanceTimersByTime(60);
      });

      expect(result.current.enableTransition).toBe(true);
    });
  });

  describe('getItemWidth', () => {
    it('returns actual image width when available', () => {
      const { result } = renderHookWithContext({});

      expect(result.current.getItemWidth(0)).toBe(200); // Product 1
      expect(result.current.getItemWidth(1)).toBe(250); // Product 2
      expect(result.current.getItemWidth(2)).toBe(180); // Product 3
      expect(result.current.getItemWidth(3)).toBe(220); // Product 4
      expect(result.current.getItemWidth(4)).toBe(190); // Product 5
    });

    it('returns fallback width when image width is not available', () => {
      mockStoreContext.getProductImageWidth.mockReturnValue(0);
      const { result } = renderHookWithContext({});

      const fallbackWidth = Math.min(400 * 0.4, 200); // 160px
      expect(result.current.getItemWidth(0)).toBe(fallbackWidth);
    });

    it('handles invalid product index gracefully', () => {
      const { result } = renderHookWithContext({});

      const fallbackWidth = Math.min(400 * 0.4, 200); // 160px
      expect(result.current.getItemWidth(999)).toBe(fallbackWidth);
    });
  });

  describe('getScrollPosition', () => {
    it('returns current scroll position', () => {
      const { result } = renderHookWithContext({});

      expect(result.current.getScrollPosition()).toBe(0);
    });
  });

  describe('Scroll button states', () => {
    it('disables prev button at the beginning', () => {
      const { result } = renderHookWithContext({});

      expect(result.current.canScrollPrev).toBe(false);
      expect(result.current.canScrollNext).toBe(true);
    });

    it('enables both buttons when in the middle', () => {
      const { result } = renderHookWithContext({});

      // Scroll to middle position
      act(() => {
        result.current.scrollNext();
      });

      expect(result.current.canScrollPrev).toBe(true);
      expect(result.current.canScrollNext).toBe(true);
    });

    it('handles single product correctly', () => {
      const singleProduct = [mockProducts[0]];
      const { result } = renderHookWithContext({ products: singleProduct });

      expect(result.current.canScrollPrev).toBe(false);
      expect(result.current.canScrollNext).toBe(false);
    });
  });

  describe('scrollNext', () => {
    it('scrolls to next view based on partially visible items', () => {
      const { result } = renderHookWithContext({});

      const initialPosition = result.current.getScrollPosition();
      expect(initialPosition).toBe(0);

      act(() => {
        result.current.scrollNext();
      });

      const newPosition = result.current.getScrollPosition();
      expect(newPosition).toBeGreaterThan(initialPosition);
      expect(result.current.canScrollPrev).toBe(true);
    });
    it('does not scroll beyond maximum position', () => {
      const { result } = renderHookWithContext({});

      // Scroll multiple times to reach the end
      let previousPosition = -1;
      let currentPosition = 0;
      let scrollAttempts = 0;

      // Keep scrolling until position stops changing or we hit a reasonable limit
      while (previousPosition !== currentPosition && scrollAttempts < 10) {
        previousPosition = result.current.getScrollPosition();

        act(() => {
          result.current.scrollNext();
        });

        currentPosition = result.current.getScrollPosition();
        scrollAttempts++;
      }

      const finalPosition = result.current.getScrollPosition();
      const canScrollNext = result.current.canScrollNext;

      // Try to scroll one more time - neither position nor state should change
      act(() => {
        result.current.scrollNext();
      });

      expect(result.current.getScrollPosition()).toBe(finalPosition);
      expect(result.current.canScrollNext).toBe(canScrollNext);
    });
  });

  describe('scrollPrev', () => {
    it('scrolls back by view width', () => {
      const { result } = renderHookWithContext({});

      // First scroll forward
      act(() => {
        result.current.scrollNext();
      });

      const middlePosition = result.current.getScrollPosition();
      expect(middlePosition).toBeGreaterThan(0);

      // Then scroll back
      act(() => {
        result.current.scrollPrev();
      });

      const backPosition = result.current.getScrollPosition();
      expect(backPosition).toBeLessThan(middlePosition);
    });

    it('does not scroll before position 0', () => {
      const { result } = renderHookWithContext({});

      expect(result.current.getScrollPosition()).toBe(0);

      act(() => {
        result.current.scrollPrev();
      });

      expect(result.current.getScrollPosition()).toBe(0);
      expect(result.current.canScrollPrev).toBe(false);
    });
  });

  describe('Products change', () => {
    it('resets scroll position when products change', () => {
      const { result, rerender } = renderHookWithContext({});

      // Scroll to some position
      act(() => {
        result.current.scrollNext();
      });

      expect(result.current.getScrollPosition()).toBeGreaterThan(0);

      // Change products
      const newProducts = [
        {
          ...mockProducts[0],
          id: '4',
          title: 'New Product',
        },
      ];

      const wrapper = ({ children }: { children: ReactNode }) =>
        createElement(StoreContext.Provider, { value: mockStoreContext }, children);

      const { result: newResult } = renderHook(
        () =>
          useProductCarousel({
            products: newProducts,
            containerRef: mockContainerRef,
            scrollContainerRef: mockScrollContainerRef,
          }),
        { wrapper },
      );

      expect(newResult.current.getScrollPosition()).toBe(0);
    });

    it('temporarily disables transition when products change', () => {
      const { result } = renderHookWithContext({});

      // Fast forward to get past initial timeout
      act(() => {
        jest.advanceTimersByTime(60);
      });

      expect(result.current.enableTransition).toBe(true);

      // Change products
      const newProducts = [mockProducts[0]];

      const wrapper = ({ children }: { children: ReactNode }) =>
        createElement(StoreContext.Provider, { value: mockStoreContext }, children);

      const { result: newResult } = renderHook(
        () =>
          useProductCarousel({
            products: newProducts,
            containerRef: mockContainerRef,
            scrollContainerRef: mockScrollContainerRef,
          }),
        { wrapper },
      );

      expect(newResult.current.enableTransition).toBe(false);

      // Fast forward the timer
      act(() => {
        jest.advanceTimersByTime(60);
      });

      expect(newResult.current.enableTransition).toBe(true);
    });
  });

  describe('Edge cases', () => {
    it('handles empty products array', () => {
      const { result } = renderHookWithContext({ products: [] });

      expect(result.current.scrollPosition).toBe(0);
      expect(result.current.canScrollPrev).toBe(false);
      expect(result.current.canScrollNext).toBe(false);
      expect(result.current.getItemWidth(0)).toBeGreaterThan(0); // Should return fallback width
    });

    it('handles null scroll container ref', () => {
      const nullScrollContainerRef = { current: null };

      const wrapper = ({ children }: { children: ReactNode }) =>
        createElement(StoreContext.Provider, { value: mockStoreContext }, children);

      const { result } = renderHook(
        () =>
          useProductCarousel({
            products: mockProducts,
            containerRef: mockContainerRef,
            scrollContainerRef: nullScrollContainerRef,
          }),
        { wrapper },
      );

      expect(result.current.getItemWidth(0)).toBe(200); // Should still use image width from context
    });

    it('handles container with zero width', () => {
      const zeroWidthScrollContainer = {
        clientWidth: 0,
      } as HTMLDivElement;

      const zeroWidthScrollContainerRef = {
        current: zeroWidthScrollContainer,
      };

      const wrapper = ({ children }: { children: ReactNode }) =>
        createElement(StoreContext.Provider, { value: mockStoreContext }, children);

      const { result } = renderHook(
        () =>
          useProductCarousel({
            products: mockProducts,
            containerRef: mockContainerRef,
            scrollContainerRef: zeroWidthScrollContainerRef,
          }),
        { wrapper },
      );

      expect(result.current.canScrollNext).toBe(true);
    });
  });
});
