/**
 * ImagePreview Component Unit Tests
 * Tests for full-screen image preview with navigation
 */

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ImagePreview } from './ImagePreview';
import type { TransactionImage } from '../../../types';

describe('ImagePreview', () => {
  const mockImages: TransactionImage[] = [
    {
      id: 1,
      transactionId: 100,
      filePath: '/uploads/image1.jpg',
      fileName: 'receipt1.jpg',
      fileSize: 102400, // 100 KB
      mimeType: 'image/jpeg',
      createdAt: '2024-01-01T10:00:00Z',
    },
    {
      id: 2,
      transactionId: 100,
      filePath: '/uploads/image2.jpg',
      fileName: 'receipt2.jpg',
      fileSize: 204800, // 200 KB
      mimeType: 'image/jpeg',
      createdAt: '2024-01-01T10:01:00Z',
    },
    {
      id: 3,
      transactionId: 100,
      filePath: '/uploads/image3.jpg',
      fileName: 'receipt3.jpg',
      fileSize: 153600, // 150 KB
      mimeType: 'image/jpeg',
      createdAt: '2024-01-01T10:02:00Z',
    },
  ];

  const mockOnClose = vi.fn();

  beforeEach(() => {
    mockOnClose.mockClear();
  });

  afterEach(() => {
    // Restore body overflow
    document.body.style.overflow = '';
  });

  describe('Rendering', () => {
    it('should not render when open is false', () => {
      const { container } = render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={false}
          onClose={mockOnClose}
        />
      );

      expect(container.firstChild).toBeNull();
    });

    it('should render when open is true', () => {
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      expect(screen.getByRole('dialog')).toBeInTheDocument();
      expect(screen.getByLabelText('图片预览')).toBeInTheDocument();
    });

    it('should not render when images array is empty', () => {
      const { container } = render(
        <ImagePreview
          images={[]}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      expect(container.firstChild).toBeNull();
    });

    it('should display the correct image at initialIndex', () => {
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={1}
          open={true}
          onClose={mockOnClose}
        />
      );

      const image = screen.getByAltText('receipt2.jpg');
      expect(image).toBeInTheDocument();
      expect(image).toHaveAttribute('src', expect.stringContaining('/images/2'));
    });

    it('should display image counter', () => {
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      expect(screen.getByText('1 / 3')).toBeInTheDocument();
    });

    it('should display image filename and filesize', () => {
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      expect(screen.getByText('receipt1.jpg')).toBeInTheDocument();
      expect(screen.getByText('100.0 KB')).toBeInTheDocument();
    });

    it('should display navigation buttons when multiple images', () => {
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      expect(screen.getByLabelText('上一张')).toBeInTheDocument();
      expect(screen.getByLabelText('下一张')).toBeInTheDocument();
    });

    it('should not display navigation buttons when single image', () => {
      render(
        <ImagePreview
          images={[mockImages[0]]}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      expect(screen.queryByLabelText('上一张')).not.toBeInTheDocument();
      expect(screen.queryByLabelText('下一张')).not.toBeInTheDocument();
    });
  });

  describe('Close Functionality', () => {
    it('should call onClose when close button is clicked', async () => {
      const user = userEvent.setup();
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      const closeButton = screen.getByLabelText('关闭预览');
      await user.click(closeButton);

      expect(mockOnClose).toHaveBeenCalledTimes(1);
    });

    it('should call onClose when overlay is clicked', async () => {
      const user = userEvent.setup();
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      const overlay = screen.getByRole('dialog');
      await user.click(overlay);

      expect(mockOnClose).toHaveBeenCalledTimes(1);
    });

    it('should not call onClose when image container is clicked', async () => {
      const user = userEvent.setup();
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      const image = screen.getByAltText('receipt1.jpg');
      await user.click(image);

      expect(mockOnClose).not.toHaveBeenCalled();
    });

    it('should call onClose when Escape key is pressed', () => {
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      fireEvent.keyDown(window, { key: 'Escape' });

      expect(mockOnClose).toHaveBeenCalledTimes(1);
    });
  });

  describe('Navigation', () => {
    it('should navigate to next image when next button is clicked', async () => {
      const user = userEvent.setup();
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      expect(screen.getByText('1 / 3')).toBeInTheDocument();
      expect(screen.getByAltText('receipt1.jpg')).toBeInTheDocument();

      const nextButton = screen.getByLabelText('下一张');
      await user.click(nextButton);

      await waitFor(() => {
        expect(screen.getByText('2 / 3')).toBeInTheDocument();
        expect(screen.getByAltText('receipt2.jpg')).toBeInTheDocument();
      });
    });

    it('should navigate to previous image when previous button is clicked', async () => {
      const user = userEvent.setup();
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={1}
          open={true}
          onClose={mockOnClose}
        />
      );

      expect(screen.getByText('2 / 3')).toBeInTheDocument();
      expect(screen.getByAltText('receipt2.jpg')).toBeInTheDocument();

      const prevButton = screen.getByLabelText('上一张');
      await user.click(prevButton);

      await waitFor(() => {
        expect(screen.getByText('1 / 3')).toBeInTheDocument();
        expect(screen.getByAltText('receipt1.jpg')).toBeInTheDocument();
      });
    });

    it('should wrap to last image when clicking previous on first image', async () => {
      const user = userEvent.setup();
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      expect(screen.getByText('1 / 3')).toBeInTheDocument();

      const prevButton = screen.getByLabelText('上一张');
      await user.click(prevButton);

      await waitFor(() => {
        expect(screen.getByText('3 / 3')).toBeInTheDocument();
        expect(screen.getByAltText('receipt3.jpg')).toBeInTheDocument();
      });
    });

    it('should wrap to first image when clicking next on last image', async () => {
      const user = userEvent.setup();
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={2}
          open={true}
          onClose={mockOnClose}
        />
      );

      expect(screen.getByText('3 / 3')).toBeInTheDocument();

      const nextButton = screen.getByLabelText('下一张');
      await user.click(nextButton);

      await waitFor(() => {
        expect(screen.getByText('1 / 3')).toBeInTheDocument();
        expect(screen.getByAltText('receipt1.jpg')).toBeInTheDocument();
      });
    });

    it('should navigate to next image when ArrowRight key is pressed', () => {
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      expect(screen.getByText('1 / 3')).toBeInTheDocument();

      fireEvent.keyDown(window, { key: 'ArrowRight' });

      waitFor(() => {
        expect(screen.getByText('2 / 3')).toBeInTheDocument();
      });
    });

    it('should navigate to previous image when ArrowLeft key is pressed', () => {
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={1}
          open={true}
          onClose={mockOnClose}
        />
      );

      expect(screen.getByText('2 / 3')).toBeInTheDocument();

      fireEvent.keyDown(window, { key: 'ArrowLeft' });

      waitFor(() => {
        expect(screen.getByText('1 / 3')).toBeInTheDocument();
      });
    });
  });

  describe('Body Scroll Lock', () => {
    it('should lock body scroll when modal is open', () => {
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      expect(document.body.style.overflow).toBe('hidden');
    });

    it('should restore body scroll when modal is closed', () => {
      const { rerender } = render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      expect(document.body.style.overflow).toBe('hidden');

      rerender(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={false}
          onClose={mockOnClose}
        />
      );

      expect(document.body.style.overflow).toBe('');
    });
  });

  describe('Index Reset', () => {
    it('should reset to initialIndex when it changes', () => {
      const { rerender } = render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      expect(screen.getByText('1 / 3')).toBeInTheDocument();

      rerender(
        <ImagePreview
          images={mockImages}
          initialIndex={2}
          open={true}
          onClose={mockOnClose}
        />
      );

      waitFor(() => {
        expect(screen.getByText('3 / 3')).toBeInTheDocument();
      });
    });
  });

  describe('Touch/Swipe Navigation', () => {
    it('should navigate to next image on left swipe', () => {
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      const container = screen.getByAltText('receipt1.jpg').parentElement!;

      // Simulate left swipe (swipe from right to left)
      fireEvent.touchStart(container, {
        targetTouches: [{ clientX: 200 }],
      });
      fireEvent.touchMove(container, {
        targetTouches: [{ clientX: 100 }],
      });
      fireEvent.touchEnd(container);

      waitFor(() => {
        expect(screen.getByText('2 / 3')).toBeInTheDocument();
      });
    });

    it('should navigate to previous image on right swipe', () => {
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={1}
          open={true}
          onClose={mockOnClose}
        />
      );

      const container = screen.getByAltText('receipt2.jpg').parentElement!;

      // Simulate right swipe (swipe from left to right)
      fireEvent.touchStart(container, {
        targetTouches: [{ clientX: 100 }],
      });
      fireEvent.touchMove(container, {
        targetTouches: [{ clientX: 200 }],
      });
      fireEvent.touchEnd(container);

      waitFor(() => {
        expect(screen.getByText('1 / 3')).toBeInTheDocument();
      });
    });

    it('should not navigate on small swipe distance', () => {
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      const container = screen.getByAltText('receipt1.jpg').parentElement!;

      // Simulate small swipe (less than minimum distance)
      fireEvent.touchStart(container, {
        targetTouches: [{ clientX: 100 }],
      });
      fireEvent.touchMove(container, {
        targetTouches: [{ clientX: 120 }],
      });
      fireEvent.touchEnd(container);

      // Should still be on first image
      expect(screen.getByText('1 / 3')).toBeInTheDocument();
    });
  });

  describe('Accessibility', () => {
    it('should have proper ARIA attributes', () => {
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      const dialog = screen.getByRole('dialog');
      expect(dialog).toHaveAttribute('aria-modal', 'true');
      expect(dialog).toHaveAttribute('aria-label', '图片预览');
    });

    it('should have proper button labels', () => {
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      expect(screen.getByLabelText('关闭预览')).toBeInTheDocument();
      expect(screen.getByLabelText('上一张')).toBeInTheDocument();
      expect(screen.getByLabelText('下一张')).toBeInTheDocument();
    });

    it('should have non-draggable image', () => {
      render(
        <ImagePreview
          images={mockImages}
          initialIndex={0}
          open={true}
          onClose={mockOnClose}
        />
      );

      const image = screen.getByAltText('receipt1.jpg');
      expect(image).toHaveAttribute('draggable', 'false');
    });
  });
});
