// src/components/FlyerCorrectionTool.test.tsx
import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach, type Mocked } from 'vitest';
import { FlyerCorrectionTool } from './FlyerCorrectionTool';
import * as aiApiClient from '../services/aiApiClient';
import { notifySuccess } from '../services/notificationService';

// Mock dependencies
vi.mock('../services/aiApiClient');
vi.mock('../services/notificationService');
vi.mock('../services/logger', () => ({
  logger: {
    error: vi.fn(),
  },
}));

const mockedAiApiClient = aiApiClient as Mocked<typeof aiApiClient>;
const mockedNotifySuccess = notifySuccess as Mocked<typeof notifySuccess>;

const defaultProps = {
  isOpen: true,
  onClose: vi.fn(),
  imageUrl: 'http://example.com/flyer.jpg',
  onDataExtracted: vi.fn(),
};

describe('FlyerCorrectionTool', () => {
  beforeEach(() => {
    vi.clearAllMocks();

    // Mock global fetch for fetching the image blob inside the component
    global.fetch = vi.fn(() =>
      Promise.resolve(new Response(new Blob(['dummy-image-content'], { type: 'image/jpeg' })))
    ) as Mocked<typeof fetch>;

    // Mock canvas methods for jsdom environment
    window.HTMLCanvasElement.prototype.getContext = vi.fn((contextId: string) => {
      if (contextId === '2d') {
        return {
          clearRect: vi.fn(),
          strokeRect: vi.fn(),
          setLineDash: vi.fn(),
          strokeStyle: '',
          lineWidth: 0,
        } as unknown as CanvasRenderingContext2D;
      }
      return null;
    }) as typeof window.HTMLCanvasElement.prototype.getContext;
  });

  it('should not render when isOpen is false', () => {
    const { container } = render(<FlyerCorrectionTool {...defaultProps} isOpen={false} />);
    expect(container.firstChild).toBeNull();
  });

  it('should render correctly when isOpen is true', () => {
    render(<FlyerCorrectionTool {...defaultProps} />);
    expect(screen.getByRole('heading', { name: /flyer correction tool/i })).toBeInTheDocument();
    expect(screen.getByAltText('Flyer for correction')).toBeInTheDocument();
    expect(screen.getByRole('button', { name: /extract store name/i })).toBeInTheDocument();
    expect(screen.getByRole('button', { name: /extract sale dates/i })).toBeInTheDocument();
  });

  it('should call onClose when the close button is clicked', () => {
    render(<FlyerCorrectionTool {...defaultProps} />);
    // Use the specific aria-label defined in the component to find the close button
    const closeButton = screen.getByLabelText(/close correction tool/i);
    fireEvent.click(closeButton);
    expect(defaultProps.onClose).toHaveBeenCalledTimes(1);
  });

  it('should have disabled extraction buttons initially', () => {
    render(<FlyerCorrectionTool {...defaultProps} />);
    expect(screen.getByRole('button', { name: /extract store name/i })).toBeDisabled();
    expect(screen.getByRole('button', { name: /extract sale dates/i })).toBeDisabled();
  });

  it('should enable extraction buttons after a selection is made', () => {
    render(<FlyerCorrectionTool {...defaultProps} />);
    const canvas = screen.getByRole('dialog').querySelector('canvas')!;

    // Simulate drawing a rectangle
    fireEvent.mouseDown(canvas, { clientX: 10, clientY: 10 });
    fireEvent.mouseMove(canvas, { clientX: 100, clientY: 50 });
    fireEvent.mouseUp(canvas);

    expect(screen.getByRole('button', { name: /extract store name/i })).toBeEnabled();
    expect(screen.getByRole('button', { name: /extract sale dates/i })).toBeEnabled();
  });

  it('should call rescanImageArea with correct parameters and show success', async () => {
    // Mock the response with a slight delay. This ensures the "Processing..."
    // state has time to render before the mock resolves, making the test more stable.
    mockedAiApiClient.rescanImageArea.mockImplementation(async () => {
      await new Promise(r => setTimeout(r, 50)); // 50ms delay
      return new Response(JSON.stringify({ text: 'Super Store' }));
    });

    render(<FlyerCorrectionTool {...defaultProps} />);

    // Wait for the image fetch to complete to ensure 'imageFile' state is populated
    await waitFor(() => expect(global.fetch).toHaveBeenCalledWith(defaultProps.imageUrl));

    const canvas = screen.getByRole('dialog').querySelector('canvas')!;
    const image = screen.getByAltText('Flyer for correction');

    // Mock image dimensions for coordinate scaling
    Object.defineProperty(image, 'naturalWidth', { value: 1000, configurable: true });
    Object.defineProperty(image, 'naturalHeight', { value: 800, configurable: true });
    Object.defineProperty(image, 'clientWidth', { value: 500, configurable: true });
    Object.defineProperty(image, 'clientHeight', { value: 400, configurable: true });

    // Simulate drawing a rectangle
    fireEvent.mouseDown(canvas, { clientX: 10, clientY: 10 });
    fireEvent.mouseMove(canvas, { clientX: 60, clientY: 30 });
    fireEvent.mouseUp(canvas);

    // Click the extract button
    fireEvent.click(screen.getByRole('button', { name: /extract store name/i }));

    // Check for loading state - this should now pass because of the delay
    expect(await screen.findByText('Processing...')).toBeInTheDocument();

    await waitFor(() => {
      expect(mockedAiApiClient.rescanImageArea).toHaveBeenCalledTimes(1);
      // Check that coordinates were scaled correctly (e.g., 500 -> 1000 is a 2x scale)
      expect(mockedAiApiClient.rescanImageArea).toHaveBeenCalledWith(
        expect.any(File),
        // 10*2=20, 10*2=20, (60-10)*2=100, (30-10)*2=40
        { x: 20, y: 20, width: 100, height: 40 },
        'store_name'
      );
    });

    await waitFor(() => {
      expect(mockedNotifySuccess).toHaveBeenCalledWith('Extracted: Super Store');
      expect(defaultProps.onDataExtracted).toHaveBeenCalledWith('store_name', 'Super Store');
      expect(defaultProps.onClose).toHaveBeenCalledTimes(1);
    });
  });
});