import { describe, it, expect, jest } from '@jest/globals';
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';

jest.mock('framer-motion', () => ({
  motion: {
    div: ({ children, initial, animate, variants, className, whileHover, whileTap, ...props }: any) => (
      <div
        data-testid="motion-div"
        data-initial={JSON.stringify(initial)}
        data-animate={JSON.stringify(animate)}
        data-variants={JSON.stringify(variants)}
        data-while-hover={JSON.stringify(whileHover)}
        data-while-tap={JSON.stringify(whileTap)}
        className={className}
        {...props}
      >
        {children}
      </div>
    ),
    button: ({ children, onClick, className, whileHover, whileTap, ...props }: any) => (
      <button
        data-testid="motion-button"
        onClick={onClick}
        className={className}
        data-while-hover={JSON.stringify(whileHover)}
        data-while-tap={JSON.stringify(whileTap)}
        {...props}
      >
        {children}
      </button>
    ),
    span: ({ children, className, animate, ...props }: any) => (
      <span
        data-testid="motion-span"
        className={className}
        data-animate={JSON.stringify(animate)}
        {...props}
      >
        {children}
      </span>
    ),
    svg: ({ children, className, ...props }: any) => (
      <svg data-testid="motion-svg" className={className} {...props}>
        {children}
      </svg>
    ),
    circle: ({ variants, ...props }: any) => (
      <circle data-testid="motion-circle" data-variants={JSON.stringify(variants)} {...props} />
    ),
    path: ({ variants, ...props }: any) => (
      <path data-testid="motion-path" data-variants={JSON.stringify(variants)} {...props} />
    ),
  },
  useInView: jest.fn(() => true),
  useSpring: jest.fn((value) => value),
  useTransform: jest.fn((value) => value),
}));

describe('Animation Variants', () => {
  describe('inkVariants', () => {
    it('should have correct hidden state', async () => {
      const { inkVariants } = await import('./animations');
      expect(inkVariants.hidden).toEqual({
        opacity: 0,
        scale: 0.8,
        filter: 'blur(10px)',
      });
    });

    it('should have correct visible state', async () => {
      const { inkVariants } = await import('./animations');
      expect(inkVariants.visible).toHaveProperty('opacity', 1);
      expect(inkVariants.visible).toHaveProperty('scale', 1);
      expect(inkVariants.visible).toHaveProperty('filter', 'blur(0px)');
    });

    it('should have correct transition configuration', async () => {
      const { inkVariants } = await import('./animations');
      const transition = inkVariants.visible.transition as any;
      expect(transition.duration).toBe(0.8);
      expect(transition.ease).toEqual([0.16, 1, 0.3, 1]);
    });
  });

  describe('sealStampVariants', () => {
    it('should have correct hidden state', async () => {
      const { sealStampVariants } = await import('./animations');
      expect(sealStampVariants.hidden).toEqual({
        opacity: 0,
        scale: 1.5,
        rotate: -15,
      });
    });

    it('should have correct visible state', async () => {
      const { sealStampVariants } = await import('./animations');
      expect(sealStampVariants.visible).toHaveProperty('opacity', 1);
      expect(sealStampVariants.visible).toHaveProperty('scale', 1);
      expect(sealStampVariants.visible).toHaveProperty('rotate', 0);
    });

    it('should use spring animation', async () => {
      const { sealStampVariants } = await import('./animations');
      const transition = sealStampVariants.visible.transition as any;
      expect(transition.type).toBe('spring');
      expect(transition.stiffness).toBe(300);
      expect(transition.damping).toBe(20);
    });
  });

  describe('brushStrokeVariants', () => {
    it('should have correct hidden state', async () => {
      const { brushStrokeVariants } = await import('./animations');
      expect(brushStrokeVariants.hidden).toEqual({
        pathLength: 0,
        opacity: 0,
      });
    });

    it('should have correct visible state', async () => {
      const { brushStrokeVariants } = await import('./animations');
      expect(brushStrokeVariants.visible).toHaveProperty('pathLength', 1);
      expect(brushStrokeVariants.visible).toHaveProperty('opacity', 1);
    });
  });

  describe('fadeUpVariants', () => {
    it('should have correct hidden state', async () => {
      const { fadeUpVariants } = await import('./animations');
      expect(fadeUpVariants.hidden).toEqual({
        opacity: 0,
        y: 30,
      });
    });

    it('should have correct visible state', async () => {
      const { fadeUpVariants } = await import('./animations');
      expect(fadeUpVariants.visible).toHaveProperty('opacity', 1);
      expect(fadeUpVariants.visible).toHaveProperty('y', 0);
    });
  });

  describe('staggerContainerVariants', () => {
    it('should have staggerChildren configured', async () => {
      const { staggerContainerVariants } = await import('./animations');
      const transition = staggerContainerVariants.visible.transition as any;
      expect(transition.staggerChildren).toBe(0.1);
      expect(transition.delayChildren).toBe(0.1);
    });
  });

  describe('staggerItemVariants', () => {
    it('should have correct hidden state', async () => {
      const { staggerItemVariants } = await import('./animations');
      expect(staggerItemVariants.hidden).toEqual({
        opacity: 0,
        y: 20,
        scale: 0.95,
      });
    });

    it('should have correct visible state', async () => {
      const { staggerItemVariants } = await import('./animations');
      expect(staggerItemVariants.visible).toHaveProperty('opacity', 1);
      expect(staggerItemVariants.visible).toHaveProperty('y', 0);
      expect(staggerItemVariants.visible).toHaveProperty('scale', 1);
    });
  });
});

