import { readFileSync } from "node:fs";
import { join } from "node:path";
import { expect, test } from "@playwright/test";

// ─── Pipeline Advanced ────────────────────────────────────────────
// Multi-step pipeline chain tests with 3+ steps. Covers complex
// real-world workflows, duplicate steps, format changes mid-chain,
// and deep pipelines (5+ steps).

const FIXTURES = join(process.cwd(), "tests", "fixtures");
const FORMATS = join(FIXTURES, "formats");

let token: string;

test.beforeAll(async ({ request }) => {
  const res = await request.post("/api/auth/login", {
    data: { username: "admin", password: "admin" },
  });
  const body = await res.json();
  token = body.token;
});

function fixture(name: string): Buffer {
  return readFileSync(join(FIXTURES, name));
}

function formatFixture(name: string): Buffer {
  return readFileSync(join(FORMATS, name));
}

const PNG_200x150 = fixture("test-200x150.png");
const JPG_100x100 = fixture("test-100x100.jpg");
const HEIC_200x150 = fixture("test-200x150.heic");
const JPG_SAMPLE = formatFixture("sample.jpg");
const JPG_WITH_EXIF = fixture("test-with-exif.jpg");
const WEBP_50x50 = fixture("test-50x50.webp");

// ─── 3-Step: Resize -> Compress -> Convert (JPEG to WebP) ────────

test.describe("3-step: resize -> compress -> convert", () => {
  test("resize, compress, then convert JPEG to WebP", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "sample.jpg", mimeType: "image/jpeg", buffer: JPG_SAMPLE },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "resize", settings: { width: 640, fit: "contain" } },
            { toolId: "compress", settings: { quality: 60 } },
            { toolId: "convert", settings: { format: "webp" } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.downloadUrl).toContain(".webp");
    expect(body.processedSize).toBeGreaterThan(0);
    expect(body.processedSize).toBeLessThan(body.originalSize);
  });

  test("resize, compress, then convert PNG to AVIF", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "resize", settings: { width: 100, fit: "contain" } },
            { toolId: "compress", settings: { quality: 50 } },
            { toolId: "convert", settings: { format: "avif" } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.downloadUrl).toContain(".avif");
  });
});

// ─── 4-Step: Rotate -> Resize -> Sharpening -> Compress ──────────

test.describe("4-step: rotate -> resize -> sharpening -> compress", () => {
  test("full 4-step image preparation pipeline", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "sample.jpg", mimeType: "image/jpeg", buffer: JPG_SAMPLE },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "rotate", settings: { angle: 90 } },
            { toolId: "resize", settings: { width: 500, fit: "contain" } },
            { toolId: "sharpening", settings: { sigma: 1.5 } },
            { toolId: "compress", settings: { quality: 70 } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.processedSize).toBeGreaterThan(0);
  });

  test("rotate 180 -> resize -> sharpen -> compress on PNG", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "rotate", settings: { angle: 180 } },
            { toolId: "resize", settings: { width: 150, height: 100, fit: "fill" } },
            { toolId: "sharpening", settings: { sigma: 2.0 } },
            { toolId: "compress", settings: { quality: 80 } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
  });
});

// ─── 5-Step: Strip Metadata -> Resize -> Adjust Colors -> Compress -> Convert ─

