import { NextResponse } from "next/server";
import sharp from "sharp";
import { z } from "zod";
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
import { Attachment, AttachmentPartition } from "@open-mercato/core/modules/attachments/data/entities";
import {
  buildThumbnailCacheKey,
  readThumbnailCache,
  writeThumbnailCache
} from "@open-mercato/core/modules/attachments/lib/thumbnailCache";
import { canRenderInlineAttachment } from "@open-mercato/core/modules/attachments/lib/security";
import { checkAttachmentAccess, isSuperAdminAuth } from "@open-mercato/core/modules/attachments/lib/access";
import { attachmentsTag, attachmentErrorSchema } from "../../../openapi.js";
import {
  MAX_IMAGE_SOURCE_PIXELS,
  validateImageDimensions,
  validateImageMagicBytes
} from "@open-mercato/core/modules/attachments/lib/imageSafety";
import { StorageDriverFactory } from "../../../../lib/drivers/index.js";
import { createLogger } from "@open-mercato/shared/lib/logger";
const logger = createLogger("attachments").child({ component: "image" });
const querySchema = z.object({
  width: z.coerce.number().int().min(1).max(4e3).optional(),
  height: z.coerce.number().int().min(1).max(4e3).optional(),
  cropType: z.enum(["cover", "contain"]).optional()
});
const metadata = {
  GET: { requireAuth: false }
};
async function GET(req, context) {
  const auth = await getAuthFromRequest(req);
  const { id } = await context.params;
  if (!id) {
    return NextResponse.json({ error: "Attachment id is required" }, { status: 400 });
  }
  const parsedQuery = querySchema.safeParse(
    Object.fromEntries(new URL(req.url).searchParams.entries())
  );
  if (!parsedQuery.success) {
    return NextResponse.json({ error: "Invalid size parameters" }, { status: 400 });
  }
  const { width, height, cropType } = parsedQuery.data;
  const { resolve } = await createRequestContainer();
  const em = resolve("em");
  const storageDriverFactory = resolve("storageDriverFactory") ?? new StorageDriverFactory(em);
  const findFilter = { id };
  if (auth && !isSuperAdminAuth(auth)) {
    if (auth.tenantId) findFilter.tenantId = auth.tenantId;
    if (auth.orgId) findFilter.organizationId = auth.orgId;
  }
  const attachment = await em.findOne(Attachment, findFilter);
  if (!attachment) {
    return NextResponse.json({ error: "Not found" }, { status: 404 });
  }
  if (!canRenderInlineAttachment(attachment.mimeType)) {
    return NextResponse.json({ error: "Unsupported media type" }, { status: 400 });
  }
  const partition = await em.findOne(AttachmentPartition, { code: attachment.partitionCode });
  if (!partition) {
    return NextResponse.json({ error: "Partition misconfigured" }, { status: 500 });
  }
  const access = checkAttachmentAccess(auth, attachment, partition);
  if (!access.ok) {
    const message = access.status === 401 ? "Unauthorized" : "Forbidden";
    return NextResponse.json({ error: message }, { status: access.status });
  }
  const driver = await storageDriverFactory.resolveForPartition(attachment.partitionCode, {
    tenantId: attachment.tenantId ?? "",
    organizationId: attachment.organizationId ?? ""
  });
  const cacheKey = buildThumbnailCacheKey(width, height, cropType);
  try {
    let buffer = null;
    if (cacheKey) {
      buffer = await readThumbnailCache(attachment.partitionCode, attachment.id, cacheKey);
    }
    if (!buffer) {
      const { buffer: input } = await driver.read(attachment.partitionCode, attachment.storagePath);
      const magicBytesValidation = validateImageMagicBytes(input, attachment.mimeType);
      if (!magicBytesValidation.ok) {
        return NextResponse.json({ error: magicBytesValidation.error }, { status: magicBytesValidation.status });
      }
      const dimensionsValidation = await validateImageDimensions(input);
      if (!dimensionsValidation.ok) {
        return NextResponse.json({ error: dimensionsValidation.error }, { status: dimensionsValidation.status });
      }
      let transformer = sharp(input, {
        failOn: "error",
        limitInputPixels: MAX_IMAGE_SOURCE_PIXELS
      });
      if (width || height) {
        const resizeOptions = {
          width: width || void 0,
          height: height || void 0,
          fit: cropType === "contain" ? "contain" : "cover"
        };
        if (cropType === "contain") {
          resizeOptions.background = { r: 0, g: 0, b: 0, alpha: 0 };
        }
        transformer = transformer.resize(resizeOptions);
      }
      buffer = await transformer.toBuffer();
      if (cacheKey) {
        void writeThumbnailCache(attachment.partitionCode, attachment.id, cacheKey, buffer).catch((cacheError) => {
          logger.error("Thumbnail cache write failed", { err: cacheError });
        });
      }
    }
    if (!buffer) {
      return NextResponse.json({ error: "Failed to render image" }, { status: 500 });
    }
    const responseBody = new Uint8Array(buffer);
    return new NextResponse(responseBody, {
      headers: {
        "Content-Type": attachment.mimeType || "image/jpeg",
        "Cache-Control": partition.isPublic ? "public, max-age=3600" : "private, max-age=60",
        "X-Content-Type-Options": "nosniff"
      }
    });
  } catch (error) {
    logger.error("Image read failed", { err: error });
    return NextResponse.json({ error: "Failed to render image" }, { status: 500 });
  }
}
const openApi = {
  tag: attachmentsTag,
  summary: "Serve resized images",
  methods: {
    GET: {
      summary: "Serve image with optional resizing",
      description: "Returns an image attachment with optional on-the-fly resizing and cropping. Resized images are cached for performance. Only works with image MIME types. Path parameter: {id} - Attachment UUID. Query parameters: ?width=N (1-4000 pixels), ?height=N (1-4000 pixels), ?cropType=cover|contain (resize behavior).",
      responses: [
        {
          status: 200,
          description: "Binary image content (Content-Type: image/jpeg, image/png, etc.)",
          schema: z.any().describe("Binary image content - actual Content-Type header set to image MIME type, not application/json")
        }
      ],
      errors: [
        { status: 400, description: "Invalid parameters, missing ID, or non-image attachment", schema: attachmentErrorSchema },
        { status: 401, description: "Unauthorized - authentication required for private partitions", schema: attachmentErrorSchema },
        { status: 403, description: "Forbidden - insufficient permissions", schema: attachmentErrorSchema },
        { status: 404, description: "Image not found", schema: attachmentErrorSchema },
        { status: 500, description: "Partition misconfigured or image rendering failed", schema: attachmentErrorSchema }
      ]
    }
  }
};
export {
  GET,
  metadata,
  openApi
};
//# sourceMappingURL=route.js.map
