/**
 * Integration tests for the SnapOtter API.
 *
 * These tests build a real Fastify server backed by an in-memory SQLite DB
 * and exercise every endpoint with real HTTP-like requests via `app.inject()`.
 * The goal is adversarial: we try to BREAK the system with invalid inputs,
 * path traversal, missing auth, type confusion, and boundary conditions.
 */

import { readFileSync } from "node:fs";
import { join } from "node:path";
import { apiToolPath } from "@snapotter/shared";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { fixtures, readFixture } from "../../fixtures/index.js";
import {
  buildTestApp,
  createMultipartPayload,
  loginAsAdmin,
  type TestApp,
} from "../test-server.js";

// ---------------------------------------------------------------------------
// Fixtures
// ---------------------------------------------------------------------------
const PNG_200x150 = readFixture(fixtures.image.base.png200);
const JPG_100x100 = readFixture(fixtures.image.base.jpg100);
const WEBP_50x50 = readFixture(fixtures.image.base.webp50);
const PNG_1x1 = readFixture(fixtures.image.edge.px1);
const EXIF_JPG = readFixture(fixtures.image.exifGps);

// ---------------------------------------------------------------------------
// Shared state
// ---------------------------------------------------------------------------
let testApp: TestApp;
let app: TestApp["app"];
let adminToken: string;

beforeAll(async () => {
  testApp = await buildTestApp();
  app = testApp.app;
  adminToken = await loginAsAdmin(app);
}, 30_000);

afterAll(async () => {
  await testApp.cleanup();
}, 10_000);

// adminToken is created once in beforeAll — reuse it throughout.
// Individual tests that need a fresh token should create their own.

// ═══════════════════════════════════════════════════════════════════════════
// AUTH ENDPOINTS
// ═══════════════════════════════════════════════════════════════════════════
describe("Auth endpoints", () => {
  // ── POST /api/auth/login ───────────────────────────────────────
  describe("POST /api/auth/login", () => {
    it("returns a token for valid credentials", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        payload: { username: "admin", password: "Adminpass1" },
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.token).toBeDefined();
      expect(typeof body.token).toBe("string");
      expect(body.user.username).toBe("admin");
      expect(body.user.role).toBe("admin");
      expect(body.expiresAt).toBeDefined();
    });

    it("returns 401 for wrong password", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        payload: { username: "admin", password: "wrongpassword" },
      });
      expect(res.statusCode).toBe(401);
      expect(JSON.parse(res.body).error).toMatch(/invalid/i);
    });

    it("returns 401 for non-existent user", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        payload: { username: "ghost", password: "whatever123" },
      });
      expect(res.statusCode).toBe(401);
    });

    it("returns 400 when username is missing", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        payload: { password: "Adminpass1" },
      });
      expect(res.statusCode).toBe(400);
    });

    it("returns 400 when password is missing", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        payload: { username: "admin" },
      });
      expect(res.statusCode).toBe(400);
    });

    it("returns 400 when body is empty", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        payload: {},
      });
      expect(res.statusCode).toBe(400);
    });

    it("returns 400 when body is null", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        headers: { "content-type": "application/json" },
        payload: "null",
      });
      // The route checks body?.username — null body should trigger 400
      expect(res.statusCode).toBe(400);
    });

    it("handles SQL injection attempt in username gracefully", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        payload: { username: "' OR 1=1 --", password: "anything" },
      });
      // Should NOT succeed — parameterized queries prevent injection
      expect(res.statusCode).toBe(401);
    });

    it("handles extremely long username", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        payload: { username: "a".repeat(10_000), password: "Adminpass1" },
      });
      // Zod rejects username > 255 chars before auth logic runs
      expect(res.statusCode).toBe(400);
    });

    it("handles unicode in credentials", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        payload: { username: "\u0000admin", password: "Adminpass1" },
      });
      expect(res.statusCode).toBe(401);
    });
  });

  // ── GET /api/auth/session ──────────────────────────────────────
  describe("GET /api/auth/session", () => {
    it("returns user info with a valid token", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/auth/session",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.user.username).toBe("admin");
      expect(body.user.role).toBe("admin");
      expect(body.expiresAt).toBeDefined();
    });

    it("returns 401 with no token", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/auth/session",
      });
      expect(res.statusCode).toBe(401);
    });

    it("returns 401 with an invalid token", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/auth/session",
        headers: { authorization: "Bearer totally-fake-token-12345" },
      });
      expect(res.statusCode).toBe(401);
    });

    it("returns 401 with a malformed Authorization header", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/auth/session",
        headers: { authorization: "NotBearer something" },
      });
      expect(res.statusCode).toBe(401);
    });

    it("returns 401 with empty Bearer token", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/auth/session",
        headers: { authorization: "Bearer " },
      });
      expect(res.statusCode).toBe(401);
    });
  });

  // ── POST /api/auth/logout ──────────────────────────────────────
  describe("POST /api/auth/logout", () => {
    it("invalidates the session token", async () => {
      // Get a fresh token to logout
      const loginRes = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        payload: { username: "admin", password: "Adminpass1" },
      });
      const freshToken = JSON.parse(loginRes.body).token;

      // Logout
      const logoutRes = await app.inject({
        method: "POST",
        url: "/api/auth/logout",
        headers: { authorization: `Bearer ${freshToken}` },
      });
      expect(logoutRes.statusCode).toBe(200);
      expect(JSON.parse(logoutRes.body).ok).toBe(true);

      // Verify the token no longer works
      const sessionRes = await app.inject({
        method: "GET",
        url: "/api/auth/session",
        headers: { authorization: `Bearer ${freshToken}` },
      });
      expect(sessionRes.statusCode).toBe(401);
    });

    it("succeeds even without a token (idempotent)", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/logout",
      });
      // Logout is on a public path prefix (/api/auth/) — should succeed
      expect(res.statusCode).toBe(200);
    });
  });

  // ── POST /api/auth/change-password ─────────────────────────────
  describe("POST /api/auth/change-password", () => {
    it("changes password with valid current password", async () => {
      // Create a disposable user first
      const regRes = await app.inject({
        method: "POST",
        url: "/api/auth/register",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { username: "changepw_user", password: "Original1234", role: "user" },
      });
      expect(regRes.statusCode).toBe(201);

      // Log in as the new user
      const loginRes = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        payload: { username: "changepw_user", password: "Original1234" },
      });
      const userToken = JSON.parse(loginRes.body).token;

      // Change password
      const changeRes = await app.inject({
        method: "POST",
        url: "/api/auth/change-password",
        headers: { authorization: `Bearer ${userToken}` },
        payload: { currentPassword: "Original1234", newPassword: "Newpassword99" },
      });
      expect(changeRes.statusCode).toBe(200);
      expect(JSON.parse(changeRes.body).ok).toBe(true);

      // Verify old password no longer works
      const oldLoginRes = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        payload: { username: "changepw_user", password: "Original1234" },
      });
      expect(oldLoginRes.statusCode).toBe(401);

      // Verify new password works
      const newLoginRes = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        payload: { username: "changepw_user", password: "Newpassword99" },
      });
      expect(newLoginRes.statusCode).toBe(200);
    });

    it("rejects wrong current password", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/change-password",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { currentPassword: "wrong_password_here", newPassword: "Newpass1234" },
      });
      expect(res.statusCode).toBe(401);
      expect(JSON.parse(res.body).code).toBe("INVALID_PASSWORD");
    });

    it("rejects new password shorter than 8 characters", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/change-password",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { currentPassword: "Adminpass1", newPassword: "short" },
      });
      expect(res.statusCode).toBe(400);
      expect(JSON.parse(res.body).code).toBe("VALIDATION_ERROR");
    });

    it("returns 400 when fields are missing", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/change-password",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { currentPassword: "Adminpass1" },
      });
      expect(res.statusCode).toBe(400);
    });

    it("returns 401 without authentication", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/change-password",
        payload: { currentPassword: "Adminpass1", newPassword: "Newpass1234" },
      });
      expect(res.statusCode).toBe(401);
    });
  });

  // ── POST /api/auth/register ────────────────────────────────────
  describe("POST /api/auth/register", () => {
    it("admin can create a new user", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/register",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { username: "newuser1", password: "Password1234", role: "user" },
      });
      expect(res.statusCode).toBe(201);
      const body = JSON.parse(res.body);
      expect(body.username).toBe("newuser1");
      expect(body.role).toBe("user");
      expect(body.id).toBeDefined();
    });

    it("returns 409 for duplicate username", async () => {
      // First creation
      await app.inject({
        method: "POST",
        url: "/api/auth/register",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { username: "duplicate_user", password: "Password1234" },
      });
      // Second attempt
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/register",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { username: "duplicate_user", password: "Differentpass1" },
      });
      expect(res.statusCode).toBe(409);
      expect(JSON.parse(res.body).code).toBe("CONFLICT");
    });

    it("rejects password shorter than 8 characters", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/register",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { username: "shortpw_user", password: "short" },
      });
      expect(res.statusCode).toBe(400);
    });

    it("returns 400 when username is missing", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/register",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { password: "Password1234" },
      });
      expect(res.statusCode).toBe(400);
    });

    it("non-admin cannot register users", async () => {
      // Create a regular user first
      await app.inject({
        method: "POST",
        url: "/api/auth/register",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { username: "regular_user", password: "Password1234", role: "user" },
      });
      // Log in as regular user
      const loginRes = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        payload: { username: "regular_user", password: "Password1234" },
      });
      const userToken = JSON.parse(loginRes.body).token;

      // Try to register — should fail with 403
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/register",
        headers: { authorization: `Bearer ${userToken}` },
        payload: { username: "sneaky_user", password: "Password1234" },
      });
      expect(res.statusCode).toBe(403);
    });

    it("defaults role to 'user' when invalid role provided", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/auth/register",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { username: "badrole_user", password: "Password1234", role: "superadmin" },
      });
      expect(res.statusCode).toBe(201);
      expect(JSON.parse(res.body).role).toBe("user");
    });
  });

  // ── DELETE /api/auth/users/:id ─────────────────────────────────
  describe("DELETE /api/auth/users/:id", () => {
    it("admin can't delete themselves", async () => {
      // Get the admin's session to find their user ID
      const sessionRes = await app.inject({
        method: "GET",
        url: "/api/auth/session",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      const adminId = JSON.parse(sessionRes.body).user.id;

      const res = await app.inject({
        method: "DELETE",
        url: `/api/auth/users/${adminId}`,
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(res.statusCode).toBe(400);
      expect(JSON.parse(res.body).code).toBe("SELF_DELETE");
    });

    it("admin can delete another user", async () => {
      // Create a user to delete
      const regRes = await app.inject({
        method: "POST",
        url: "/api/auth/register",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { username: "doomed_user", password: "Password1234" },
      });
      const userId = JSON.parse(regRes.body).id;

      const deleteRes = await app.inject({
        method: "DELETE",
        url: `/api/auth/users/${userId}`,
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(deleteRes.statusCode).toBe(200);
      expect(JSON.parse(deleteRes.body).ok).toBe(true);

      // Verify the user can no longer log in
      const loginRes = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        payload: { username: "doomed_user", password: "Password1234" },
      });
      expect(loginRes.statusCode).toBe(401);
    });

    it("returns 404 for non-existent user ID", async () => {
      const res = await app.inject({
        method: "DELETE",
        url: "/api/auth/users/00000000-0000-0000-0000-000000000000",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(res.statusCode).toBe(404);
    });

    it("non-admin cannot delete users", async () => {
      // Create + login as regular user
      await app.inject({
        method: "POST",
        url: "/api/auth/register",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { username: "nonadmin_deleter", password: "Password1234", role: "user" },
      });
      const loginRes = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        payload: { username: "nonadmin_deleter", password: "Password1234" },
      });
      const userToken = JSON.parse(loginRes.body).token;

      const res = await app.inject({
        method: "DELETE",
        url: "/api/auth/users/some-random-id",
        headers: { authorization: `Bearer ${userToken}` },
      });
      expect(res.statusCode).toBe(403);
    });
  });
});

// ═══════════════════════════════════════════════════════════════════════════
// FILE UPLOAD / DOWNLOAD
// ═══════════════════════════════════════════════════════════════════════════
describe("File upload/download", () => {
  // ── POST /api/v1/upload ────────────────────────────────────────
  describe("POST /api/v1/upload", () => {
    it("uploads a valid PNG and returns a jobId", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        {
          name: "file",
          filename: "test.png",
          contentType: "image/png",
          content: PNG_200x150,
        },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/upload",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.jobId).toBeDefined();
      expect(body.files).toHaveLength(1);
      expect(body.files[0].name).toBe("test.png");
      expect(body.files[0].format).toBe("png");
      expect(body.files[0].size).toBeGreaterThan(0);
    });

    it("uploads multiple files at once", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "a.png", contentType: "image/png", content: PNG_1x1 },
        { name: "file", filename: "b.jpg", contentType: "image/jpeg", content: JPG_100x100 },
        { name: "file", filename: "c.webp", contentType: "image/webp", content: WEBP_50x50 },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/upload",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      expect(JSON.parse(res.body).files).toHaveLength(3);
    });

    it("returns 400 for empty multipart (no files)", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        // Only a text field, no file
        { name: "metadata", content: "some text" },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/upload",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(400);
      expect(JSON.parse(res.body).error).toMatch(/no valid files/i);
    });

    // Upload is modality-agnostic in 2.0: it accepts any file and defers content
    // validation to per-tool processing (a disguised/invalid file fails when a
    // tool actually runs on it). Stored files are never executed.
    it("accepts a non-image file upload (content validated per-tool at process time)", async () => {
      const textContent = Buffer.from("This is not an image. Just plain text content.");
      const { body: payload, contentType } = createMultipartPayload([
        {
          name: "file",
          filename: "malicious.txt",
          contentType: "text/plain",
          content: textContent,
        },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/upload",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
    });

    it("accepts a file with image extension but non-image content (validated at process time)", async () => {
      const fakeImage = Buffer.from("#!/bin/bash\necho 'gotcha'");
      const { body: payload, contentType } = createMultipartPayload([
        {
          name: "file",
          filename: "evil.png",
          contentType: "image/png",
          content: fakeImage,
        },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/upload",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
    });

    it("sanitizes path traversal in filename", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        {
          name: "file",
          filename: "../../../etc/passwd.png",
          contentType: "image/png",
          content: PNG_1x1,
        },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/upload",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      // Should succeed but the filename should be sanitized
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.files[0].name).not.toContain("..");
      expect(body.files[0].name).not.toContain("/");
    });
  });

  // ── GET /api/v1/download/:jobId/:filename ──────────────────────
  describe("GET /api/v1/download/:jobId/:filename", () => {
    let uploadJobId: string;
    let uploadedFilename: string;

    beforeAll(async () => {
      const { body: payload, contentType } = createMultipartPayload([
        {
          name: "file",
          filename: "download-test.png",
          contentType: "image/png",
          content: PNG_200x150,
        },
      ]);
      const res = await app.inject({
        method: "POST",
        url: "/api/v1/upload",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      const body = JSON.parse(res.body);
      uploadJobId = body.jobId;
      uploadedFilename = body.files[0].name;
    });

    it("downloads an uploaded file successfully", async () => {
      const res = await app.inject({
        method: "GET",
        url: `/api/v1/download/${uploadJobId}/${uploadedFilename}`,
      });
      expect(res.statusCode).toBe(200);
      expect(res.headers["content-type"]).toContain("image/png");
      expect(res.headers["content-disposition"]).toContain("attachment");
      // Verify the downloaded bytes match what we uploaded
      expect(Buffer.from(res.rawPayload).length).toBe(PNG_200x150.length);
    });

    it("download is public (no auth required)", async () => {
      const res = await app.inject({
        method: "GET",
        url: `/api/v1/download/${uploadJobId}/${uploadedFilename}`,
        // No authorization header
      });
      expect(res.statusCode).toBe(200);
    });

    it("returns 404 for non-existent file", async () => {
      const res = await app.inject({
        method: "GET",
        url: `/api/v1/download/${uploadJobId}/nonexistent.png`,
      });
      expect(res.statusCode).toBe(404);
    });

    it("returns 404 for non-existent jobId", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/download/00000000-0000-0000-0000-000000000000/file.png",
      });
      expect(res.statusCode).toBe(404);
    });

    it("returns 400 for path traversal in jobId", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/download/..%2F..%2F..%2Fetc/passwd",
      });
      expect(res.statusCode).toBe(400);
    });

    it("returns 400 for path traversal in filename", async () => {
      const res = await app.inject({
        method: "GET",
        url: `/api/v1/download/${uploadJobId}/..%2F..%2F..%2Fetc%2Fpasswd`,
      });
      expect(res.statusCode).toBe(400);
    });

    it("returns 400 or 401 for backslash traversal in filename", async () => {
      const res = await app.inject({
        method: "GET",
        url: `/api/v1/download/${uploadJobId}/..\\..\\..\\etc\\passwd`,
      });
      // Backslash may corrupt the URL path so the public-path check fails (401)
      // or the path-traversal guard catches it (400). Either way, blocked.
      expect([400, 401]).toContain(res.statusCode);
    });

    it("returns 400 for null bytes in filename", async () => {
      const res = await app.inject({
        method: "GET",
        url: `/api/v1/download/${uploadJobId}/file.png%00.txt`,
      });
      expect(res.statusCode).toBe(400);
    });
  });
});