test.describe("5-step: strip-metadata -> resize -> adjust-colors -> compress -> convert", () => {
  test("full 5-step processing pipeline on JPEG with EXIF", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "photo.jpg", mimeType: "image/jpeg", buffer: JPG_WITH_EXIF },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "strip-metadata", settings: {} },
            { toolId: "resize", settings: { width: 800, fit: "contain" } },
            { toolId: "adjust-colors", settings: { brightness: 10, contrast: 15, saturation: 5 } },
            { toolId: "compress", settings: { quality: 75 } },
            { toolId: "convert", settings: { format: "webp" } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.downloadUrl).toContain(".webp");
    expect(body.processedSize).toBeGreaterThan(0);
  });

  test("full 5-step pipeline on high-res sample image", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "sample.jpg", mimeType: "image/jpeg", buffer: JPG_SAMPLE },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "strip-metadata", settings: {} },
            { toolId: "resize", settings: { width: 1200, fit: "contain" } },
            { toolId: "adjust-colors", settings: { brightness: -5, contrast: 10 } },
            { toolId: "compress", settings: { quality: 65 } },
            { toolId: "convert", settings: { format: "avif" } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.downloadUrl).toContain(".avif");
    expect(body.processedSize).toBeLessThan(body.originalSize);
  });
});

// ─── Pipeline with Same Step Twice: Resize -> Resize ─────────────

test.describe("Pipeline with same step twice", () => {
  test("resize 200->100, then resize 100->50 (two sequential resizes)", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "resize", settings: { width: 100, fit: "contain" } },
            { toolId: "resize", settings: { width: 50, fit: "contain" } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.processedSize).toBeGreaterThan(0);
  });

  test("resize with different fits: cover then fill", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "sample.jpg", mimeType: "image/jpeg", buffer: JPG_SAMPLE },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "resize", settings: { width: 300, height: 300, fit: "cover" } },
            { toolId: "resize", settings: { width: 200, height: 150, fit: "fill" } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
  });

  test("double compress with decreasing quality", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "sample.jpg", mimeType: "image/jpeg", buffer: JPG_SAMPLE },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "compress", settings: { quality: 80 } },
            { toolId: "compress", settings: { quality: 30 } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.processedSize).toBeLessThan(body.originalSize);
  });

  test("double sharpen with different sigma values", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_100x100 },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "sharpening", settings: { sigma: 0.5 } },
            { toolId: "sharpening", settings: { sigma: 2.0 } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
  });
});

// ─── Pipeline with Format Change Mid-Chain ────────────────────────

test.describe("Pipeline with format change mid-chain", () => {
  test("convert JPEG to PNG, resize, then convert to WebP", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "sample.jpg", mimeType: "image/jpeg", buffer: JPG_SAMPLE },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "convert", settings: { format: "png" } },
            { toolId: "resize", settings: { width: 400, fit: "contain" } },
            { toolId: "convert", settings: { format: "webp" } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.downloadUrl).toContain(".webp");
  });

  test("convert PNG to JPEG, enhance, then convert to AVIF", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "convert", settings: { format: "jpg", quality: 90 } },
            { toolId: "image-enhancement", settings: { preset: "auto" } },
            { toolId: "convert", settings: { format: "avif" } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.downloadUrl).toContain(".avif");
  });

  test("convert to TIFF mid-chain then back to WebP", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_100x100 },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "convert", settings: { format: "tiff" } },
            { toolId: "resize", settings: { width: 80, fit: "contain" } },
            { toolId: "convert", settings: { format: "webp" } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.downloadUrl).toContain(".webp");
  });
});

// ─── HEIC Input Through Multi-Step Pipelines ──────────────────────

test.describe("HEIC input through multi-step pipelines", () => {
  test("HEIC: 3-step resize -> sharpen -> convert to PNG", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "test.heic", mimeType: "image/heic", buffer: HEIC_200x150 },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "resize", settings: { width: 100, fit: "contain" } },
            { toolId: "sharpening", settings: { sigma: 1.0 } },
            { toolId: "convert", settings: { format: "png" } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.downloadUrl).toContain(".png");
  });

  test("HEIC: 4-step adjust-colors -> resize -> border -> compress", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "test.heic", mimeType: "image/heic", buffer: HEIC_200x150 },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "adjust-colors", settings: { brightness: 15, contrast: 10 } },
            { toolId: "resize", settings: { width: 150, fit: "contain" } },
            { toolId: "border", settings: { size: 5, color: "#000000" } },
            { toolId: "compress", settings: { quality: 70 } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.processedSize).toBeGreaterThan(0);
  });
});

