// src/utils/imageProcessor.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import type { Logger } from 'pino';

// --- Hoisted Mocks ---
const mocks = vi.hoisted(() => {
  // Create a chainable mock for the sharp library
  const toFile = vi.fn().mockResolvedValue({ info: 'mocked' });
  const webp = vi.fn(() => ({ toFile }));
  const resize = vi.fn(() => ({ webp }));
  const sharpInstance = { resize };

  // Mock the sharp function and attach static properties required by the implementation
  const sharp = vi.fn(() => sharpInstance);
  Object.assign(sharp, { fit: { cover: 'cover' } });

  return {
    sharp: sharp,
    resize,
    webp,
    toFile,
    mkdir: vi.fn().mockResolvedValue(undefined),
  };
});

// --- Mock Modules ---
vi.mock('sharp', () => ({
  __esModule: true,
  default: mocks.sharp,
}));

vi.mock('node:fs/promises', () => ({
  default: {
    mkdir: mocks.mkdir,
  },
}));

// Helper to create a type-safe mock logger
const createMockLogger = (): Logger => ({
  info: vi.fn(),
  error: vi.fn(),
  warn: vi.fn(),
  debug: vi.fn(),
  fatal: vi.fn(),
  trace: vi.fn(),
  silent: vi.fn(),
  child: vi.fn(() => createMockLogger()), // Return a new mock for child loggers
  level: 'info',
} as unknown as Logger);

const logger = createMockLogger();

vi.mock('../services/logger.server', () => ({ logger }));

// --- Import the function to be tested ---
import { generateFlyerIcon } from './imageProcessor';

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

  it('should process the image and return the correct icon filename', async () => {
    const sourceImagePath = '/path/to/flyer image (1).jpg';
    const iconsDirectory = '/path/to/icons';

    const result = await generateFlyerIcon(sourceImagePath, iconsDirectory, logger);

    // Check that the icons directory was created
    expect(mocks.mkdir).toHaveBeenCalledWith(iconsDirectory, { recursive: true });

    // Check that sharp was called with the correct source
    expect(mocks.sharp).toHaveBeenCalledWith(sourceImagePath);

    // Check the processing chain
    expect(mocks.resize).toHaveBeenCalledWith(64, 64, { fit: 'cover' });
    expect(mocks.webp).toHaveBeenCalledWith({ quality: 80 });
    expect(mocks.toFile).toHaveBeenCalledWith('/path/to/icons/icon-flyer-image-1.webp');

    // Check the returned filename
    expect(result).toBe('icon-flyer-image-1.webp');
  });

  it('should throw an error if sharp fails to process the image', async () => {
    const sharpError = new Error('Invalid image buffer');
    mocks.toFile.mockRejectedValue(sharpError);

    await expect(generateFlyerIcon('/path/to/bad-image.jpg', '/path/to/icons', logger)).rejects.toThrow('Icon generation failed.');
    expect(logger.error).toHaveBeenCalledWith(expect.any(Object), 'Failed to generate flyer icon:');
  });
});