// ═══════════════════════════════════════════════════════════════════════════
// TOOL PROCESSING
// ═══════════════════════════════════════════════════════════════════════════
describe("Tool processing", () => {
  // ── Resize ─────────────────────────────────────────────────────
  describe("POST /api/v1/tools/image/resize", () => {
    it("resizes an image successfully", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "resize-me.png", contentType: "image/png", content: PNG_200x150 },
        { name: "settings", content: JSON.stringify({ width: 100, height: 75 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.jobId).toBeDefined();
      expect(body.downloadUrl).toBeDefined();
      expect(body.originalSize).toBeGreaterThan(0);
      expect(body.processedSize).toBeGreaterThan(0);
    });

    it("resizes by percentage", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "pct.png", contentType: "image/png", content: PNG_200x150 },
        { name: "settings", content: JSON.stringify({ percentage: 50 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
    });

    it("returns 400 for invalid settings (negative width)", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "bad.png", contentType: "image/png", content: PNG_1x1 },
        { name: "settings", content: JSON.stringify({ width: -100 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(400);
    });

    it("returns 400 when no file is provided", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "settings", content: JSON.stringify({ width: 100 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(400);
      expect(JSON.parse(res.body).error).toMatch(/no file/i);
    });

    it("returns 400 for malformed settings JSON", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "img.png", contentType: "image/png", content: PNG_1x1 },
        { name: "settings", content: "{not valid json!!!" },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(400);
      expect(JSON.parse(res.body).error).toMatch(/json/i);
    });

    it("returns 400 with empty settings (no dimensions given)", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "defaults.png", contentType: "image/png", content: PNG_200x150 },
        { name: "settings", content: "{}" },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(400);
    });

    it("download URL from resize result is accessible", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "chain.png", contentType: "image/png", content: PNG_200x150 },
        { name: "settings", content: JSON.stringify({ width: 50 }) },
      ]);

      const resizeRes = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      const { downloadUrl } = JSON.parse(resizeRes.body);

      const downloadRes = await app.inject({
        method: "GET",
        url: downloadUrl,
      });
      expect(downloadRes.statusCode).toBe(200);
    });
  });

  // ── Crop ───────────────────────────────────────────────────────
  describe("POST /api/v1/tools/image/crop", () => {
    it("crops an image successfully", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "crop.png", contentType: "image/png", content: PNG_200x150 },
        { name: "settings", content: JSON.stringify({ left: 10, top: 10, width: 50, height: 50 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/crop",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.downloadUrl).toBeDefined();
    });

    it("returns 400 when required crop fields are missing", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "crop.png", contentType: "image/png", content: PNG_200x150 },
        { name: "settings", content: JSON.stringify({ left: 10 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/crop",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(400);
    });

    it("returns 422 when crop exceeds image bounds", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "crop.png", contentType: "image/png", content: PNG_200x150 },
        {
          name: "settings",
          content: JSON.stringify({ left: 0, top: 0, width: 9999, height: 9999 }),
        },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/crop",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      // Sharp should throw when crop extends beyond image — wrapped as 422
      expect(res.statusCode).toBe(422);
    });
  });

  // ── Convert ────────────────────────────────────────────────────
  describe("POST /api/v1/tools/image/convert", () => {
    it("converts PNG to JPEG", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "convert.png", contentType: "image/png", content: PNG_200x150 },
        { name: "settings", content: JSON.stringify({ format: "jpg" }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/convert",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.downloadUrl).toContain(".jpg");
    });

    it("converts PNG to WebP", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "convert.png", contentType: "image/png", content: PNG_200x150 },
        { name: "settings", content: JSON.stringify({ format: "webp", quality: 80 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/convert",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
    });

    it("rejects unsupported format", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "convert.png", contentType: "image/png", content: PNG_1x1 },
        { name: "settings", content: JSON.stringify({ format: "xyz" }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/convert",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(400);
    });

    it("rejects quality outside valid range", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "convert.png", contentType: "image/png", content: PNG_1x1 },
        { name: "settings", content: JSON.stringify({ format: "jpg", quality: 999 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/convert",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(400);
    });
  });

  // ── Rotate ─────────────────────────────────────────────────────
  describe("POST /api/v1/tools/image/rotate", () => {
    it("rotates 90 degrees", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "rotate.png", contentType: "image/png", content: PNG_200x150 },
        { name: "settings", content: JSON.stringify({ angle: 90 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/rotate",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
    });

    it("flips horizontally", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "flip.png", contentType: "image/png", content: PNG_200x150 },
        { name: "settings", content: JSON.stringify({ horizontal: true }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/rotate",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
    });

    it("works with default settings (no rotation)", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "norotate.png", contentType: "image/png", content: PNG_1x1 },
        { name: "settings", content: "{}" },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/rotate",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
    });
  });

  // ── Nonexistent tool ───────────────────────────────────────────
  describe("POST /api/v1/tools/nonexistent", () => {
    it("returns 404 for a tool that does not exist", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "img.png", contentType: "image/png", content: PNG_1x1 },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/nonexistent",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(404);
    });
  });

  // ── Different input formats ────────────────────────────────────
  describe("Input format handling", () => {
    it("processes JPEG input", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "test.jpg", contentType: "image/jpeg", content: JPG_100x100 },
        { name: "settings", content: JSON.stringify({ width: 50 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
    });

    it("processes WebP input", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "test.webp", contentType: "image/webp", content: WEBP_50x50 },
        { name: "settings", content: JSON.stringify({ width: 25 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
    });
  });
});

