import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { FileUpload } from '@/components/files/FileUpload';
import { FileList } from '@/components/files/FileList';
import { FileGallery } from '@/components/files/FileGallery';
import { FilePreview } from '@/components/files/FilePreview';
import {
  formatFileSize,
  isImageMime,
  MAX_FILE_SIZE_MB,
} from '@/lib/files';
import { apiFetch } from '@/lib/api';

// Mock the api module
vi.mock('@/lib/api', () => ({
  apiFetch: vi.fn(),
  API_BASE_URL: 'http://localhost:8000/api/v1',
}));

const mockedApiFetch = vi.mocked(apiFetch);

// Mock fetch for file operations
const mockFetch = vi.fn();
global.fetch = mockFetch as any;

// Mock URL.createObjectURL and URL.revokeObjectURL
global.URL.createObjectURL = vi.fn(() => 'blob:http://test/blob123');
global.URL.revokeObjectURL = vi.fn();

describe('FileUpload', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockFetch.mockResolvedValue({
      ok: true,
      status: 201,
      json: async () => ({
        id: 'file-1',
        vehicle_id: 'veh-1',
        original_filename: 'test.jpg',
        stored_filename: 'abc123.jpg',
        file_path: '/tmp/uploads/veh-1/abc123.jpg',
        mime_type: 'image/jpeg',
        file_size: 1024,
        thumbnail_path: '/tmp/uploads/veh-1/thumbnails/thumb_abc123.jpg',
        created_at: '2025-01-01T00:00:00Z',
        updated_at: '2025-01-01T00:00:00Z',
      }),
    });
  });

  it('renders drag-and-drop zone', () => {
    render(<FileUpload vehicleId="veh-1" />);
    expect(screen.getByTestId('dropzone')).toBeDefined();
    expect(screen.getByTestId('file-input')).toBeDefined();
    expect(screen.getByText(/Drag & drop files or click to browse/i)).toBeDefined();
  });

  it('shows supported file types and size limit', () => {
    render(<FileUpload vehicleId="veh-1" />);
    expect(screen.getByText(new RegExp(`max ${MAX_FILE_SIZE_MB}MB`, 'i'))).toBeDefined();
  });

  it('accepts file selection via input', async () => {
    render(<FileUpload vehicleId="veh-1" />);
    const input = screen.getByTestId('file-input') as HTMLInputElement;

    const file = new File(['test image data'], 'test.jpg', { type: 'image/jpeg' });
    fireEvent.change(input, { target: { files: [file] } });

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

  it('shows upload status after file selection', async () => {
    render(<FileUpload vehicleId="veh-1" />);
    const input = screen.getByTestId('file-input') as HTMLInputElement;

    const file = new File(['test image data'], 'test.jpg', { type: 'image/jpeg' });
    fireEvent.change(input, { target: { files: [file] } });

    await waitFor(() => {
      expect(screen.getByTestId('upload-statuses')).toBeDefined();
    });
  });

  it('rejects unsupported file types', async () => {
    render(<FileUpload vehicleId="veh-1" />);
    const input = screen.getByTestId('file-input') as HTMLInputElement;

    const file = new File(['malware'], 'malware.exe', { type: 'application/x-msdownload' });
    fireEvent.change(input, { target: { files: [file] } });

    await waitFor(() => {
      expect(screen.getByText(/Unsupported file type/i)).toBeDefined();
    });
  });

  it('rejects oversized files', async () => {
    render(<FileUpload vehicleId="veh-1" />);
    const input = screen.getByTestId('file-input') as HTMLInputElement;

    // Create a file larger than 20MB
    const largeContent = new Array(21 * 1024 * 1024).fill('x').join('');
    const file = new File([largeContent], 'large.jpg', { type: 'image/jpeg' });
    fireEvent.change(input, { target: { files: [file] } });

    await waitFor(() => {
      expect(screen.getByText(/exceeds.*MB limit/i)).toBeDefined();
    });
  });

  it('calls onUploadComplete on successful upload', async () => {
    const onUploadComplete = vi.fn();
    render(<FileUpload vehicleId="veh-1" onUploadComplete={onUploadComplete} />);
    const input = screen.getByTestId('file-input') as HTMLInputElement;

    const file = new File(['test image data'], 'test.jpg', { type: 'image/jpeg' });
    fireEvent.change(input, { target: { files: [file] } });

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

describe('FileList', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockedApiFetch.mockResolvedValue({
      items: [
        {
          id: 'file-1',
          vehicle_id: 'veh-1',
          original_filename: 'photo.jpg',
          stored_filename: 'abc123.jpg',
          file_path: '/tmp/uploads/veh-1/abc123.jpg',
          mime_type: 'image/jpeg',
          file_size: 102400,
          thumbnail_path: '/tmp/uploads/veh-1/thumbnails/thumb_abc123.jpg',
          created_at: '2025-01-01T00:00:00Z',
          updated_at: '2025-01-01T00:00:00Z',
        },
        {
          id: 'file-2',
          vehicle_id: 'veh-1',
          original_filename: 'document.pdf',
          stored_filename: 'def456.pdf',
          file_path: '/tmp/uploads/veh-1/def456.pdf',
          mime_type: 'application/pdf',
          file_size: 204800,
          thumbnail_path: null,
          created_at: '2025-01-01T00:00:00Z',
          updated_at: '2025-01-01T00:00:00Z',
        },
      ],
      total: 2,
      page: 1,
      page_size: 20,
    } as any);
  });

  it('renders file list with items', async () => {
    render(<FileList vehicleId="veh-1" />);
    await waitFor(() => {
      expect(screen.getByTestId('file-list')).toBeDefined();
    });
    expect(screen.getByText('photo.jpg')).toBeDefined();
    expect(screen.getByText('document.pdf')).toBeDefined();
  });

  it('shows thumbnails for image files', async () => {
    render(<FileList vehicleId="veh-1" />);
    await waitFor(() => {
      expect(screen.getByTestId('file-thumb-file-1')).toBeDefined();
    });
  });

  it('shows file icon for non-image files', async () => {
    render(<FileList vehicleId="veh-1" />);
    await waitFor(() => {
      expect(screen.getByTestId('file-item-file-2')).toBeDefined();
    });
    // Non-image file should not have a thumbnail img
    expect(screen.queryByTestId('file-thumb-file-2')).toBeNull();
  });

  it('shows delete button for each file', async () => {
    render(<FileList vehicleId="veh-1" />);
    await waitFor(() => {
      expect(screen.getByTestId('delete-btn-file-1')).toBeDefined();
      expect(screen.getByTestId('delete-btn-file-2')).toBeDefined();
    });
  });

  it('shows download button for each file', async () => {
    render(<FileList vehicleId="veh-1" />);
    await waitFor(() => {
      expect(screen.getByTestId('download-btn-file-1')).toBeDefined();
      expect(screen.getByTestId('download-btn-file-2')).toBeDefined();
    });
  });

  it('opens delete confirmation dialog', async () => {
    render(<FileList vehicleId="veh-1" />);
    await waitFor(() => {
      expect(screen.getByTestId('delete-btn-file-1')).toBeDefined();
    });

    fireEvent.click(screen.getByTestId('delete-btn-file-1'));

    await waitFor(() => {
      expect(screen.getByTestId('delete-confirm')).toBeDefined();
      expect(screen.getByTestId('confirm-delete-btn')).toBeDefined();
    });
  });

  it('shows empty state when no files', async () => {
    mockedApiFetch.mockResolvedValueOnce({
      items: [],
      total: 0,
      page: 1,
      page_size: 20,
    } as any);

    render(<FileList vehicleId="veh-1" />);
    await waitFor(() => {
      expect(screen.getByTestId('file-list-empty')).toBeDefined();
    });
  });

  it('shows error state on API failure', async () => {
    mockedApiFetch.mockRejectedValueOnce({
      error: { code: 'UNKNOWN', message: 'Network error' },
    });

    render(<FileList vehicleId="veh-1" />);
    await waitFor(() => {
      expect(screen.getByTestId('file-list-error')).toBeDefined();
    });
  });
});

describe('FileGallery', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockedApiFetch.mockResolvedValue({
      items: [
        {
          id: 'img-1',
          vehicle_id: 'veh-1',
          original_filename: 'photo1.jpg',
          stored_filename: 'abc123.jpg',
          file_path: '/tmp/uploads/veh-1/abc123.jpg',
          mime_type: 'image/jpeg',
          file_size: 102400,
          thumbnail_path: '/tmp/uploads/veh-1/thumbnails/thumb_abc123.jpg',
          created_at: '2025-01-01T00:00:00Z',
          updated_at: '2025-01-01T00:00:00Z',
        },
        {
          id: 'img-2',
          vehicle_id: 'veh-1',
          original_filename: 'photo2.png',
          stored_filename: 'def456.png',
          file_path: '/tmp/uploads/veh-1/def456.png',
          mime_type: 'image/png',
          file_size: 204800,
          thumbnail_path: '/tmp/uploads/veh-1/thumbnails/thumb_def456.png',
          created_at: '2025-01-01T00:00:00Z',
          updated_at: '2025-01-01T00:00:00Z',
        },
      ],
      total: 2,
      page: 1,
      page_size: 100,
    } as any);
  });

  it('renders gallery with images', async () => {
    render(<FileGallery vehicleId="veh-1" />);
    await waitFor(() => {
      expect(screen.getByTestId('file-gallery')).toBeDefined();
    });
    expect(screen.getByTestId('gallery-item-img-1')).toBeDefined();
    expect(screen.getByTestId('gallery-item-img-2')).toBeDefined();
  });

  it('shows empty state when no images', async () => {
    mockedApiFetch.mockResolvedValueOnce({
      items: [],
      total: 0,
      page: 1,
      page_size: 100,
    } as any);

    render(<FileGallery vehicleId="veh-1" />);
    await waitFor(() => {
      expect(screen.getByTestId('gallery-empty')).toBeDefined();
    });
  });

  it('filters out non-image files', async () => {
    mockedApiFetch.mockResolvedValueOnce({
      items: [
        {
          id: 'img-1',
          vehicle_id: 'veh-1',
          original_filename: 'photo.jpg',
          stored_filename: 'abc.jpg',
          file_path: '/tmp/abc.jpg',
          mime_type: 'image/jpeg',
          file_size: 1024,
          thumbnail_path: '/tmp/thumb.jpg',
          created_at: '2025-01-01T00:00:00Z',
          updated_at: '2025-01-01T00:00:00Z',
        },
        {
          id: 'doc-1',
          vehicle_id: 'veh-1',
          original_filename: 'doc.pdf',
          stored_filename: 'def.pdf',
          file_path: '/tmp/def.pdf',
          mime_type: 'application/pdf',
          file_size: 2048,
          thumbnail_path: null,
          created_at: '2025-01-01T00:00:00Z',
          updated_at: '2025-01-01T00:00:00Z',
        },
      ],
      total: 2,
      page: 1,
      page_size: 100,
    } as any);

    render(<FileGallery vehicleId="veh-1" />);
    await waitFor(() => {
      expect(screen.getByTestId('gallery-item-img-1')).toBeDefined();
    });
    expect(screen.queryByTestId('gallery-item-doc-1')).toBeNull();
  });
});

