import { beforeEach, describe, expect, it, vi } from 'vitest';

vi.mock('crypto', () => ({
  randomUUID: vi.fn(() => 'test-uuid'),
}));

vi.mock('@vercel/blob', () => ({
  put: vi.fn(),
}));

vi.mock('../../../lib/permission-middleware.js', () => ({
  getUserFromRequest: vi.fn(),
}));

vi.mock('../../../lib/rate-limit.js', () => ({
  checkUploadRateLimit: vi.fn(),
}));

import { put } from '@vercel/blob';
import { getUserFromRequest } from '../../../lib/permission-middleware.js';
import { checkUploadRateLimit } from '../../../lib/rate-limit.js';
import handler from '../../../pages/api/scan/upload-image.js';

const TINY_JPEG =
  'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAALCAABAAEBAREA/8QAFAABAAAAAAAAAAAAAAAAAAAAAv/EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AL+AAf/Z';

function createRes() {
  const res = {
    statusCode: 200,
    headers: {},
    body: null,
    status(code) {
      res.statusCode = code;
      return res;
    },
    json(data) {
      res.body = data;
      return res;
    },
    setHeader(name, value) {
      res.headers[name] = value;
    },
  };
  return res;
}

describe('POST /api/scan/upload-image', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    getUserFromRequest.mockResolvedValue({ userId: 7, email: 'a@b.c', role: 'user' });
    checkUploadRateLimit.mockResolvedValue({ allowed: true, remaining: 9, reset: Date.now() + 60_000 });
    put.mockResolvedValue({ url: 'https://blob.example/scans/7/test-uuid.jpg' });
  });

  it('returns 405 for non-POST methods', async () => {
    const res = createRes();
    await handler({ method: 'GET' }, res);
    expect(res.statusCode).toBe(405);
  });

  it('returns 401 when unauthenticated', async () => {
    getUserFromRequest.mockResolvedValue(null);
    const res = createRes();
    await handler({ method: 'POST', body: { imageData: TINY_JPEG } }, res);
    expect(res.statusCode).toBe(401);
    expect(put).not.toHaveBeenCalled();
  });

  it('returns 429 when upload rate limit is exceeded', async () => {
    checkUploadRateLimit.mockResolvedValue({
      allowed: false,
      remaining: 0,
      reset: Date.now() + 30_000,
    });
    const res = createRes();
    await handler({ method: 'POST', body: { imageData: TINY_JPEG } }, res);
    expect(res.statusCode).toBe(429);
    expect(res.headers['Retry-After']).toBeDefined();
    expect(put).not.toHaveBeenCalled();
  });

  it('returns 400 for unsupported image MIME in data URL', async () => {
    const res = createRes();
    await handler(
      { method: 'POST', body: { imageData: 'data:image/gif;base64,AAAA' } },
      res
    );
    expect(res.statusCode).toBe(400);
    expect(res.body.error).toMatch(/JPEG, PNG, or WebP/);
    expect(put).not.toHaveBeenCalled();
  });

  it('uploads a valid JPEG data URL and returns the blob URL', async () => {
    const res = createRes();
    await handler({ method: 'POST', body: { imageData: TINY_JPEG } }, res);
    expect(res.statusCode).toBe(200);
    expect(res.body.url).toBe('https://blob.example/scans/7/test-uuid.jpg');
    expect(put).toHaveBeenCalledWith(
      'scans/7/test-uuid.jpg',
      expect.any(Buffer),
      expect.objectContaining({ access: 'public', contentType: 'image/jpeg' })
    );
  });
});