// ─── Deep Pipeline: 6+ Steps ──────────────────────────────────────

test.describe("Deep pipelines (6+ steps)", () => {
  test("7-step full processing pipeline", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "sample.jpg", mimeType: "image/jpeg", buffer: JPG_SAMPLE },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "strip-metadata", settings: {} },
            { toolId: "rotate", settings: { angle: 90 } },
            { toolId: "resize", settings: { width: 400, fit: "contain" } },
            { toolId: "adjust-colors", settings: { brightness: 5, contrast: 10, saturation: -5 } },
            { toolId: "sharpening", settings: { sigma: 1.0 } },
            {
              toolId: "watermark-text",
              settings: {
                text: "DEEP PIPELINE",
                fontSize: 14,
                color: "#808080",
                opacity: 20,
                position: "bottom-right",
              },
            },
            { toolId: "compress", settings: { quality: 70 } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.processedSize).toBeGreaterThan(0);
    expect(body.processedSize).toBeLessThan(body.originalSize);
  });

  test("6-step pipeline with format change at the end", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "photo.jpg", mimeType: "image/jpeg", buffer: JPG_WITH_EXIF },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "strip-metadata", settings: {} },
            { toolId: "resize", settings: { width: 600, fit: "contain" } },
            { toolId: "adjust-colors", settings: { grayscale: true } },
            { toolId: "sharpening", settings: { sigma: 1.5 } },
            { toolId: "border", settings: { size: 8, color: "#ffffff" } },
            { toolId: "convert", settings: { format: "webp" } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.downloadUrl).toContain(".webp");
  });
});

// ─── Workflow: E-commerce Product Pipeline ────────────────────────

test.describe("Workflow: e-commerce product pipeline", () => {
  test("crop -> resize -> enhance -> watermark -> compress -> convert", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "sample.jpg", mimeType: "image/jpeg", buffer: JPG_SAMPLE },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "crop", settings: { left: 50, top: 50, width: 400, height: 350 } },
            { toolId: "resize", settings: { width: 800, height: 800, fit: "contain" } },
            { toolId: "image-enhancement", settings: { preset: "vivid" } },
            {
              toolId: "watermark-text",
              settings: {
                text: "SAMPLE",
                fontSize: 20,
                color: "#cccccc",
                opacity: 25,
                position: "center",
              },
            },
            { toolId: "compress", settings: { quality: 85 } },
            { toolId: "convert", settings: { format: "webp" } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.downloadUrl).toContain(".webp");
    expect(body.processedSize).toBeGreaterThan(0);
  });
});

// ─── Workflow: Blog Post Image Pipeline ───────────────────────────

test.describe("Workflow: blog post image pipeline", () => {
  test("strip-metadata -> resize -> text-overlay -> optimize-for-web", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "photo.jpg", mimeType: "image/jpeg", buffer: JPG_WITH_EXIF },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "strip-metadata", settings: {} },
            { toolId: "resize", settings: { width: 1200, fit: "contain" } },
            {
              toolId: "text-overlay",
              settings: {
                text: "Blog Header Image",
                fontSize: 36,
                color: "#ffffff",
                position: "bottom",
                backgroundBox: true,
                backgroundColor: "#333333",
              },
            },
            { toolId: "optimize-for-web", settings: { maxWidth: 1200, quality: 80 } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.processedSize).toBeGreaterThan(0);
  });
});

// ─── Workflow: Archive Preparation Pipeline ───────────────────────

test.describe("Workflow: archive preparation pipeline", () => {
  test("strip-metadata -> adjust-colors -> resize -> convert to TIFF", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "sample.jpg", mimeType: "image/jpeg", buffer: JPG_SAMPLE },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "strip-metadata", settings: {} },
            { toolId: "adjust-colors", settings: { brightness: 0, contrast: 5 } },
            { toolId: "resize", settings: { width: 2000, fit: "contain" } },
            { toolId: "convert", settings: { format: "tiff" } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.downloadUrl).toContain(".tiff");
  });
});