// ═══════════════════════════════════════════════════════════════════════════
// HEALTH & CONFIG
// ═══════════════════════════════════════════════════════════════════════════
describe("Health & Config", () => {
  describe("GET /api/v1/health", () => {
    it("returns only status and version without auth", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/health",
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.status).toBe("healthy");
      expect(body.version).toBeDefined();
      expect(body.uptime).toBeUndefined();
      expect(body.database).toBeUndefined();
      expect(body.storage).toBeUndefined();
      expect(body.queue).toBeUndefined();
    });

    it("also works with auth", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/health",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(res.statusCode).toBe(200);
    });
  });

  describe("GET /api/v1/admin/health", () => {
    it("returns full diagnostics for admin", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/admin/health",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.status).toBe("healthy");
      expect(body.version).toBeDefined();
      expect(body.uptime).toBeDefined();
      expect(body.database).toBe("ok");
      expect(body.storage).toBeDefined();
      expect(body.queue).toBeDefined();
    });

    it("rejects unauthenticated requests", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/admin/health",
      });
      expect(res.statusCode).toBe(401);
    });

    it("returns 403 for authenticated non-admin user", async () => {
      // Create a regular user and log in as them
      await app.inject({
        method: "POST",
        url: "/api/auth/register",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { username: "health_nonadmin", password: "Password1234", role: "user" },
      });
      const loginRes = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        payload: { username: "health_nonadmin", password: "Password1234" },
      });
      const userToken = JSON.parse(loginRes.body).token;

      const res = await app.inject({
        method: "GET",
        url: "/api/v1/admin/health",
        headers: { authorization: `Bearer ${userToken}` },
      });
      expect(res.statusCode).toBe(403);
    });
  });

  describe("GET /api/v1/config/auth", () => {
    it("returns authEnabled flag without auth", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/config/auth",
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(typeof body.authEnabled).toBe("boolean");
      expect(body.authEnabled).toBe(true); // We set AUTH_ENABLED=true in test-server
    });
  });
});

// ═══════════════════════════════════════════════════════════════════════════
// AUTH MIDDLEWARE
// ═══════════════════════════════════════════════════════════════════════════
describe("Auth middleware", () => {
  it("protected route without token returns 401", async () => {
    const res = await app.inject({
      method: "GET",
      url: "/api/v1/settings",
    });
    expect(res.statusCode).toBe(401);
  });

  it("protected route with valid token succeeds", async () => {
    const res = await app.inject({
      method: "GET",
      url: "/api/v1/settings",
      headers: { authorization: `Bearer ${adminToken}` },
    });
    expect(res.statusCode).toBe(200);
  });

  it("public route /api/v1/health works without token", async () => {
    const res = await app.inject({ method: "GET", url: "/api/v1/health" });
    expect(res.statusCode).toBe(200);
  });

  it("public route /api/v1/config/auth works without token", async () => {
    const res = await app.inject({ method: "GET", url: "/api/v1/config/auth" });
    expect(res.statusCode).toBe(200);
  });

  it("upload route requires auth", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "img.png", contentType: "image/png", content: PNG_1x1 },
    ]);
    const res = await app.inject({
      method: "POST",
      url: "/api/v1/upload",
      headers: { "content-type": contentType },
      payload,
    });
    expect(res.statusCode).toBe(401);
  });

  it("tool routes require auth", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "img.png", contentType: "image/png", content: PNG_1x1 },
      { name: "settings", content: "{}" },
    ]);
    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/resize",
      headers: { "content-type": contentType },
      payload,
    });
    expect(res.statusCode).toBe(401);
  });

  it("settings routes require auth", async () => {
    const res = await app.inject({
      method: "PUT",
      url: "/api/v1/settings",
      payload: { theme: "dark" },
    });
    expect(res.statusCode).toBe(401);
  });

  it("expired token is rejected", async () => {
    // We can't easily create an expired session through the API, but we can
    // check that a completely fake token is rejected on protected routes.
    const res = await app.inject({
      method: "GET",
      url: "/api/v1/settings",
      headers: { authorization: "Bearer expired-fake-token-xyz" },
    });
    expect(res.statusCode).toBe(401);
  });
});

// ═══════════════════════════════════════════════════════════════════════════
// SETTINGS
// ═══════════════════════════════════════════════════════════════════════════
describe("Settings", () => {
  describe("GET /api/v1/settings", () => {
    it("returns settings object (requires auth)", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/settings",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.settings).toBeDefined();
      expect(typeof body.settings).toBe("object");
    });

    it("returns 401 without auth", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/settings",
      });
      expect(res.statusCode).toBe(401);
    });
  });

  describe("PUT /api/v1/settings", () => {
    it("admin can save settings", async () => {
      const res = await app.inject({
        method: "PUT",
        url: "/api/v1/settings",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { theme: "dark", locale: "en" },
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.ok).toBe(true);
      expect(body.updatedCount).toBe(2);
    });

    it("saved settings are retrievable", async () => {
      // Save
      await app.inject({
        method: "PUT",
        url: "/api/v1/settings",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { testKey: "testValue" },
      });

      // Retrieve
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/settings/testKey",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.key).toBe("testKey");
      expect(body.value).toBe("testValue");
    });

    it("non-admin cannot save settings", async () => {
      // Create a regular user
      await app.inject({
        method: "POST",
        url: "/api/auth/register",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { username: "settings_user", password: "Password1234", role: "user" },
      });
      const loginRes = await app.inject({
        method: "POST",
        url: "/api/auth/login",
        payload: { username: "settings_user", password: "Password1234" },
      });
      const userToken = JSON.parse(loginRes.body).token;

      const res = await app.inject({
        method: "PUT",
        url: "/api/v1/settings",
        headers: { authorization: `Bearer ${userToken}` },
        payload: { theme: "hacked" },
      });
      expect(res.statusCode).toBe(403);
    });

    it("returns 400 for non-object body", async () => {
      const res = await app.inject({
        method: "PUT",
        url: "/api/v1/settings",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": "application/json",
        },
        payload: JSON.stringify([1, 2, 3]),
      });
      expect(res.statusCode).toBe(400);
    });

    it("settings upsert works (update existing key)", async () => {
      // Set initial value
      await app.inject({
        method: "PUT",
        url: "/api/v1/settings",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { upsertKey: "original" },
      });

      // Update
      await app.inject({
        method: "PUT",
        url: "/api/v1/settings",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { upsertKey: "updated" },
      });

      // Verify
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/settings/upsertKey",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(JSON.parse(res.body).value).toBe("updated");
    });

    it("rejects HTML tags in setting values", async () => {
      const res = await app.inject({
        method: "PUT",
        url: "/api/v1/settings",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { test_setting: "<script>alert('xss')</script>" },
      });
      expect(res.statusCode).toBe(400);
      const body = JSON.parse(res.body);
      expect(body.code).toBe("VALIDATION_ERROR");
    });

    it("rejects HTML tags in setting keys", async () => {
      const res = await app.inject({
        method: "PUT",
        url: "/api/v1/settings",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { "<img src=x onerror=alert(1)>": "test" },
      });
      expect(res.statusCode).toBe(400);
      const body = JSON.parse(res.body);
      expect(body.code).toBe("VALIDATION_ERROR");
    });

    it("does not partially write entries when a later entry contains HTML tags", async () => {
      const cleanKey = `atomicity_test_clean_${Date.now()}`;
      const res = await app.inject({
        method: "PUT",
        url: "/api/v1/settings",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: {
          [cleanKey]: "safe_value",
          "<script>xss</script>": "evil",
        },
      });
      expect(res.statusCode).toBe(400);
      const body = JSON.parse(res.body);
      expect(body.code).toBe("VALIDATION_ERROR");

      // The clean entry must NOT have been written
      const getRes = await app.inject({
        method: "GET",
        url: `/api/v1/settings/${cleanKey}`,
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(getRes.statusCode).toBe(404);
    });

    it("allows normal setting values without HTML", async () => {
      const res = await app.inject({
        method: "PUT",
        url: "/api/v1/settings",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { test_setting: "My App (v2.0) - Production" },
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.ok).toBe(true);
    });
  });

  describe("GET /api/v1/settings/:key", () => {
    it("returns 404 for non-existent key", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/settings/nonexistent_key_xyz",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(res.statusCode).toBe(404);
    });
  });
});