describe('Animation Components', () => {
  describe('InkReveal', () => {
    it('should render children correctly', async () => {
      const { InkReveal } = await import('./animations');
      render(<InkReveal>Test Content</InkReveal>);
      expect(screen.getByText('Test Content')).toBeInTheDocument();
    });

    it('should apply custom className', async () => {
      const { InkReveal } = await import('./animations');
      render(<InkReveal className="custom-class">Test</InkReveal>);
      const element = screen.getByTestId('motion-div');
      expect(element).toHaveClass('custom-class');
    });

    it('should use inkVariants', async () => {
      const { InkReveal, inkVariants } = await import('./animations');
      render(<InkReveal>Test</InkReveal>);
      const element = screen.getByTestId('motion-div');
      const variants = JSON.parse(element.getAttribute('data-variants') || '{}');
      expect(variants).toEqual(inkVariants);
    });
  });

  describe('SealStamp', () => {
    it('should render children correctly', async () => {
      const { SealStamp } = await import('./animations');
      render(<SealStamp>Seal Content</SealStamp>);
      expect(screen.getByText('Seal Content')).toBeInTheDocument();
    });

    it('should apply custom className', async () => {
      const { SealStamp } = await import('./animations');
      render(<SealStamp className="seal-class">Test</SealStamp>);
      const element = screen.getByTestId('motion-div');
      expect(element).toHaveClass('seal-class');
    });
  });

  describe('FadeUp', () => {
    it('should render children correctly', async () => {
      const { FadeUp } = await import('./animations');
      render(<FadeUp>Fade Content</FadeUp>);
      expect(screen.getByText('Fade Content')).toBeInTheDocument();
    });

    it('should apply custom duration', async () => {
      const { FadeUp } = await import('./animations');
      render(<FadeUp duration={1.2}>Test</FadeUp>);
      const element = screen.getByTestId('motion-div');
      const variants = JSON.parse(element.getAttribute('data-variants') || '{}');
      expect(variants.visible.transition.duration).toBe(1.2);
    });

    it('should apply delay prop', async () => {
      const { FadeUp } = await import('./animations');
      render(<FadeUp delay={0.3}>Test</FadeUp>);
      const element = screen.getByTestId('motion-div');
      const variants = JSON.parse(element.getAttribute('data-variants') || '{}');
      expect(variants.visible.transition.delay).toBe(0.3);
    });
  });

  describe('StaggerContainer', () => {
    it('should render children correctly', async () => {
      const { StaggerContainer } = await import('./animations');
      render(
        <StaggerContainer>
          <div>Item 1</div>
          <div>Item 2</div>
        </StaggerContainer>
      );
      expect(screen.getByText('Item 1')).toBeInTheDocument();
      expect(screen.getByText('Item 2')).toBeInTheDocument();
    });

    it('should apply custom staggerDelay', async () => {
      const { StaggerContainer } = await import('./animations');
      render(<StaggerContainer staggerDelay={0.2}>Test</StaggerContainer>);
      const element = screen.getByTestId('motion-div');
      const variants = JSON.parse(element.getAttribute('data-variants') || '{}');
      expect(variants.visible.transition.staggerChildren).toBe(0.2);
    });
  });

  describe('StaggerItem', () => {
    it('should render children correctly', async () => {
      const { StaggerItem } = await import('./animations');
      render(<StaggerItem>Item Content</StaggerItem>);
      expect(screen.getByText('Item Content')).toBeInTheDocument();
    });
  });

  describe('RippleButton', () => {
    it('should render children correctly', async () => {
      const { RippleButton } = await import('./animations');
      render(<RippleButton>Click Me</RippleButton>);
      expect(screen.getByText('Click Me')).toBeInTheDocument();
    });

    it('should handle click events', async () => {
      const { RippleButton } = await import('./animations');
      const handleClick = jest.fn();
      render(<RippleButton onClick={handleClick}>Click Me</RippleButton>);
      
      const button = screen.getByTestId('motion-button');
      fireEvent.click(button);
      
      expect(handleClick).toHaveBeenCalled();
    });

    it('should apply custom className', async () => {
      const { RippleButton } = await import('./animations');
      render(<RippleButton className="custom-button">Test</RippleButton>);
      const element = screen.getByTestId('motion-button');
      expect(element).toHaveClass('custom-button');
    });
  });

  describe('InkCard', () => {
    it('should render children correctly', async () => {
      const { InkCard } = await import('./animations');
      render(<InkCard>Card Content</InkCard>);
      expect(screen.getByText('Card Content')).toBeInTheDocument();
    });

    it('should apply custom hoverScale', async () => {
      const { InkCard } = await import('./animations');
      render(<InkCard hoverScale={1.1}>Test</InkCard>);
      const element = screen.getByTestId('motion-div');
      const whileHover = JSON.parse(element.getAttribute('data-while-hover') || '{}');
      expect(whileHover.scale).toBe(1.1);
    });
  });

  describe('CountUp', () => {
    it('should render with prefix and suffix', async () => {
      const { CountUp } = await import('./animations');
      render(<CountUp end={100} prefix="$" suffix="%" />);
      expect(screen.getByText(/\$/)).toBeInTheDocument();
      expect(screen.getByText(/%/)).toBeInTheDocument();
    });

    it('should apply custom className', async () => {
      const { CountUp } = await import('./animations');
      render(<CountUp end={100} className="counter-class" />);
      const element = screen.getByTestId('motion-span');
      expect(element).toHaveClass('counter-class');
    });
  });

  describe('Typewriter', () => {
    it('should render component correctly', async () => {
      const { Typewriter } = await import('./animations');
      render(<Typewriter text="Hello" />);
      const cursor = screen.getByText('|');
      expect(cursor).toBeInTheDocument();
    });

    it('should apply custom className', async () => {
      const { Typewriter } = await import('./animations');
      render(<Typewriter text="Test" className="typewriter-class" />);
      const container = screen.getByText('|').closest('.typewriter-class');
      expect(container).toBeInTheDocument();
    });
  });

  describe('FloatingElement', () => {
    it('should render children correctly', async () => {
      const { FloatingElement } = await import('./animations');
      render(<FloatingElement>Floating Content</FloatingElement>);
      expect(screen.getByText('Floating Content')).toBeInTheDocument();
    });

    it('should apply custom amplitude', async () => {
      const { FloatingElement } = await import('./animations');
      render(<FloatingElement amplitude={20}>Test</FloatingElement>);
      const element = screen.getByTestId('motion-div');
      const animate = JSON.parse(element.getAttribute('data-animate') || '{}');
      expect(animate.y).toEqual([-20, 20, -20]);
    });
  });

  describe('PulseElement', () => {
    it('should render children correctly', async () => {
      const { PulseElement } = await import('./animations');
      render(<PulseElement>Pulse Content</PulseElement>);
      expect(screen.getByText('Pulse Content')).toBeInTheDocument();
    });

    it('should apply custom scale', async () => {
      const { PulseElement } = await import('./animations');
      render(<PulseElement scale={1.1}>Test</PulseElement>);
      const element = screen.getByTestId('motion-div');
      const animate = JSON.parse(element.getAttribute('data-animate') || '{}');
      expect(animate.scale).toEqual([1, 1.1, 1]);
    });
  });

  describe('GradientText', () => {
    it('should render children correctly', async () => {
      const { GradientText } = await import('./animations');
      render(<GradientText>Gradient Text</GradientText>);
      expect(screen.getByText('Gradient Text')).toBeInTheDocument();
    });

    it('should apply custom colors', async () => {
      const { GradientText } = await import('./animations');
      render(<GradientText colors={['#ff0000', '#00ff00', '#0000ff']}>Test</GradientText>);
      const element = screen.getByTestId('motion-span');
      expect(element).toBeInTheDocument();
    });
  });

  describe('SplitText', () => {
    it('should render text correctly', async () => {
      const { SplitText } = await import('./animations');
      render(<SplitText text="Hi" />);
      expect(screen.getByText('H')).toBeInTheDocument();
      expect(screen.getByText('i')).toBeInTheDocument();
    });

    it('should apply custom className', async () => {
      const { SplitText } = await import('./animations');
      render(<SplitText text="Test" className="split-class" />);
      const elements = screen.getAllByTestId('motion-span');
      const parentElement = elements.find(el => el.classList.contains('split-class'));
      expect(parentElement).toBeDefined();
    });
  });

  describe('GlitchText', () => {
    it('should render text correctly', async () => {
      const { GlitchText } = await import('./animations');
      render(<GlitchText text="Glitch" />);
      const glitchElements = screen.getAllByText('Glitch');
      expect(glitchElements.length).toBeGreaterThan(0);
    });

    it('should apply custom className', async () => {
      const { GlitchText } = await import('./animations');
      render(<GlitchText text="Test" className="glitch-class" />);
      const testElements = screen.getAllByText('Test');
      const container = testElements[0].closest('.glitch-class');
      expect(container).toBeInTheDocument();
    });
  });

  describe('MagneticButton', () => {
    it('should render children correctly', async () => {
      const { MagneticButton } = await import('./animations');
      render(<MagneticButton>Magnetic</MagneticButton>);
      expect(screen.getByText('Magnetic')).toBeInTheDocument();
    });

    it('should handle click events', async () => {
      const { MagneticButton } = await import('./animations');
      const handleClick = jest.fn();
      render(<MagneticButton onClick={handleClick}>Click</MagneticButton>);
      
      const element = screen.getByText('Click');
      fireEvent.click(element);
      
      expect(handleClick).toHaveBeenCalled();
    });
  });

  describe('BlurReveal', () => {
    it('should render children correctly', async () => {
      const { BlurReveal } = await import('./animations');
      render(<BlurReveal>Blur Content</BlurReveal>);
      expect(screen.getByText('Blur Content')).toBeInTheDocument();
    });

    it('should apply custom className', async () => {
      const { BlurReveal } = await import('./animations');
      render(<BlurReveal className="blur-class">Test</BlurReveal>);
      const element = screen.getByTestId('motion-div');
      expect(element).toHaveClass('blur-class');
    });
  });

  describe('WaveText', () => {
    it('should render text correctly', async () => {
      const { WaveText } = await import('./animations');
      render(<WaveText text="Hi" />);
      expect(screen.getByText('H')).toBeInTheDocument();
      expect(screen.getByText('i')).toBeInTheDocument();
    });
  });

  describe('ShimmerButton', () => {
    it('should render children correctly', async () => {
      const { ShimmerButton } = await import('./animations');
      render(<ShimmerButton>Shimmer</ShimmerButton>);
      expect(screen.getByText('Shimmer')).toBeInTheDocument();
    });

    it('should handle click events', async () => {
      const { ShimmerButton } = await import('./animations');
      const handleClick = jest.fn();
      render(<ShimmerButton onClick={handleClick}>Click</ShimmerButton>);
      
      const button = screen.getByTestId('motion-button');
      fireEvent.click(button);
      
      expect(handleClick).toHaveBeenCalled();
    });
  });
});

