import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { RetouchUpload } from '@/components/retouch/RetouchUpload';
import { BeforeAfterSlider } from '@/components/retouch/BeforeAfterSlider';
import { PriceComparison } from '@/components/retouch/PriceComparison';

// Mock fetch globally
const mockFetch = vi.fn();
global.fetch = mockFetch as unknown as typeof fetch;

// Mock localStorage
const localStorageMock = (() => {
  let store: Record<string, string> = {};
  return {
    getItem: (key: string) => store[key] || null,
    setItem: (key: string, value: string) => { store[key] = value; },
    removeItem: (key: string) => { delete store[key]; },
    clear: () => { store = {}; },
  };
})();
Object.defineProperty(window, 'localStorage', { value: localStorageMock });

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

const { apiFetch } = await import('@/lib/api');

beforeEach(() => {
  vi.clearAllMocks();
  localStorageMock.clear();
});

describe('RetouchUpload', () => {
  it('renders drag-and-drop zone', () => {
    render(<RetouchUpload />);
    expect(screen.getByTestId('retouch-dropzone')).toBeInTheDocument();
    expect(screen.getByText('Drag and drop vehicle image here')).toBeInTheDocument();
  });

  it('shows file input with image accept', () => {
    render(<RetouchUpload />);
    const input = screen.getByTestId('retouch-file-input');
    expect(input).toHaveAttribute('type', 'file');
    expect(input).toHaveAttribute('accept', 'image/*');
  });

  it('shows error for non-image file', async () => {
    render(<RetouchUpload />);
    const input = screen.getByTestId('retouch-file-input') as HTMLInputElement;

    const file = new File(['data'], 'doc.pdf', { type: 'application/pdf' });
    Object.defineProperty(input, 'files', { value: [file], writable: false });
    fireEvent.change(input);

    await waitFor(() => {
      expect(screen.getByTestId('retouch-upload-error')).toBeInTheDocument();
      expect(screen.getByText('Only image files are allowed')).toBeInTheDocument();
    });
  });

  it('uploads image file successfully', async () => {
    mockFetch.mockResolvedValueOnce({
      ok: true,
      json: async () => ({
        message: 'Retouch processing queued',
        retouch_id: 'new-retouch-id',
        status: 'pending',
      }),
    });

    let uploadedId = '';
    render(<RetouchUpload onUploadComplete={(id) => { uploadedId = id; }} />);
    const input = screen.getByTestId('retouch-file-input') as HTMLInputElement;

    const file = new File(['image-data'], 'truck.png', { type: 'image/png' });
    Object.defineProperty(input, 'files', { value: [file], writable: false });
    fireEvent.change(input);

    await waitFor(() => {
      expect(screen.getByTestId('retouch-upload-success')).toBeInTheDocument();
      expect(uploadedId).toBe('new-retouch-id');
    });

    expect(mockFetch).toHaveBeenCalledTimes(1);
    const call = mockFetch.mock.calls[0];
    expect(call[0]).toContain('/retouch/process');
    expect(call[1].method).toBe('POST');
  });

  it('shows error on upload failure', async () => {
    mockFetch.mockResolvedValueOnce({
      ok: false,
      json: async () => ({
        error: { code: 'INVALID_MIME_TYPE', message: 'Invalid MIME type' },
      }),
    });

    render(<RetouchUpload />);
    const input = screen.getByTestId('retouch-file-input') as HTMLInputElement;

    const file = new File(['image-data'], 'truck.png', { type: 'image/png' });
    Object.defineProperty(input, 'files', { value: [file], writable: false });
    fireEvent.change(input);

    await waitFor(() => {
      expect(screen.getByTestId('retouch-upload-error')).toBeInTheDocument();
      expect(screen.getByText('Invalid MIME type')).toBeInTheDocument();
    });
  });
});