// ═══════════════════════════════════════════════════════════════════════════
// API KEY MANAGEMENT
// ═══════════════════════════════════════════════════════════════════════════
describe("API Keys", () => {
  describe("POST /api/v1/api-keys", () => {
    it("generates a new API key", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/v1/api-keys",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: "Test Key" },
      });
      expect(res.statusCode).toBe(201);
      const body = JSON.parse(res.body);
      expect(body.key).toBeDefined();
      expect(body.key).toMatch(/^si_/);
      expect(body.name).toBe("Test Key");
      expect(body.id).toBeDefined();
    });

    it("uses default name when none provided", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/v1/api-keys",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: {},
      });
      expect(res.statusCode).toBe(201);
      expect(JSON.parse(res.body).name).toBe("Default API Key");
    });

    it("rejects name longer than 100 chars", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/v1/api-keys",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: "x".repeat(101) },
      });
      expect(res.statusCode).toBe(400);
    });

    it("requires auth", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/v1/api-keys",
        payload: { name: "Sneaky Key" },
      });
      expect(res.statusCode).toBe(401);
    });
  });

  describe("GET /api/v1/api-keys", () => {
    it("lists user's API keys (without exposing raw keys)", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/api-keys",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.apiKeys).toBeDefined();
      expect(Array.isArray(body.apiKeys)).toBe(true);
      // Verify that no raw key is returned in list
      for (const key of body.apiKeys) {
        expect(key.key).toBeUndefined();
        expect(key.keyHash).toBeUndefined();
        expect(key.id).toBeDefined();
        expect(key.name).toBeDefined();
      }
    });
  });

  describe("DELETE /api/v1/api-keys/:id", () => {
    it("deletes own API key", async () => {
      // Create a key
      const createRes = await app.inject({
        method: "POST",
        url: "/api/v1/api-keys",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: "To Delete" },
      });
      const keyId = JSON.parse(createRes.body).id;

      // Delete it
      const deleteRes = await app.inject({
        method: "DELETE",
        url: `/api/v1/api-keys/${keyId}`,
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(deleteRes.statusCode).toBe(200);
      expect(JSON.parse(deleteRes.body).ok).toBe(true);
    });

    it("returns 404 for non-existent key", async () => {
      const res = await app.inject({
        method: "DELETE",
        url: "/api/v1/api-keys/00000000-0000-0000-0000-000000000000",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(res.statusCode).toBe(404);
    });
  });
});

// ═══════════════════════════════════════════════════════════════════════════
// STITCH
// ═══════════════════════════════════════════════════════════════════════════
describe("POST /api/v1/tools/image/stitch", () => {
  it("stitches two images horizontally with default settings", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "a.png", contentType: "image/png", content: PNG_200x150 },
      { name: "file", filename: "b.jpg", contentType: "image/jpeg", content: JPG_100x100 },
      { name: "settings", content: JSON.stringify({}) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/stitch",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      body: payload,
    });

    expect(res.statusCode).toBe(200);
    const body = JSON.parse(res.body);
    expect(body.jobId).toBeDefined();
    expect(body.downloadUrl).toMatch(/\/api\/v1\/download\//);
    expect(body.processedSize).toBeGreaterThan(0);
  });

  it("stitches vertically", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "a.png", contentType: "image/png", content: PNG_200x150 },
      { name: "file", filename: "b.jpg", contentType: "image/jpeg", content: JPG_100x100 },
      { name: "settings", content: JSON.stringify({ direction: "vertical" }) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/stitch",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      body: payload,
    });

    expect(res.statusCode).toBe(200);
  });

  it("outputs jpeg when requested", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "a.png", contentType: "image/png", content: PNG_200x150 },
      { name: "file", filename: "b.jpg", contentType: "image/jpeg", content: JPG_100x100 },
      { name: "settings", content: JSON.stringify({ format: "jpeg" }) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/stitch",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      body: payload,
    });

    expect(res.statusCode).toBe(200);
    const body = JSON.parse(res.body);
    expect(body.downloadUrl).toMatch(/stitch\.jpeg$/);
  });

  it("returns 400 with only one image", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "a.png", contentType: "image/png", content: PNG_200x150 },
      { name: "settings", content: JSON.stringify({}) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/stitch",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      body: payload,
    });

    expect(res.statusCode).toBe(400);
    const body = JSON.parse(res.body);
    expect(body.error).toMatch(/at least 2/i);
  });

  it("returns 400 with no images", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "settings", content: JSON.stringify({}) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/stitch",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      body: payload,
    });

    expect(res.statusCode).toBe(400);
  });

  it("accepts gap and backgroundColor settings", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "a.png", contentType: "image/png", content: PNG_200x150 },
      { name: "file", filename: "b.png", contentType: "image/png", content: PNG_200x150 },
      { name: "settings", content: JSON.stringify({ gap: 10, backgroundColor: "#FF0000" }) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/stitch",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      body: payload,
    });

    expect(res.statusCode).toBe(200);
  });

  it("stitches in grid mode with 2 columns", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "a.png", contentType: "image/png", content: PNG_200x150 },
      { name: "file", filename: "b.jpg", contentType: "image/jpeg", content: JPG_100x100 },
      { name: "file", filename: "c.png", contentType: "image/png", content: PNG_200x150 },
      { name: "file", filename: "d.jpg", contentType: "image/jpeg", content: JPG_100x100 },
      { name: "settings", content: JSON.stringify({ direction: "grid", gridColumns: 2 }) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/stitch",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      body: payload,
    });

    expect(res.statusCode).toBe(200);
    const body = JSON.parse(res.body);
    expect(body.jobId).toBeDefined();
    expect(body.downloadUrl).toMatch(/\/api\/v1\/download\//);
  });

  it("applies alignment setting", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "a.png", contentType: "image/png", content: PNG_200x150 },
      { name: "file", filename: "b.jpg", contentType: "image/jpeg", content: JPG_100x100 },
      { name: "settings", content: JSON.stringify({ alignment: "start" }) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/stitch",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      body: payload,
    });

    expect(res.statusCode).toBe(200);
  });

  it("applies border and cornerRadius", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "a.png", contentType: "image/png", content: PNG_200x150 },
      { name: "file", filename: "b.png", contentType: "image/png", content: PNG_200x150 },
      { name: "settings", content: JSON.stringify({ border: 20, cornerRadius: 10 }) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/stitch",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      body: payload,
    });

    expect(res.statusCode).toBe(200);
  });

  it("respects quality setting for jpeg", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "a.png", contentType: "image/png", content: PNG_200x150 },
      { name: "file", filename: "b.png", contentType: "image/png", content: PNG_200x150 },
      { name: "settings", content: JSON.stringify({ format: "jpeg", quality: 50 }) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/stitch",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      body: payload,
    });

    expect(res.statusCode).toBe(200);
  });

  it("handles resizeMode stretch", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "a.png", contentType: "image/png", content: PNG_200x150 },
      { name: "file", filename: "b.jpg", contentType: "image/jpeg", content: JPG_100x100 },
      { name: "settings", content: JSON.stringify({ resizeMode: "stretch" }) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/stitch",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      body: payload,
    });

    expect(res.statusCode).toBe(200);
  });

  it("handles resizeMode crop", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "a.png", contentType: "image/png", content: PNG_200x150 },
      { name: "file", filename: "b.jpg", contentType: "image/jpeg", content: JPG_100x100 },
      { name: "settings", content: JSON.stringify({ resizeMode: "crop" }) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/stitch",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      body: payload,
    });

    expect(res.statusCode).toBe(200);
  });
});