describe('FilePreview', () => {
  it('renders image preview for image files', () => {
    const file = {
      id: 'file-1',
      vehicle_id: 'veh-1',
      original_filename: 'photo.jpg',
      stored_filename: 'abc.jpg',
      file_path: '/tmp/abc.jpg',
      mime_type: 'image/jpeg',
      file_size: 102400,
      thumbnail_path: '/tmp/thumb.jpg',
      created_at: '2025-01-01T00:00:00Z',
      updated_at: '2025-01-01T00:00:00Z',
    };

    render(<FilePreview file={file} open={true} onClose={() => {}} />);
    expect(screen.getByTestId('file-preview')).toBeDefined();
    expect(screen.getByTestId('preview-image')).toBeDefined();
  });

  it('renders placeholder for non-image files', () => {
    const file = {
      id: 'file-2',
      vehicle_id: 'veh-1',
      original_filename: 'doc.pdf',
      stored_filename: 'def.pdf',
      file_path: '/tmp/def.pdf',
      mime_type: 'application/pdf',
      file_size: 204800,
      thumbnail_path: null,
      created_at: '2025-01-01T00:00:00Z',
      updated_at: '2025-01-01T00:00:00Z',
    };

    render(<FilePreview file={file} open={true} onClose={() => {}} />);
    expect(screen.getByTestId('file-preview')).toBeDefined();
    expect(screen.queryByTestId('preview-image')).toBeNull();
    expect(screen.getByText(/Preview not available/i)).toBeDefined();
  });

  it('shows file metadata', () => {
    const file = {
      id: 'file-1',
      vehicle_id: 'veh-1',
      original_filename: 'photo.jpg',
      stored_filename: 'abc.jpg',
      file_path: '/tmp/abc.jpg',
      mime_type: 'image/jpeg',
      file_size: 102400,
      thumbnail_path: '/tmp/thumb.jpg',
      created_at: '2025-01-01T00:00:00Z',
      updated_at: '2025-01-01T00:00:00Z',
    };

    render(<FilePreview file={file} open={true} onClose={() => {}} />);
    expect(screen.getByText('image/jpeg')).toBeDefined();
    expect(screen.getByText('100.0 KB')).toBeDefined();
  });

  it('shows download button', () => {
    const file = {
      id: 'file-1',
      vehicle_id: 'veh-1',
      original_filename: 'photo.jpg',
      stored_filename: 'abc.jpg',
      file_path: '/tmp/abc.jpg',
      mime_type: 'image/jpeg',
      file_size: 1024,
      thumbnail_path: '/tmp/thumb.jpg',
      created_at: '2025-01-01T00:00:00Z',
      updated_at: '2025-01-01T00:00:00Z',
    };

    render(<FilePreview file={file} open={true} onClose={() => {}} />);
    expect(screen.getByTestId('preview-download-btn')).toBeDefined();
  });

  it('returns null when file is null', () => {
    const { container } = render(<FilePreview file={null} open={false} onClose={() => {}} />);
    expect(container.firstChild).toBeNull();
  });
});

describe('Utility functions', () => {
  it('formatFileSize formats bytes correctly', () => {
    expect(formatFileSize(0)).toBe('0 B');
    expect(formatFileSize(512)).toBe('512 B');
    expect(formatFileSize(1024)).toBe('1.0 KB');
    expect(formatFileSize(1024 * 1024)).toBe('1.0 MB');
    expect(formatFileSize(1024 * 1024 * 5)).toBe('5.0 MB');
  });

  it('isImageMime detects image MIME types', () => {
    expect(isImageMime('image/jpeg')).toBe(true);
    expect(isImageMime('image/png')).toBe(true);
    expect(isImageMime('image/webp')).toBe(true);
    expect(isImageMime('application/pdf')).toBe(false);
    expect(isImageMime('text/plain')).toBe(false);
  });
});