describe('BeforeAfterSlider', () => {
  it('renders both images', () => {
    render(
      <BeforeAfterSlider
        beforeSrc="https://example.com/before.png"
        afterSrc="https://example.com/after.png"
      />
    );
    expect(screen.getByTestId('before-after-slider')).toBeInTheDocument();
    expect(screen.getByTestId('before-image')).toHaveAttribute('src', 'https://example.com/before.png');
    expect(screen.getByTestId('after-image')).toHaveAttribute('src', 'https://example.com/after.png');
  });

  it('renders labels', () => {
    render(
      <BeforeAfterSlider
        beforeSrc="https://example.com/before.png"
        afterSrc="https://example.com/after.png"
        beforeLabel="Original"
        afterLabel="Retuschiert"
      />
    );
    expect(screen.getByText('Original')).toBeInTheDocument();
    expect(screen.getByText('Retuschiert')).toBeInTheDocument();
  });

  it('renders slider handle', () => {
    render(
      <BeforeAfterSlider
        beforeSrc="https://example.com/before.png"
        afterSrc="https://example.com/after.png"
      />
    );
    expect(screen.getByTestId('slider-handle')).toBeInTheDocument();
  });
});

describe('PriceComparison', () => {
  it('renders vehicle ID input and compare button', () => {
    render(<PriceComparison />);
    expect(screen.getByTestId('price-compare-vehicle-input')).toBeInTheDocument();
    expect(screen.getByTestId('price-compare-button')).toBeInTheDocument();
  });

  it('shows error when comparing without vehicle ID', async () => {
    render(<PriceComparison />);
    const button = screen.getByTestId('price-compare-button');
    fireEvent.click(button);

    await waitFor(() => {
      expect(screen.getByTestId('price-compare-error')).toBeInTheDocument();
      expect(screen.getByText('Please enter a vehicle ID')).toBeInTheDocument();
    });
  });

  it('fetches and displays comparable listings', async () => {
    vi.mocked(apiFetch).mockResolvedValueOnce({
      vehicle_id: 'veh-123',
      comparable_listings: [
        {
          title: 'Mercedes Actros 2020',
          make: 'Mercedes',
          model: 'Actros',
          year: 2020,
          price: 42000,
          mileage_km: 110000,
          location: 'Berlin',
          url: 'https://mobile.de/listing/123',
          source: 'mobile.de',
        },
        {
          title: 'Mercedes Actros 2020',
          make: 'Mercedes',
          model: 'Actros',
          year: 2020,
          price: 48000,
          mileage_km: 95000,
          location: 'Hamburg',
          url: 'https://mobile.de/listing/456',
          source: 'mobile.de',
        },
      ],
      average_price: 45000,
      listing_count: 2,
    });

    render(<PriceComparison />);
    const input = screen.getByTestId('price-compare-vehicle-input') as HTMLInputElement;
    fireEvent.change(input, { target: { value: 'veh-123' } });
    fireEvent.click(screen.getByTestId('price-compare-button'));

    await waitFor(() => {
      expect(screen.getByTestId('price-compare-results')).toBeInTheDocument();
      expect(screen.getAllByText('Mercedes Actros 2020').length).toBe(2);
      expect(screen.getByTestId('price-compare-row-0')).toBeInTheDocument();
      expect(screen.getByTestId('price-compare-row-1')).toBeInTheDocument();
    });
  });

  it('shows empty state when no listings found', async () => {
    vi.mocked(apiFetch).mockResolvedValueOnce({
      vehicle_id: 'veh-456',
      comparable_listings: [],
      average_price: null,
      listing_count: 0,
    });

    render(<PriceComparison />);
    const input = screen.getByTestId('price-compare-vehicle-input') as HTMLInputElement;
    fireEvent.change(input, { target: { value: 'veh-456' } });
    fireEvent.click(screen.getByTestId('price-compare-button'));

    await waitFor(() => {
      expect(screen.getByTestId('price-compare-empty')).toBeInTheDocument();
      expect(screen.getByText('No comparable listings found.')).toBeInTheDocument();
    });
  });

  it('shows error on fetch failure', async () => {
    vi.mocked(apiFetch).mockRejectedValueOnce({
      error: { code: 'VEHICLE_NOT_FOUND', message: 'Vehicle not found' },
    });

    render(<PriceComparison />);
    const input = screen.getByTestId('price-compare-vehicle-input') as HTMLInputElement;
    fireEvent.change(input, { target: { value: 'bad-id' } });
    fireEvent.click(screen.getByTestId('price-compare-button'));

    await waitFor(() => {
      expect(screen.getByTestId('price-compare-error')).toBeInTheDocument();
      expect(screen.getByText('Vehicle not found')).toBeInTheDocument();
    });
  });
});