// ═══════════════════════════════════════════════════════════════════════════
// PIPELINE
// ═══════════════════════════════════════════════════════════════════════════
describe("Pipeline", () => {
  describe("POST /api/v1/pipeline/execute", () => {
    it("executes a multi-step pipeline (resize then rotate)", async () => {
      const pipeline = {
        steps: [
          { toolId: "resize", settings: { width: 100 } },
          { toolId: "rotate", settings: { angle: 90 } },
        ],
      };

      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "pipeline.png", contentType: "image/png", content: PNG_200x150 },
        { name: "pipeline", content: JSON.stringify(pipeline) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.stepsCompleted).toBe(2);
      expect(body.downloadUrl).toBeDefined();
    });

    it("returns 400 when pipeline references non-existent tool", async () => {
      const pipeline = {
        steps: [{ toolId: "fake-tool-xyz", settings: {} }],
      };

      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "pipeline.png", contentType: "image/png", content: PNG_1x1 },
        { name: "pipeline", content: JSON.stringify(pipeline) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(400);
    });

    it("returns 400 when pipeline has no steps", async () => {
      const pipeline = { steps: [] };

      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "pipeline.png", contentType: "image/png", content: PNG_1x1 },
        { name: "pipeline", content: JSON.stringify(pipeline) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(400);
    });

    it("returns 400 without pipeline definition", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "pipeline.png", contentType: "image/png", content: PNG_1x1 },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(400);
    });

    it("returns 400 when a step has invalid settings", async () => {
      const pipeline = {
        steps: [
          { toolId: "crop", settings: { left: -1, top: -1 } }, // invalid: min(0)
        ],
      };

      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "pipeline.png", contentType: "image/png", content: PNG_200x150 },
        { name: "pipeline", content: JSON.stringify(pipeline) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(400);
    });

    it("executes Social Media Ready template pipeline", async () => {
      const pipeline = {
        steps: [
          { toolId: "resize", settings: { width: 1080, height: 1080, fit: "cover" } },
          { toolId: "compress", settings: { quality: 80 } },
          { toolId: "strip-metadata", settings: {} },
          { toolId: "convert", settings: { format: "webp" } },
        ],
      };

      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "social.png", contentType: "image/png", content: PNG_200x150 },
        { name: "pipeline", content: JSON.stringify(pipeline) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.stepsCompleted).toBe(4);
      expect(body.steps).toHaveLength(4);
      expect(body.steps[0].toolId).toBe("resize");
      expect(body.steps[1].toolId).toBe("compress");
      expect(body.steps[2].toolId).toBe("strip-metadata");
      expect(body.steps[3].toolId).toBe("convert");
      expect(body.downloadUrl).toContain(".webp");
      // Note: upscaling from 200x150 to 1080x1080 increases size
      expect(body.processedSize).toBeGreaterThan(0);
    });

    it("executes Privacy Clean template pipeline", async () => {
      const pipeline = {
        steps: [
          { toolId: "strip-metadata", settings: {} },
          { toolId: "convert", settings: { format: "jpg" } },
        ],
      };

      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "privacy.png", contentType: "image/png", content: PNG_200x150 },
        { name: "pipeline", content: JSON.stringify(pipeline) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.stepsCompleted).toBe(2);
      expect(body.downloadUrl).toContain(".jpg");
    });

    it("executes Web Optimization template pipeline", async () => {
      const pipeline = {
        steps: [
          { toolId: "resize", settings: { width: 1920, fit: "inside" } },
          { toolId: "convert", settings: { format: "webp" } },
          { toolId: "compress", settings: { quality: 80 } },
        ],
      };

      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "web.png", contentType: "image/png", content: PNG_200x150 },
        { name: "pipeline", content: JSON.stringify(pipeline) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.stepsCompleted).toBe(3);
    });

    it("executes Profile Picture template pipeline", async () => {
      const pipeline = {
        steps: [
          { toolId: "resize", settings: { width: 400, height: 400, fit: "cover" } },
          { toolId: "compress", settings: { quality: 85 } },
        ],
      };

      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "profile.png", contentType: "image/png", content: PNG_200x150 },
        { name: "pipeline", content: JSON.stringify(pipeline) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.stepsCompleted).toBe(2);
    });

    it("executes Watermark Batch template pipeline", async () => {
      const pipeline = {
        steps: [
          { toolId: "watermark-text", settings: { text: "SAMPLE", opacity: 30 } },
          { toolId: "strip-metadata", settings: {} },
          { toolId: "compress", settings: { quality: 85 } },
        ],
      };

      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "watermark.png", contentType: "image/png", content: PNG_200x150 },
        { name: "pipeline", content: JSON.stringify(pipeline) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.stepsCompleted).toBe(3);
    });

    it("executes a single-step pipeline", async () => {
      const pipeline = {
        steps: [{ toolId: "convert", settings: { format: "webp" } }],
      };

      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "single.png", contentType: "image/png", content: PNG_200x150 },
        { name: "pipeline", content: JSON.stringify(pipeline) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.stepsCompleted).toBe(1);
      expect(body.downloadUrl).toContain(".webp");
    });

    it("returns full step details in response", async () => {
      const pipeline = {
        steps: [
          { toolId: "resize", settings: { width: 50 } },
          { toolId: "convert", settings: { format: "jpg" } },
          { toolId: "compress", settings: { quality: 60 } },
        ],
      };

      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "details.png", contentType: "image/png", content: PNG_200x150 },
        { name: "pipeline", content: JSON.stringify(pipeline) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.jobId).toBeDefined();
      expect(typeof body.jobId).toBe("string");
      expect(body.originalSize).toBeGreaterThan(0);
      expect(body.processedSize).toBeGreaterThan(0);
      expect(body.steps).toHaveLength(3);
      for (const step of body.steps) {
        expect(step.step).toBeGreaterThan(0);
        expect(step.toolId).toBeDefined();
        expect(step.size).toBeGreaterThan(0);
      }
    });

    it("returns 400 for invalid JSON pipeline", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "bad.png", contentType: "image/png", content: PNG_1x1 },
        { name: "pipeline", content: "not valid json{{{" },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(400);
      expect(JSON.parse(res.body).error).toContain("JSON");
    });

    it("returns 400 when no file is provided", async () => {
      const pipeline = {
        steps: [{ toolId: "resize", settings: { width: 100 } }],
      };

      const { body: payload, contentType } = createMultipartPayload([
        { name: "pipeline", content: JSON.stringify(pipeline) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(400);
    });

    it("handles pipeline with different image formats (JPG input)", async () => {
      const pipeline = {
        steps: [
          { toolId: "resize", settings: { width: 50 } },
          { toolId: "convert", settings: { format: "png" } },
        ],
      };

      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "test.jpg", contentType: "image/jpeg", content: JPG_100x100 },
        { name: "pipeline", content: JSON.stringify(pipeline) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.stepsCompleted).toBe(2);
      expect(body.downloadUrl).toContain(".png");
    });

    it("handles pipeline with WebP input", async () => {
      const pipeline = {
        steps: [
          { toolId: "resize", settings: { width: 25 } },
          { toolId: "convert", settings: { format: "jpg" } },
        ],
      };

      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "test.webp", contentType: "image/webp", content: WEBP_50x50 },
        { name: "pipeline", content: JSON.stringify(pipeline) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.stepsCompleted).toBe(2);
    });

    it("executes a pipeline with border + rotate combo", async () => {
      const pipeline = {
        steps: [
          { toolId: "border", settings: { borderWidth: 10, borderColor: "#ff0000" } },
          { toolId: "rotate", settings: { angle: 45 } },
        ],
      };

      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "bordered.png", contentType: "image/png", content: PNG_200x150 },
        { name: "pipeline", content: JSON.stringify(pipeline) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      expect(JSON.parse(res.body).stepsCompleted).toBe(2);
    });

    it("executes pipeline with 5 steps chained", async () => {
      const pipeline = {
        steps: [
          { toolId: "resize", settings: { width: 150 } },
          { toolId: "rotate", settings: { angle: 90 } },
          { toolId: "strip-metadata", settings: {} },
          { toolId: "compress", settings: { quality: 70 } },
          { toolId: "convert", settings: { format: "webp" } },
        ],
      };

      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "chain.png", contentType: "image/png", content: PNG_200x150 },
        { name: "pipeline", content: JSON.stringify(pipeline) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.stepsCompleted).toBe(5);
      expect(body.steps).toHaveLength(5);
    });

    it("accepts pipeline with more than 20 steps when limit is unlimited", async () => {
      const steps = Array.from({ length: 21 }, () => ({
        toolId: "resize",
        settings: { width: 100 },
      }));

      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "many-steps.png", contentType: "image/png", content: PNG_1x1 },
        { name: "pipeline", content: JSON.stringify({ steps }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
    });

    it("processes result file is downloadable", async () => {
      const pipeline = {
        steps: [{ toolId: "resize", settings: { width: 50 } }],
      };

      const { body: payload, contentType } = createMultipartPayload([
        {
          name: "file",
          filename: "downloadable.png",
          contentType: "image/png",
          content: PNG_200x150,
        },
        { name: "pipeline", content: JSON.stringify(pipeline) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);

      // Verify the download URL is valid
      const dlRes = await app.inject({
        method: "GET",
        url: body.downloadUrl,
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(dlRes.statusCode).toBe(200);
      expect(dlRes.rawPayload.length).toBeGreaterThan(0);
    });

    it("returns 400 with mixed valid and invalid tool IDs", async () => {
      const pipeline = {
        steps: [
          { toolId: "resize", settings: { width: 100 } },
          { toolId: "nonexistent-tool", settings: {} },
          { toolId: "convert", settings: { format: "jpg" } },
        ],
      };

      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "mixed.png", contentType: "image/png", content: PNG_1x1 },
        { name: "pipeline", content: JSON.stringify(pipeline) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/execute",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(400);
      expect(JSON.parse(res.body).error).toContain("nonexistent-tool");
    });
  });

  describe("Pipeline CRUD", () => {
    let savedPipelineId: string;

    it("saves a pipeline definition", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/save",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: {
          name: "My Pipeline",
          description: "Test pipeline",
          steps: [
            { toolId: "resize", settings: { width: 100 } },
            { toolId: "convert", settings: { format: "jpg" } },
          ],
        },
      });
      expect(res.statusCode).toBe(201);
      const body = JSON.parse(res.body);
      expect(body.id).toBeDefined();
      expect(body.name).toBe("My Pipeline");
      savedPipelineId = body.id;
    });

    it("lists saved pipelines", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/pipeline/list",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.pipelines).toBeDefined();
      expect(body.pipelines.length).toBeGreaterThan(0);
    });

    it("deletes a saved pipeline", async () => {
      const res = await app.inject({
        method: "DELETE",
        url: `/api/v1/pipeline/${savedPipelineId}`,
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(res.statusCode).toBe(200);
      expect(JSON.parse(res.body).ok).toBe(true);
    });

    it("returns 404 deleting non-existent pipeline", async () => {
      const res = await app.inject({
        method: "DELETE",
        url: "/api/v1/pipeline/00000000-0000-0000-0000-000000000000",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(res.statusCode).toBe(404);
    });

    it("rejects pipeline save with invalid tool ID", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/save",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: {
          name: "Bad Pipeline",
          steps: [{ toolId: "does-not-exist", settings: {} }],
        },
      });
      expect(res.statusCode).toBe(400);
    });

    it("rejects pipeline save with empty name", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/save",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: {
          name: "",
          steps: [{ toolId: "resize", settings: {} }],
        },
      });
      expect(res.statusCode).toBe(400);
    });

    it("saves pipeline without description", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/save",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: {
          name: "No Description Pipeline",
          steps: [{ toolId: "resize", settings: { width: 100 } }],
        },
      });
      expect(res.statusCode).toBe(201);
      const body = JSON.parse(res.body);
      expect(body.name).toBe("No Description Pipeline");
      expect(body.description).toBeNull();

      // Cleanup
      await app.inject({
        method: "DELETE",
        url: `/api/v1/pipeline/${body.id}`,
        headers: { authorization: `Bearer ${adminToken}` },
      });
    });

    it("saves and retrieves pipeline with correct step data", async () => {
      const steps = [
        { toolId: "resize", settings: { width: 200, height: 200, fit: "cover" } },
        { toolId: "compress", settings: { quality: 75 } },
        { toolId: "convert", settings: { format: "webp" } },
      ];

      const saveRes = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/save",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: "Full Pipeline", description: "With all data", steps },
      });
      expect(saveRes.statusCode).toBe(201);
      const saved = JSON.parse(saveRes.body);

      // List and verify the pipeline steps are correctly stored
      const listRes = await app.inject({
        method: "GET",
        url: "/api/v1/pipeline/list",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      const listed = JSON.parse(listRes.body);
      const found = listed.pipelines.find((p: { id: string }) => p.id === saved.id);
      expect(found).toBeDefined();
      expect(found.steps).toHaveLength(3);
      expect(found.steps[0].toolId).toBe("resize");
      expect(found.steps[0].settings.width).toBe(200);
      expect(found.steps[1].toolId).toBe("compress");
      expect(found.steps[2].toolId).toBe("convert");
      expect(found.createdAt).toBeDefined();

      // Cleanup
      await app.inject({
        method: "DELETE",
        url: `/api/v1/pipeline/${saved.id}`,
        headers: { authorization: `Bearer ${adminToken}` },
      });
    });

    it("rejects pipeline name exceeding 100 characters", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/save",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: {
          name: "A".repeat(101),
          steps: [{ toolId: "resize", settings: {} }],
        },
      });
      expect(res.statusCode).toBe(400);
    });

    it("rejects pipeline description exceeding 500 characters", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/save",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: {
          name: "Long Desc",
          description: "D".repeat(501),
          steps: [{ toolId: "resize", settings: {} }],
        },
      });
      expect(res.statusCode).toBe(400);
    });

    it("accepts saving pipeline with more than 20 steps when limit is unlimited", async () => {
      const steps = Array.from({ length: 21 }, () => ({
        toolId: "resize",
        settings: { width: 100 },
      }));

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/save",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: "Many Steps", steps },
      });
      expect(res.statusCode).toBe(201);
    });

    it("can save and delete multiple pipelines", async () => {
      // Save two pipelines
      const res1 = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/save",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: {
          name: "Pipeline A",
          steps: [{ toolId: "resize", settings: { width: 100 } }],
        },
      });
      const res2 = await app.inject({
        method: "POST",
        url: "/api/v1/pipeline/save",
        headers: { authorization: `Bearer ${adminToken}` },
        payload: {
          name: "Pipeline B",
          steps: [{ toolId: "convert", settings: { format: "jpg" } }],
        },
      });
      expect(res1.statusCode).toBe(201);
      expect(res2.statusCode).toBe(201);

      const id1 = JSON.parse(res1.body).id;
      const id2 = JSON.parse(res2.body).id;

      // List should have both
      const listRes = await app.inject({
        method: "GET",
        url: "/api/v1/pipeline/list",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      const pipelines = JSON.parse(listRes.body).pipelines;
      expect(pipelines.some((p: { id: string }) => p.id === id1)).toBe(true);
      expect(pipelines.some((p: { id: string }) => p.id === id2)).toBe(true);

      // Delete both
      await app.inject({
        method: "DELETE",
        url: `/api/v1/pipeline/${id1}`,
        headers: { authorization: `Bearer ${adminToken}` },
      });
      await app.inject({
        method: "DELETE",
        url: `/api/v1/pipeline/${id2}`,
        headers: { authorization: `Bearer ${adminToken}` },
      });
    });
  });

  describe("GET /api/v1/pipeline/tools", () => {
    it("returns the list of pipeline-compatible tool IDs", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/pipeline/tools",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(Array.isArray(body.toolIds)).toBe(true);
      expect(body.toolIds.length).toBeGreaterThan(0);
    });

    it("includes factory-registered tools (resize, crop, convert)", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/pipeline/tools",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      const { toolIds } = JSON.parse(res.body);
      expect(toolIds).toContain("resize");
      expect(toolIds).toContain("crop");
      expect(toolIds).toContain("convert");
      expect(toolIds).toContain("compress");
      expect(toolIds).toContain("rotate");
    });

    it("includes AI tools registered for pipeline use", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/pipeline/tools",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      const { toolIds } = JSON.parse(res.body);
      expect(toolIds).toContain("remove-background");
      expect(toolIds).toContain("upscale");
      expect(toolIds).toContain("blur-faces");
      expect(toolIds).toContain("noise-removal");
    });

    it("excludes tools that are not pipeline-compatible", async () => {
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/pipeline/tools",
        headers: { authorization: `Bearer ${adminToken}` },
      });
      const { toolIds } = JSON.parse(res.body);
      expect(toolIds).not.toContain("ocr");
      expect(toolIds).not.toContain("erase-object");
      expect(toolIds).not.toContain("info");
      expect(toolIds).not.toContain("collage");
      expect(toolIds).not.toContain("compare");
    });
  });

  describe("Pipeline rejects incompatible tools", () => {
    const customRouteTools = ["ocr", "erase-object"];

    for (const toolId of customRouteTools) {
      it(`returns 400 when pipeline uses "${toolId}" (custom-route tool)`, async () => {
        const pipeline = { steps: [{ toolId, settings: {} }] };

        const { body: payload, contentType } = createMultipartPayload([
          { name: "file", filename: "test.png", contentType: "image/png", content: PNG_200x150 },
          { name: "pipeline", content: JSON.stringify(pipeline) },
        ]);

        const res = await app.inject({
          method: "POST",
          url: "/api/v1/pipeline/execute",
          headers: {
            authorization: `Bearer ${adminToken}`,
            "content-type": contentType,
          },
          payload,
        });
        expect(res.statusCode).toBe(400);
        expect(JSON.parse(res.body).error).toContain("not found");
      });
    }
  });
});

