import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { Message, MessageAccessToken, MessageObject, MessageRecipient } from "../../../data/entities.js";
import { MAX_TOKEN_USE_COUNT } from "../../../commands/tokens.js";
import { messageTokenResponseSchema } from "../../openapi.js";
import { hashAuthToken } from "../../../../auth/lib/tokenHash.js";
const metadata = {
  GET: { requireAuth: false }
};
function responseForTokenError(error) {
  if (!(error instanceof Error)) return null;
  if (error.message === "Invalid or expired link") {
    return Response.json({ error: "Invalid or expired link" }, { status: 404 });
  }
  if (error.message === "This link has expired") {
    return Response.json({ error: "This link has expired" }, { status: 410 });
  }
  if (error.message === "This link can no longer be used") {
    return Response.json({ error: "This link can no longer be used" }, { status: 409 });
  }
  if (error.message === "Message not found") {
    return Response.json({ error: "Message not found" }, { status: 404 });
  }
  return null;
}
async function resolveTokenAccess(em, token) {
  const hashedToken = hashAuthToken(token);
  const accessToken = await findOneWithDecryption(em, MessageAccessToken, { token: hashedToken });
  if (!accessToken) {
    throw new Error("Invalid or expired link");
  }
  if (accessToken.expiresAt < /* @__PURE__ */ new Date()) {
    throw new Error("This link has expired");
  }
  if (accessToken.useCount >= MAX_TOKEN_USE_COUNT) {
    throw new Error("This link can no longer be used");
  }
  const message = await findOneWithDecryption(em, Message, {
    id: accessToken.messageId,
    deletedAt: null
  });
  if (!message) {
    throw new Error("Message not found");
  }
  const tenantScope = { tenantId: message.tenantId, organizationId: message.organizationId ?? null };
  const recipient = await findOneWithDecryption(
    em,
    MessageRecipient,
    {
      messageId: accessToken.messageId,
      recipientUserId: accessToken.recipientUserId,
      deletedAt: null
    },
    void 0,
    tenantScope
  );
  if (!recipient) {
    throw new Error("Invalid or expired link");
  }
  return {
    message,
    recipientUserId: accessToken.recipientUserId
  };
}
function resolveAuthenticatedUserId(auth) {
  if (!auth) return null;
  if (auth.isApiKey && typeof auth.userId === "string") return auth.userId;
  return auth.sub;
}
async function GET(req, { params }) {
  const container = await createRequestContainer();
  const commandBus = container.resolve("commandBus");
  const em = container.resolve("em");
  let access;
  try {
    access = await resolveTokenAccess(em, params.token);
  } catch (error) {
    const errorResponse = responseForTokenError(error);
    if (errorResponse) return errorResponse;
    throw error;
  }
  const tenantScope = { tenantId: access.message.tenantId, organizationId: access.message.organizationId ?? null };
  const objects = await findWithDecryption(
    em,
    MessageObject,
    { messageId: access.message.id },
    void 0,
    tenantScope
  );
  const requiresAuth = objects.some((item) => item.actionRequired);
  let auth = null;
  if (requiresAuth) {
    auth = await getAuthFromRequest(req);
    if (!auth) {
      return Response.json({ requiresAuth: true });
    }
    if (resolveAuthenticatedUserId(auth) !== access.recipientUserId) {
      return Response.json({ error: "Forbidden", requiresAuth: true }, { status: 403 });
    }
  }
  let commandResult;
  try {
    const executed = await commandBus.execute("messages.tokens.consume", {
      input: { token: params.token },
      ctx: {
        container,
        auth,
        organizationScope: null,
        selectedOrganizationId: null,
        organizationIds: null,
        request: req
      }
    });
    commandResult = executed.result;
  } catch (error) {
    const errorResponse = responseForTokenError(error);
    if (errorResponse) return errorResponse;
    throw error;
  }
  const message = await findOneWithDecryption(
    em,
    Message,
    { id: commandResult.messageId, tenantId: access.message.tenantId, deletedAt: null },
    void 0,
    tenantScope
  );
  if (!message) {
    return Response.json({ error: "Message not found" }, { status: 404 });
  }
  return Response.json({
    id: message.id,
    type: message.type,
    subject: message.subject,
    body: message.body,
    bodyFormat: message.bodyFormat,
    priority: message.priority,
    senderUserId: message.senderUserId,
    sentAt: message.sentAt,
    actionData: message.actionData,
    actionTaken: message.actionTaken,
    actionTakenAt: message.actionTakenAt,
    actionTakenByUserId: message.actionTakenByUserId,
    objects: objects.map((item) => ({
      id: item.id,
      entityModule: item.entityModule,
      entityType: item.entityType,
      entityId: item.entityId,
      actionRequired: item.actionRequired,
      actionType: item.actionType,
      actionLabel: item.actionLabel,
      snapshot: item.entitySnapshot
    })),
    requiresAuth: objects.some((item) => item.actionRequired),
    recipientUserId: commandResult.recipientUserId
  });
}
const openApi = {
  tag: "Messages",
  methods: {
    GET: {
      summary: "Access message via token",
      responses: [
        {
          status: 200,
          description: "Message detail via token",
          schema: messageTokenResponseSchema
        },
        { status: 404, description: "Invalid or expired link" },
        { status: 409, description: "Token usage exceeded" },
        { status: 410, description: "Token expired" }
      ]
    }
  }
};
export {
  GET,
  metadata,
  openApi
};
//# sourceMappingURL=route.js.map