describe('Animation Hooks', () => {
  describe('useParallax', () => {
    it('should be defined', async () => {
      const { useParallax } = await import('./animations');
      expect(useParallax).toBeDefined();
    });
  });

  describe('useSmoothSpring', () => {
    it('should be defined', async () => {
      const { useSmoothSpring } = await import('./animations');
      expect(useSmoothSpring).toBeDefined();
    });
  });
});

describe('SVG Components', () => {
  describe('InkDropSVG', () => {
    it('should render SVG correctly', async () => {
      const { InkDropSVG } = await import('./animations');
      render(<InkDropSVG />);
      expect(screen.getByTestId('motion-svg')).toBeInTheDocument();
      expect(screen.getByTestId('motion-circle')).toBeInTheDocument();
    });

    it('should apply custom className', async () => {
      const { InkDropSVG } = await import('./animations');
      render(<InkDropSVG className="ink-drop-class" />);
      const element = screen.getByTestId('motion-svg');
      expect(element).toHaveClass('ink-drop-class');
    });
  });

  describe('InkSplash', () => {
    it('should render SVG correctly', async () => {
      const { InkSplash } = await import('./animations');
      render(<InkSplash />);
      expect(screen.getByTestId('motion-svg')).toBeInTheDocument();
      expect(screen.getByTestId('motion-path')).toBeInTheDocument();
    });

    it('should apply custom color', async () => {
      const { InkSplash } = await import('./animations');
      render(<InkSplash color="#ff0000" />);
      const path = screen.getByTestId('motion-path');
      expect(path).toHaveAttribute('fill', '#ff0000');
    });

    it('should apply custom size', async () => {
      const { InkSplash } = await import('./animations');
      render(<InkSplash size={200} />);
      const svg = screen.getByTestId('motion-svg');
      expect(svg).toHaveAttribute('width', '200');
      expect(svg).toHaveAttribute('height', '200');
    });
  });
});
