/**
 * @ai-summary Unit tests for DocumentPreview component
 * @ai-context Tests image/PDF preview with mocked API calls
 */

import { render, screen, waitFor } from '@testing-library/react';
import { DocumentPreview } from './DocumentPreview';
import { documentsApi } from '../api/documents.api';
import type { DocumentRecord } from '../types/documents.types';

// Mock the documents API
jest.mock('../api/documents.api');
const mockDocumentsApi = jest.mocked(documentsApi);

// Mock URL.createObjectURL and revokeObjectURL
const mockCreateObjectURL = jest.fn();
const mockRevokeObjectURL = jest.fn();
Object.defineProperty(global.URL, 'createObjectURL', {
  value: mockCreateObjectURL,
});
Object.defineProperty(global.URL, 'revokeObjectURL', {
  value: mockRevokeObjectURL,
});

describe('DocumentPreview', () => {
  const mockPdfDocument: DocumentRecord = {
    id: 'doc-1',
    user_id: 'user-1',
    vehicle_id: 'vehicle-1',
    document_type: 'insurance',
    title: 'Insurance Document',
    content_type: 'application/pdf',
    created_at: '2024-01-01T00:00:00Z',
    updated_at: '2024-01-01T00:00:00Z',
  };

  const mockImageDocument: DocumentRecord = {
    id: 'doc-2',
    user_id: 'user-1',
    vehicle_id: 'vehicle-1',
    document_type: 'registration',
    title: 'Registration Photo',
    content_type: 'image/jpeg',
    created_at: '2024-01-01T00:00:00Z',
    updated_at: '2024-01-01T00:00:00Z',
  };

  const mockNonPreviewableDocument: DocumentRecord = {
    id: 'doc-3',
    user_id: 'user-1',
    vehicle_id: 'vehicle-1',
    document_type: 'insurance',
    title: 'Text Document',
    content_type: 'text/plain',
    created_at: '2024-01-01T00:00:00Z',
    updated_at: '2024-01-01T00:00:00Z',
  };

  beforeEach(() => {
    jest.clearAllMocks();
    mockCreateObjectURL.mockReturnValue('blob:http://localhost/test-blob');
  });

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

  describe('PDF Preview', () => {
    it('should render PDF preview for PDF documents', async () => {
      const mockBlob = new Blob(['fake pdf content'], { type: 'application/pdf' });
      mockDocumentsApi.download.mockResolvedValue(mockBlob);

      render(<DocumentPreview doc={mockPdfDocument} />);

      // Should show loading initially
      expect(screen.getByText('Loading preview...')).toBeInTheDocument();

      // Wait for the PDF object to appear
      await waitFor(() => {
        const pdfObject = screen.getByRole('application', { name: 'PDF Preview' });
        expect(pdfObject).toBeInTheDocument();
        expect(pdfObject).toHaveAttribute('data', 'blob:http://localhost/test-blob');
        expect(pdfObject).toHaveAttribute('type', 'application/pdf');
      });

      expect(mockDocumentsApi.download).toHaveBeenCalledWith('doc-1');
      expect(mockCreateObjectURL).toHaveBeenCalledWith(mockBlob);
    });

    it('should provide fallback link for PDF when object fails', async () => {
      const mockBlob = new Blob(['fake pdf content'], { type: 'application/pdf' });
      mockDocumentsApi.download.mockResolvedValue(mockBlob);

      render(<DocumentPreview doc={mockPdfDocument} />);

      await waitFor(() => {
        // Check that fallback link exists within the object element
        const fallbackLink = screen.getByRole('link', { name: 'Open PDF' });
        expect(fallbackLink).toBeInTheDocument();
        expect(fallbackLink).toHaveAttribute('href', 'blob:http://localhost/test-blob');
        expect(fallbackLink).toHaveAttribute('target', '_blank');
      });
    });
  });

  describe('Image Preview', () => {
    it('should render image preview for image documents', async () => {
      const mockBlob = new Blob(['fake image content'], { type: 'image/jpeg' });
      mockDocumentsApi.download.mockResolvedValue(mockBlob);

      render(<DocumentPreview doc={mockImageDocument} />);

      // Should show loading initially
      expect(screen.getByText('Loading preview...')).toBeInTheDocument();

      // Wait for the image to appear
      await waitFor(() => {
        const image = screen.getByRole('img', { name: 'Registration Photo' });
        expect(image).toBeInTheDocument();
        expect(image).toHaveAttribute('src', 'blob:http://localhost/test-blob');
      });

      expect(mockDocumentsApi.download).toHaveBeenCalledWith('doc-2');
      expect(mockCreateObjectURL).toHaveBeenCalledWith(mockBlob);
    });

    it('should have proper image styling', async () => {
      const mockBlob = new Blob(['fake image content'], { type: 'image/jpeg' });
      mockDocumentsApi.download.mockResolvedValue(mockBlob);

      render(<DocumentPreview doc={mockImageDocument} />);

      await waitFor(() => {
        const image = screen.getByRole('img');
        expect(image).toHaveClass('max-w-full', 'h-auto', 'rounded-lg', 'border');
      });
    });
  });

  describe('Non-previewable Documents', () => {
    it('should show no preview message for non-previewable documents', () => {
      render(<DocumentPreview doc={mockNonPreviewableDocument} />);

      expect(screen.getByText('No preview available.')).toBeInTheDocument();
      expect(mockDocumentsApi.download).not.toHaveBeenCalled();
    });

    it('should not create blob URL for non-previewable documents', () => {
      render(<DocumentPreview doc={mockNonPreviewableDocument} />);

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

  describe('Error Handling', () => {
    it('should show error message when download fails', async () => {
      mockDocumentsApi.download.mockRejectedValue(new Error('Download failed'));

      render(<DocumentPreview doc={mockPdfDocument} />);

      await waitFor(() => {
        expect(screen.getByText('Failed to load preview')).toBeInTheDocument();
      });

      expect(mockDocumentsApi.download).toHaveBeenCalledWith('doc-1');
      expect(mockCreateObjectURL).not.toHaveBeenCalled();
    });

    it('should handle network errors gracefully', async () => {
      mockDocumentsApi.download.mockRejectedValue(new Error('Network error'));

      render(<DocumentPreview doc={mockImageDocument} />);

      await waitFor(() => {
        expect(screen.getByText('Failed to load preview')).toBeInTheDocument();
      });
    });
  });

  describe('Content Type Detection', () => {
    it('should detect PDF from content type', () => {
      render(<DocumentPreview doc={mockPdfDocument} />);

      // PDF should be considered previewable
      expect(screen.queryByText('No preview available.')).not.toBeInTheDocument();
      expect(screen.getByText('Loading preview...')).toBeInTheDocument();
    });

    it('should detect images from content type', () => {
      render(<DocumentPreview doc={mockImageDocument} />);

      // Image should be considered previewable
      expect(screen.queryByText('No preview available.')).not.toBeInTheDocument();
      expect(screen.getByText('Loading preview...')).toBeInTheDocument();
    });

    it('should handle PNG images', async () => {
      const pngDocument = { ...mockImageDocument, content_type: 'image/png' };
      const mockBlob = new Blob(['fake png content'], { type: 'image/png' });
      mockDocumentsApi.download.mockResolvedValue(mockBlob);

      render(<DocumentPreview doc={pngDocument} />);

      await waitFor(() => {
        const image = screen.getByRole('img');
        expect(image).toBeInTheDocument();
      });
    });

    it('should handle documents with undefined content type', () => {
      const undefinedTypeDoc = { ...mockPdfDocument, content_type: undefined };
      render(<DocumentPreview doc={undefinedTypeDoc} />);

      expect(screen.getByText('No preview available.')).toBeInTheDocument();
    });
  });

  describe('Memory Management', () => {
    it('should clean up blob URL on unmount', async () => {
      const mockBlob = new Blob(['fake content'], { type: 'application/pdf' });
      mockDocumentsApi.download.mockResolvedValue(mockBlob);

      const { unmount } = render(<DocumentPreview doc={mockPdfDocument} />);

      await waitFor(() => {
        expect(mockCreateObjectURL).toHaveBeenCalled();
      });

      unmount();

      expect(mockRevokeObjectURL).toHaveBeenCalledWith('blob:http://localhost/test-blob');
    });

    it('should clean up blob URL when document changes', async () => {
      const mockBlob1 = new Blob(['fake content 1'], { type: 'application/pdf' });
      const mockBlob2 = new Blob(['fake content 2'], { type: 'image/jpeg' });

      mockDocumentsApi.download
        .mockResolvedValueOnce(mockBlob1)
        .mockResolvedValueOnce(mockBlob2);

      const { rerender } = render(<DocumentPreview doc={mockPdfDocument} />);

      await waitFor(() => {
        expect(mockCreateObjectURL).toHaveBeenCalledWith(mockBlob1);
      });

      // Change to different document
      rerender(<DocumentPreview doc={mockImageDocument} />);

      await waitFor(() => {
        expect(mockRevokeObjectURL).toHaveBeenCalledWith('blob:http://localhost/test-blob');
        expect(mockCreateObjectURL).toHaveBeenCalledWith(mockBlob2);
      });
    });
  });
});