// ═══════════════════════════════════════════════════════════════════════════
// OCR API
// ═══════════════════════════════════════════════════════════════════════════
describe("OCR API", () => {
  describe("POST /api/v1/tools/image/ocr", () => {
    it("accepts quality param and returns text without engine field", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "ocr-test.png", contentType: "image/png", content: PNG_200x150 },
        { name: "settings", content: JSON.stringify({ quality: "fast" }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/ocr",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });

      // OCR may fail with 422 if Python sidecar/engines are not installed (CI),
      // or 501 if the OCR feature bundle is not installed
      if (res.statusCode === 200) {
        const body = JSON.parse(res.body);
        expect(body.text).toBeDefined();
        expect(body.jobId).toBeDefined();
        expect(body).not.toHaveProperty("engine");
      } else {
        expect([422, 501]).toContain(res.statusCode);
      }
    });

    it("backward compat: old engine param is still accepted", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        {
          name: "file",
          filename: "ocr-compat.png",
          contentType: "image/png",
          content: PNG_200x150,
        },
        { name: "settings", content: JSON.stringify({ engine: "tesseract" }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/ocr",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });

      // Should not be a 400 — engine param must still be accepted
      expect(res.statusCode).not.toBe(400);
      // Either succeeds (200), OCR engine unavailable (422), or feature not installed (501)
      expect([200, 422, 501]).toContain(res.statusCode);
    });

    it("accepts language auto and enhance params", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "ocr-lang.png", contentType: "image/png", content: PNG_200x150 },
        {
          name: "settings",
          content: JSON.stringify({ quality: "balanced", language: "auto", enhance: true }),
        },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/ocr",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });

      // All three params should be accepted without validation errors
      expect(res.statusCode).not.toBe(400);
      expect([200, 422, 501]).toContain(res.statusCode);
    });

    it("returns 400 for invalid quality value", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "ocr-bad.png", contentType: "image/png", content: PNG_200x150 },
        { name: "settings", content: JSON.stringify({ quality: "ultra" }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/ocr",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      // 400 when feature is installed, 501 when feature bundle is missing
      expect([400, 501]).toContain(res.statusCode);
    });

    it("returns 400 when no file is provided", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "settings", content: JSON.stringify({ quality: "fast" }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/ocr",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      // 400 when feature is installed, 501 when feature bundle is missing
      expect([400, 501]).toContain(res.statusCode);
    });
  });
});

// ═══════════════════════════════════════════════════════════════════════════
// BATCH PROCESSING
// ═══════════════════════════════════════════════════════════════════════════
describe("Batch processing", () => {
  describe("POST /api/v1/tools/:section/:toolId/batch", () => {
    it("processes multiple images in a batch and returns ZIP", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "a.png", contentType: "image/png", content: PNG_1x1 },
        { name: "file", filename: "b.png", contentType: "image/png", content: PNG_200x150 },
        { name: "settings", content: JSON.stringify({ width: 50 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize/batch",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      // Batch streams a ZIP directly — raw status comes from writeHead
      expect(res.statusCode).toBe(200);
      expect(res.headers["content-type"]).toBe("application/zip");
      expect(res.headers["x-job-id"]).toBeDefined();
    });

    it("returns 404 for non-existent batch tool", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "a.png", contentType: "image/png", content: PNG_1x1 },
        { name: "settings", content: "{}" },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/nonexistent/batch",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(404);
    });

    it("returns 400 for batch with no files", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "settings", content: JSON.stringify({ width: 50 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize/batch",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(400);
    });

    it("batch converts multiple images to webp", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "img1.png", contentType: "image/png", content: PNG_200x150 },
        { name: "file", filename: "img2.jpg", contentType: "image/jpeg", content: JPG_100x100 },
        { name: "settings", content: JSON.stringify({ format: "webp" }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/convert/batch",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      expect(res.headers["content-type"]).toBe("application/zip");
    });

    it("batch compresses images with quality setting", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "a.png", contentType: "image/png", content: PNG_200x150 },
        { name: "file", filename: "b.jpg", contentType: "image/jpeg", content: JPG_100x100 },
        { name: "settings", content: JSON.stringify({ quality: 50 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/compress/batch",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      expect(res.headers["content-type"]).toBe("application/zip");
    });

    it("batch strips metadata from images", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "meta1.png", contentType: "image/png", content: PNG_200x150 },
        { name: "file", filename: "meta2.jpg", contentType: "image/jpeg", content: JPG_100x100 },
        { name: "settings", content: JSON.stringify({}) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/strip-metadata/batch",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      expect(res.headers["content-type"]).toBe("application/zip");
    });

    it("batch rotates images", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "rot1.png", contentType: "image/png", content: PNG_200x150 },
        { name: "file", filename: "rot2.webp", contentType: "image/webp", content: WEBP_50x50 },
        { name: "settings", content: JSON.stringify({ angle: 180 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/rotate/batch",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      expect(res.headers["content-type"]).toBe("application/zip");
    });

    it("returns 400 for batch with invalid settings", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "a.png", contentType: "image/png", content: PNG_1x1 },
        { name: "settings", content: JSON.stringify({ format: "invalid-format" }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/convert/batch",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(400);
    });

    it("batch with single file works", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "solo.png", contentType: "image/png", content: PNG_200x150 },
        { name: "settings", content: JSON.stringify({ width: 100 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize/batch",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      expect(res.headers["content-type"]).toBe("application/zip");
    });

    it("batch uses default settings when none provided", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "default.png", contentType: "image/png", content: PNG_200x150 },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/strip-metadata/batch",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      expect(res.headers["content-type"]).toBe("application/zip");
    });

    it("batch includes X-File-Results header", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "first.png", contentType: "image/png", content: PNG_1x1 },
        { name: "file", filename: "second.png", contentType: "image/png", content: PNG_200x150 },
        { name: "settings", content: JSON.stringify({ width: 50 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize/batch",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      expect(res.headers["x-file-results"]).toBeDefined();
      const parsed = JSON.parse(decodeURIComponent(res.headers["x-file-results"] as string));
      expect(parsed["0"]).toBeDefined();
      expect(parsed["1"]).toBeDefined();
    });

    it("returns ZIP with content-disposition attachment header", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "a.png", contentType: "image/png", content: PNG_1x1 },
        { name: "settings", content: JSON.stringify({ width: 50 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize/batch",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      expect(res.headers["content-disposition"]).toContain("attachment");
      expect(res.headers["content-disposition"]).toContain("batch-resize");
    });

    it("batch with clientJobId uses provided ID", async () => {
      const clientJobId = "my-custom-job-id-12345";
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "a.png", contentType: "image/png", content: PNG_1x1 },
        { name: "settings", content: JSON.stringify({ width: 50 }) },
        { name: "clientJobId", content: clientJobId },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize/batch",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      expect(res.headers["x-job-id"]).toBe(clientJobId);
    });

    it("batch handles mixed file formats", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "a.png", contentType: "image/png", content: PNG_200x150 },
        { name: "file", filename: "b.jpg", contentType: "image/jpeg", content: JPG_100x100 },
        { name: "file", filename: "c.webp", contentType: "image/webp", content: WEBP_50x50 },
        { name: "settings", content: JSON.stringify({ width: 50 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize/batch",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);
      expect(res.headers["content-type"]).toBe("application/zip");
    });

    it("batch returns X-File-Results header with index-to-filename mapping", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "first.png", contentType: "image/png", content: PNG_1x1 },
        { name: "file", filename: "second.png", contentType: "image/png", content: PNG_200x150 },
        { name: "settings", content: JSON.stringify({ width: 50 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize/batch",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);

      const fileResults = res.headers["x-file-results"];
      expect(fileResults).toBeDefined();
      const parsed = JSON.parse(decodeURIComponent(fileResults as string));
      expect(parsed["0"]).toBeDefined();
      expect(parsed["1"]).toBeDefined();
      expect(typeof parsed["0"]).toBe("string");
      expect(typeof parsed["1"]).toBe("string");
    });

    it("batch X-File-Results entries contain original filename stems in order", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "aaa.png", contentType: "image/png", content: PNG_1x1 },
        { name: "file", filename: "bbb.png", contentType: "image/png", content: PNG_200x150 },
        { name: "file", filename: "ccc.jpg", contentType: "image/jpeg", content: JPG_100x100 },
        { name: "settings", content: JSON.stringify({ width: 50 }) },
      ]);

      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize/batch",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
      expect(res.statusCode).toBe(200);

      const fileResults = JSON.parse(decodeURIComponent(res.headers["x-file-results"] as string));
      const names = [fileResults["0"], fileResults["1"], fileResults["2"]];
      expect(names[0]).toContain("aaa");
      expect(names[1]).toContain("bbb");
      expect(names[2]).toContain("ccc");
    });
  });

  describe("Batch rejects incompatible tools", () => {
    const customRouteTools = ["ocr", "erase-object"];

    for (const toolId of customRouteTools) {
      it(`returns 404 for batch "${toolId}" (custom-route tool)`, async () => {
        const { body: payload, contentType } = createMultipartPayload([
          { name: "file", filename: "a.png", contentType: "image/png", content: PNG_200x150 },
          { name: "settings", content: JSON.stringify({}) },
        ]);

        const res = await app.inject({
          method: "POST",
          url: `${apiToolPath(toolId)}/batch`,
          headers: {
            authorization: `Bearer ${adminToken}`,
            "content-type": contentType,
          },
          payload,
        });
        expect(res.statusCode).toBe(404);
        expect(JSON.parse(res.body).error).toContain("not found");
      });
    }
  });
});