// ─── Pipeline Step Metadata Validation ────────────────────────────

test.describe("Pipeline step metadata validation", () => {
  test("3-step pipeline returns step metadata if available", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "resize", settings: { width: 100, fit: "contain" } },
            { toolId: "sharpening", settings: { sigma: 1.0 } },
            { toolId: "compress", settings: { quality: 60 } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    if (body.steps) {
      expect(body.steps).toBeInstanceOf(Array);
      expect(body.steps.length).toBe(3);
    }
  });

  test("5-step pipeline returns step metadata if available", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "sample.jpg", mimeType: "image/jpeg", buffer: JPG_SAMPLE },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "strip-metadata", settings: {} },
            { toolId: "resize", settings: { width: 500, fit: "contain" } },
            { toolId: "adjust-colors", settings: { brightness: 10 } },
            { toolId: "sharpening", settings: { sigma: 0.8 } },
            { toolId: "compress", settings: { quality: 70 } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    if (body.steps) {
      expect(body.steps).toBeInstanceOf(Array);
      expect(body.steps.length).toBe(5);
    }
  });
});

// ─── WebP Input Through Multi-Step Pipelines ──────────────────────

test.describe("WebP input through multi-step pipelines", () => {
  test("WebP: 3-step resize -> adjust-colors -> convert to PNG", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "test.webp", mimeType: "image/webp", buffer: WEBP_50x50 },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "resize", settings: { width: 100, height: 100, fit: "fill" } },
            { toolId: "adjust-colors", settings: { brightness: 20, saturation: 15 } },
            { toolId: "convert", settings: { format: "png" } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.downloadUrl).toContain(".png");
  });
});

// ─── Pipeline with Enhancement + Border + Convert ─────────────────

test.describe("Enhancement + Border + Convert pipeline", () => {
  test("3-step enhance -> border -> convert to WebP", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_100x100 },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "image-enhancement", settings: { preset: "auto" } },
            { toolId: "border", settings: { size: 10, color: "#333333" } },
            { toolId: "convert", settings: { format: "webp" } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.downloadUrl).toContain(".webp");
  });
});

// ─── Pipeline with Multiple Color Operations ──────────────────────

test.describe("Pipeline with multiple color operations", () => {
  test("adjust-colors -> replace-color -> adjust-colors (grayscale)", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "adjust-colors", settings: { brightness: 20, contrast: 10 } },
            {
              toolId: "replace-color",
              settings: {
                targetColor: "#ffffff",
                replacementColor: "#f0f0e0",
                tolerance: 25,
              },
            },
            { toolId: "adjust-colors", settings: { grayscale: true } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
  });
});

// ─── Pipeline with Crop After Rotate ──────────────────────────────

test.describe("Pipeline with crop after rotate", () => {
  test("rotate 90 -> crop center -> resize -> compress", async ({ request }) => {
    const res = await request.post("/api/v1/pipeline/execute", {
      headers: { Authorization: `Bearer ${token}` },
      multipart: {
        file: { name: "sample.jpg", mimeType: "image/jpeg", buffer: JPG_SAMPLE },
        pipeline: JSON.stringify({
          steps: [
            { toolId: "rotate", settings: { angle: 90 } },
            { toolId: "crop", settings: { left: 20, top: 20, width: 200, height: 200 } },
            { toolId: "resize", settings: { width: 100, fit: "contain" } },
            { toolId: "compress", settings: { quality: 60 } },
          ],
        }),
      },
    });
    expect(res.ok()).toBe(true);
    const body = await res.json();
    expect(body.downloadUrl).toBeTruthy();
    expect(body.processedSize).toBeGreaterThan(0);
  });
});