// ═══════════════════════════════════════════════════════════════════════════
// SMART CROP FORMAT PRESERVATION
// ═══════════════════════════════════════════════════════════════════════════
describe("Smart crop format preservation", () => {
  // Helper: smart-crop requires the face-detection AI bundle.
  // When it's not installed, the API returns 501 — skip assertions on the body.
  const smartCropRequest = async (
    file: { name: string; filename: string; contentType: string; content: Buffer },
    settings: Record<string, unknown>,
  ) => {
    const { body: payload, contentType } = createMultipartPayload([
      file,
      { name: "settings", content: JSON.stringify(settings) },
    ]);
    return app.inject({
      method: "POST",
      url: "/api/v1/tools/image/smart-crop",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
  };

  it("preserves JPEG format for JPEG input in content mode", async () => {
    const res = await smartCropRequest(
      { name: "file", filename: "photo.jpg", contentType: "image/jpeg", content: JPG_100x100 },
      { mode: "content", threshold: 30 },
    );
    if (res.statusCode === 501) return; // feature not installed
    expect(res.statusCode).toBe(200);
    expect(JSON.parse(res.body).downloadUrl).toMatch(/_smartcrop\.jpg/);
  });

  it("preserves PNG format for PNG input", async () => {
    const res = await smartCropRequest(
      { name: "file", filename: "image.png", contentType: "image/png", content: PNG_200x150 },
      { mode: "content", threshold: 30 },
    );
    if (res.statusCode === 501) return;
    expect(res.statusCode).toBe(200);
    expect(JSON.parse(res.body).downloadUrl).toMatch(/_smartcrop\.png/);
  });

  it("preserves WebP format for WebP input", async () => {
    const res = await smartCropRequest(
      { name: "file", filename: "image.webp", contentType: "image/webp", content: WEBP_50x50 },
      { mode: "content", threshold: 30 },
    );
    if (res.statusCode === 501) return;
    expect(res.statusCode).toBe(200);
    expect(JSON.parse(res.body).downloadUrl).toMatch(/_smartcrop\.webp/);
  });

  it("preserves JPEG format in attention mode", async () => {
    const res = await smartCropRequest(
      { name: "file", filename: "photo.jpg", contentType: "image/jpeg", content: JPG_100x100 },
      { mode: "attention", width: 50, height: 50 },
    );
    if (res.statusCode === 501) return;
    expect(res.statusCode).toBe(200);
    expect(JSON.parse(res.body).downloadUrl).toMatch(/_smartcrop\.jpg/);
  });

  it("accepts quality setting without error", async () => {
    const res = await smartCropRequest(
      { name: "file", filename: "photo.jpg", contentType: "image/jpeg", content: JPG_100x100 },
      { mode: "content", threshold: 30, quality: 50 },
    );
    if (res.statusCode === 501) return;
    expect(res.statusCode).toBe(200);
  });

  it("subject mode with entropy strategy", async () => {
    const res = await smartCropRequest(
      { name: "file", filename: "photo.jpg", contentType: "image/jpeg", content: JPG_100x100 },
      { mode: "subject", strategy: "entropy", width: 50, height: 50 },
    );
    if (res.statusCode === 501) return;
    expect(res.statusCode).toBe(200);
    expect(JSON.parse(res.body).downloadUrl).toMatch(/_smartcrop\.jpg/);
  });

  it("subject mode with padding", async () => {
    const res = await smartCropRequest(
      { name: "file", filename: "photo.jpg", contentType: "image/jpeg", content: JPG_100x100 },
      { mode: "subject", width: 50, height: 50, padding: 10 },
    );
    if (res.statusCode === 501) return;
    expect(res.statusCode).toBe(200);
    expect(JSON.parse(res.body).downloadUrl).toMatch(/_smartcrop\.jpg/);
  });

  it("trim mode with new mode name", async () => {
    const res = await smartCropRequest(
      { name: "file", filename: "photo.png", contentType: "image/png", content: PNG_200x150 },
      { mode: "trim", threshold: 30 },
    );
    if (res.statusCode === 501) return;
    expect(res.statusCode).toBe(200);
    expect(JSON.parse(res.body).downloadUrl).toMatch(/_smartcrop\.png/);
  });

  it("defaults to subject mode when no mode specified", async () => {
    const res = await smartCropRequest(
      { name: "file", filename: "photo.jpg", contentType: "image/jpeg", content: JPG_100x100 },
      { width: 50, height: 50 },
    );
    if (res.statusCode === 501) return;
    expect(res.statusCode).toBe(200);
    expect(JSON.parse(res.body).downloadUrl).toMatch(/_smartcrop\.jpg/);
  });
});

// ═══════════════════════════════════════════════════════════════════════════
// CROP FORMAT PRESERVATION
// ═══════════════════════════════════════════════════════════════════════════
describe("Crop format preservation", () => {
  it("preserves JPEG format for JPEG input", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "photo.jpg", contentType: "image/jpeg", content: JPG_100x100 },
      { name: "settings", content: JSON.stringify({ left: 0, top: 0, width: 50, height: 50 }) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/crop",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    expect(res.statusCode).toBe(200);
    const body = JSON.parse(res.body);
    const dlRes = await app.inject({
      method: "GET",
      url: body.downloadUrl,
      headers: { authorization: `Bearer ${adminToken}` },
    });
    const sharp = (await import("sharp")).default;
    const meta = await sharp(dlRes.rawPayload).metadata();
    expect(meta.format).toBe("jpeg");
  });

  it("preserves PNG format for PNG input", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "image.png", contentType: "image/png", content: PNG_200x150 },
      { name: "settings", content: JSON.stringify({ left: 0, top: 0, width: 50, height: 50 }) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/crop",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    expect(res.statusCode).toBe(200);
    const body = JSON.parse(res.body);
    const dlRes = await app.inject({
      method: "GET",
      url: body.downloadUrl,
      headers: { authorization: `Bearer ${adminToken}` },
    });
    const sharp = (await import("sharp")).default;
    const meta = await sharp(dlRes.rawPayload).metadata();
    expect(meta.format).toBe("png");
  });
});

// ═══════════════════════════════════════════════════════════════════════════
// COLOR ADJUSTMENTS FORMAT PRESERVATION
// ═══════════════════════════════════════════════════════════════════════════
describe("Color adjustments format preservation", () => {
  it("preserves JPEG format for JPEG input via adjust-colors", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "photo.jpg", contentType: "image/jpeg", content: JPG_100x100 },
      { name: "settings", content: JSON.stringify({ brightness: 10 }) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/adjust-colors",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    expect(res.statusCode).toBe(200);
    const body = JSON.parse(res.body);
    const dlRes = await app.inject({
      method: "GET",
      url: body.downloadUrl,
      headers: { authorization: `Bearer ${adminToken}` },
    });
    const sharp = (await import("sharp")).default;
    const meta = await sharp(dlRes.rawPayload).metadata();
    expect(meta.format).toBe("jpeg");
  });

  it("preserves PNG format for PNG input via adjust-colors", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "image.png", contentType: "image/png", content: PNG_200x150 },
      { name: "settings", content: JSON.stringify({ saturation: 20 }) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/adjust-colors",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    expect(res.statusCode).toBe(200);
    const body = JSON.parse(res.body);
    const dlRes = await app.inject({
      method: "GET",
      url: body.downloadUrl,
      headers: { authorization: `Bearer ${adminToken}` },
    });
    const sharp = (await import("sharp")).default;
    const meta = await sharp(dlRes.rawPayload).metadata();
    expect(meta.format).toBe("png");
  });
});

// ═══════════════════════════════════════════════════════════════════════════
// EDGE CASES & ADVERSARIAL INPUTS
// ═══════════════════════════════════════════════════════════════════════════
describe("Edge cases & adversarial inputs", () => {
  it("rejects a zero-byte file upload", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      {
        name: "file",
        filename: "empty.png",
        contentType: "image/png",
        content: Buffer.alloc(0),
      },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/upload",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    // Empty files are skipped, resulting in "No valid files uploaded"
    expect(res.statusCode).toBe(400);
  });

  it("handles a file with only null bytes", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      {
        name: "file",
        filename: "nulls.png",
        contentType: "image/png",
        content: Buffer.alloc(1024, 0),
      },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/upload",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    // Upload accepts the bytes (2.0 multimodal); a null-byte "image" is rejected
    // later when a tool tries to decode it, not at upload time.
    expect(res.statusCode).toBe(200);
  });

  it("handles concurrent requests without corruption", async () => {
    const requests = Array.from({ length: 5 }, (_, i) => {
      const { body: payload, contentType } = createMultipartPayload([
        {
          name: "file",
          filename: `concurrent-${i}.png`,
          contentType: "image/png",
          content: PNG_1x1,
        },
        { name: "settings", content: JSON.stringify({ width: 1 }) },
      ]);

      return app.inject({
        method: "POST",
        url: "/api/v1/tools/image/resize",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
    });

    const results = await Promise.all(requests);
    for (const res of results) {
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.jobId).toBeDefined();
    }

    // Verify all jobIds are unique
    const jobIds = results.map((r) => JSON.parse(r.body).jobId);
    expect(new Set(jobIds).size).toBe(5);
  });

  it("rejects a JSON body on multipart-only endpoint", async () => {
    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/resize",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": "application/json",
      },
      payload: { width: 100 },
    });
    expect(res.statusCode).toBe(400);
  });

  it("handles extremely long tool settings values gracefully", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "img.png", contentType: "image/png", content: PNG_1x1 },
      { name: "settings", content: JSON.stringify({ width: 1, fit: "a".repeat(10_000) }) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/resize",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    // Zod should reject the invalid enum value
    expect(res.statusCode).toBe(400);
  });

  it("handles type confusion in settings (string where number expected)", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "img.png", contentType: "image/png", content: PNG_1x1 },
      { name: "settings", content: JSON.stringify({ width: "not-a-number" }) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/resize",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    expect(res.statusCode).toBe(400);
  });

  it("handles array where object expected in login body", async () => {
    const res = await app.inject({
      method: "POST",
      url: "/api/auth/login",
      headers: { "content-type": "application/json" },
      payload: JSON.stringify(["admin", "Adminpass1"]),
    });
    expect(res.statusCode).toBe(400);
  });

  it("handles deeply nested JSON in settings", async () => {
    // Create a deeply nested object
    let nested: Record<string, unknown> = { value: 1 };
    for (let i = 0; i < 50; i++) {
      nested = { nested };
    }

    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "img.png", contentType: "image/png", content: PNG_1x1 },
      { name: "settings", content: JSON.stringify(nested) },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/resize",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    // Zod schema only cares about the expected keys; extra nesting is ignored.
    // Sharp may fail (422) if no valid resize dimensions are derived.
    expect([200, 400, 422]).toContain(res.statusCode);
  });

  it("handles NaN/Infinity in numeric settings", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "img.png", contentType: "image/png", content: PNG_1x1 },
      { name: "settings", content: '{"width": NaN}' },
    ]);

    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/resize",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    // NaN is not valid JSON, so it should fail to parse
    expect(res.statusCode).toBe(400);
  });

  it("rejects double-encoded path traversal in download", async () => {
    const res = await app.inject({
      method: "GET",
      url: "/api/v1/download/%252e%252e%252f%252e%252e%252fetc/passwd",
    });
    // Even after double decoding, path traversal should be blocked or 404
    expect([400, 404]).toContain(res.statusCode);
  });
});

// ═══════════════════════════════════════════════════════════════════════════
// WORKSPACE INTEGRITY
// ═══════════════════════════════════════════════════════════════════════════
describe("Workspace integrity", () => {
  it("upload creates workspace with input dir", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "ws-test.png", contentType: "image/png", content: PNG_1x1 },
    ]);

    const uploadRes = await app.inject({
      method: "POST",
      url: "/api/v1/upload",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    expect(uploadRes.statusCode).toBe(200);

    const { jobId } = JSON.parse(uploadRes.body);

    // The uploaded file should be downloadable from the workspace
    const downloadRes = await app.inject({
      method: "GET",
      url: `/api/v1/download/${jobId}/ws-test.png`,
    });
    expect(downloadRes.statusCode).toBe(200);
  });

  it("tool processing creates both input and output in workspace", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "ws-tool.png", contentType: "image/png", content: PNG_200x150 },
      { name: "settings", content: JSON.stringify({ width: 50 }) },
    ]);

    const toolRes = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/resize",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    expect(toolRes.statusCode).toBe(200);

    const { jobId, downloadUrl } = JSON.parse(toolRes.body);

    // Output file is downloadable
    const outputRes = await app.inject({
      method: "GET",
      url: downloadUrl,
    });
    expect(outputRes.statusCode).toBe(200);

    // Input file is also saved in workspace (from tool-factory.ts)
    const inputRes = await app.inject({
      method: "GET",
      url: `/api/v1/download/${jobId}/ws-tool.png`,
    });
    expect(inputRes.statusCode).toBe(200);
  });

  it("each job gets a unique workspace (no cross-contamination)", async () => {
    const makeReq = () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "isolated.png", contentType: "image/png", content: PNG_1x1 },
      ]);
      return app.inject({
        method: "POST",
        url: "/api/v1/upload",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": contentType,
        },
        payload,
      });
    };

    const [res1, res2] = await Promise.all([makeReq(), makeReq()]);

    const jobId1 = JSON.parse(res1.body).jobId;
    const jobId2 = JSON.parse(res2.body).jobId;

    expect(jobId1).not.toBe(jobId2);
  });
});

// ═══════════════════════════════════════════════════════════════════════════
// EDIT METADATA
// ═══════════════════════════════════════════════════════════════════════════
describe("Edit metadata", () => {
  describe("POST /api/v1/tools/image/edit-metadata/inspect", () => {
    it("returns parsed EXIF for JPEG with metadata", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "exif.jpg", contentType: "image/jpeg", content: EXIF_JPG },
      ]);
      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/edit-metadata/inspect",
        headers: { authorization: `Bearer ${adminToken}`, "content-type": contentType },
        payload,
      });
      // 422 when exiftool is not installed (e.g. Windows dev, CI without exiftool)
      if (res.statusCode === 422) return;
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.filename).toBe("exif.jpg");
      expect(body.exif).toBeTruthy();
      expect(body.exif.Artist).toBe("Test Artist");
      expect(body.exif.Copyright).toBe("2026 Test Copyright");
    });

    it("returns no exif for metadata-free PNG", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "plain.png", contentType: "image/png", content: PNG_1x1 },
      ]);
      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/edit-metadata/inspect",
        headers: { authorization: `Bearer ${adminToken}`, "content-type": contentType },
        payload,
      });
      if (res.statusCode === 422) return;
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.exif).toBeNull();
    });

    it("rejects request with no file", async () => {
      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/edit-metadata/inspect",
        headers: {
          authorization: `Bearer ${adminToken}`,
          "content-type": "multipart/form-data; boundary=---test",
        },
        payload: Buffer.from("-----test--\r\n"),
      });
      expect(res.statusCode).toBe(400);
    });
  });

  describe("POST /api/v1/tools/image/edit-metadata", () => {
    it("writes metadata and returns downloadable file", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "edit.jpg", contentType: "image/jpeg", content: EXIF_JPG },
        { name: "settings", content: JSON.stringify({ artist: "New Author" }) },
      ]);
      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/edit-metadata",
        headers: { authorization: `Bearer ${adminToken}`, "content-type": contentType },
        payload,
      });
      // 422 when exiftool is not installed
      if (res.statusCode === 422) return;
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.downloadUrl).toBeDefined();
      expect(body.jobId).toBeDefined();
    });

    it("strips specific fields via fieldsToRemove", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "strip.jpg", contentType: "image/jpeg", content: EXIF_JPG },
        { name: "settings", content: JSON.stringify({ fieldsToRemove: ["Software"] }) },
      ]);
      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/edit-metadata",
        headers: { authorization: `Bearer ${adminToken}`, "content-type": contentType },
        payload,
      });
      if (res.statusCode === 422) return;
      expect(res.statusCode).toBe(200);
    });

    it("preserves metadata with empty settings", async () => {
      const { body: payload, contentType } = createMultipartPayload([
        { name: "file", filename: "noop.jpg", contentType: "image/jpeg", content: EXIF_JPG },
        { name: "settings", content: JSON.stringify({}) },
      ]);
      const res = await app.inject({
        method: "POST",
        url: "/api/v1/tools/image/edit-metadata",
        headers: { authorization: `Bearer ${adminToken}`, "content-type": contentType },
        payload,
      });
      if (res.statusCode === 422) return;
      expect(res.statusCode).toBe(200);
    });
  });
});

describe("Image Enhancement", () => {
  it("POST /api/v1/tools/image/image-enhancement processes an image", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "test.png", contentType: "image/png", content: PNG_200x150 },
      {
        name: "settings",
        content: JSON.stringify({
          mode: "auto",
          intensity: 50,
          corrections: {
            exposure: true,
            contrast: true,
            whiteBalance: true,
            saturation: true,
            sharpness: true,
            denoise: true,
          },
        }),
      },
    ]);
    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/image-enhancement",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    expect(res.statusCode).toBe(200);
    const body = JSON.parse(res.body);
    expect(body.jobId).toBeDefined();
    expect(body.downloadUrl).toBeDefined();
    expect(body.processedSize).toBeGreaterThan(0);
  });

  it("POST /api/v1/tools/image/image-enhancement/analyze returns analysis data", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "test.png", contentType: "image/png", content: PNG_200x150 },
    ]);
    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/image-enhancement/analyze",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    expect(res.statusCode).toBe(200);
    const body = JSON.parse(res.body);
    expect(body.scores).toBeDefined();
    expect(body.corrections).toBeDefined();
    expect(body.issues).toBeInstanceOf(Array);
    expect(body.suggestedMode).toBeDefined();
    expect(typeof body.scores.exposure).toBe("number");
  });

  it("preserves JPEG format through enhancement", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "photo.jpg", contentType: "image/jpeg", content: JPG_100x100 },
      {
        name: "settings",
        content: JSON.stringify({
          mode: "auto",
          intensity: 50,
        }),
      },
    ]);
    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/image-enhancement",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    expect(res.statusCode).toBe(200);
    const body = JSON.parse(res.body);
    expect(body.downloadUrl).toMatch(/\.jpg/);
  });

  it("rejects empty file", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "empty.png", contentType: "image/png", content: Buffer.alloc(0) },
      {
        name: "settings",
        content: JSON.stringify({ mode: "auto" }),
      },
    ]);
    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/image-enhancement",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    expect(res.statusCode).toBe(400);
  });
});

// ═══════════════════════════════════════════════════════════════════════════
// NOISE REMOVAL
// ═══════════════════════════════════════════════════════════════════════════
describe("Noise Removal", () => {
  it("POST /api/v1/tools/image/noise-removal processes with quick tier", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "noisy.png", contentType: "image/png", content: PNG_200x150 },
      {
        name: "settings",
        content: JSON.stringify({ tier: "quick", strength: 50 }),
      },
    ]);
    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/noise-removal",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    // Either succeeds, Python sidecar unavailable, or feature not installed
    expect(res.statusCode).not.toBe(400);
    expect([200, 422, 501]).toContain(res.statusCode);
    if (res.statusCode === 200) {
      expect(res.headers["content-type"]).toMatch(/^image\//);
      expect(res.headers["content-disposition"]).toMatch(/attachment/);
    }
  });

  it("returns 400 when no file is provided", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      {
        name: "settings",
        content: JSON.stringify({ tier: "quick", strength: 50 }),
      },
    ]);
    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/noise-removal",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    // 400 when feature is installed, 501 when feature bundle is missing
    expect([400, 501]).toContain(res.statusCode);
  });

  it("returns 400 for empty file", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "empty.png", contentType: "image/png", content: Buffer.alloc(0) },
      {
        name: "settings",
        content: JSON.stringify({ tier: "quick" }),
      },
    ]);
    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/noise-removal",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    // 400 when feature is installed, 501 when feature bundle is missing
    expect([400, 501]).toContain(res.statusCode);
  });

  it("accepts JPEG input", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "noisy.jpg", contentType: "image/jpeg", content: JPG_100x100 },
      {
        name: "settings",
        content: JSON.stringify({ tier: "quick", strength: 30 }),
      },
    ]);
    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/noise-removal",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    expect(res.statusCode).not.toBe(400);
    expect([200, 422, 501]).toContain(res.statusCode);
  });

  it("uses default settings when none provided", async () => {
    const { body: payload, contentType } = createMultipartPayload([
      { name: "file", filename: "noisy.png", contentType: "image/png", content: PNG_200x150 },
    ]);
    const res = await app.inject({
      method: "POST",
      url: "/api/v1/tools/image/noise-removal",
      headers: {
        authorization: `Bearer ${adminToken}`,
        "content-type": contentType,
      },
      payload,
    });
    // Defaults should be accepted without validation errors
    expect(res.statusCode).not.toBe(400);
    expect([200, 422, 501]).toContain(res.statusCode);
  });
});
