import { NextResponse } from "next/server";
import { raw } from "@mikro-orm/core";
import { Resend } from "resend";
import { parseBooleanWithDefault } from "@open-mercato/shared/lib/boolean";
import { resolveDefaultEmailFromAddress } from "@open-mercato/shared/lib/email/config";
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { InboxProposalAction, InboxEmail } from "../../../../../../data/entities.js";
import { draftReplyPayloadSchema } from "../../../../../../data/validators.js";
import { emitInboxOpsEvent } from "../../../../../../events.js";
import { createMessageRecordForReply } from "../../../../../../lib/messagesIntegration.js";
import {
  resolveRequestContext,
  resolveProposal,
  extractPathSegment,
  handleRouteError,
  isErrorResponse
} from "../../../../../routeHelpers.js";
import { createLogger } from "@open-mercato/shared/lib/logger";
const logger = createLogger("inbox_ops").child({ component: "reply-send" });
const metadata = {
  POST: { requireAuth: true, requireFeatures: ["inbox_ops.replies.send"] }
};
async function POST(req) {
  try {
    const ctx = await resolveRequestContext(req);
    const url = new URL(req.url);
    const proposal = await resolveProposal(url, ctx);
    if (isErrorResponse(proposal)) return proposal;
    const replyId = extractPathSegment(url, "replies");
    if (!replyId) {
      return NextResponse.json({ error: "Missing reply ID" }, { status: 400 });
    }
    const action = await findOneWithDecryption(
      ctx.em,
      InboxProposalAction,
      {
        id: replyId,
        proposalId: proposal.id,
        actionType: "draft_reply",
        organizationId: ctx.organizationId,
        tenantId: ctx.tenantId,
        deletedAt: null
      },
      void 0,
      ctx.scope
    );
    if (!action) {
      return NextResponse.json({ error: "Reply action not found" }, { status: 404 });
    }
    if (action.status !== "executed") {
      return NextResponse.json(
        { error: `Cannot send reply \u2014 action must be accepted first (current status: "${action.status}")` },
        { status: 409 }
      );
    }
    const existingMetadata = action.metadata && typeof action.metadata === "object" ? action.metadata : {};
    if (existingMetadata.replySentAt) {
      return NextResponse.json(
        { error: "Reply has already been sent for this action" },
        { status: 409 }
      );
    }
    const email = await findOneWithDecryption(
      ctx.em,
      InboxEmail,
      { id: proposal.inboxEmailId, deletedAt: null },
      void 0,
      ctx.scope
    );
    const payloadResult = draftReplyPayloadSchema.safeParse(action.payload);
    if (!payloadResult.success) {
      return NextResponse.json({ error: "Reply payload missing required fields (to, subject, body)" }, { status: 400 });
    }
    const { to: toAddress, toName, subject, body } = payloadResult.data;
    const apiKey = process.env.RESEND_API_KEY;
    if (!apiKey) {
      return NextResponse.json({ error: "Email service not configured" }, { status: 503 });
    }
    const emailDisabled = parseBooleanWithDefault(process.env.OM_DISABLE_EMAIL_DELIVERY, false) || parseBooleanWithDefault(process.env.OM_TEST_MODE, false);
    if (emailDisabled) {
      return NextResponse.json({ error: "Email delivery is disabled" }, { status: 503 });
    }
    const { inReplyToMessageId, references: payloadReferences } = payloadResult.data;
    const fromAddress = resolveDefaultEmailFromAddress();
    if (!fromAddress) {
      return NextResponse.json({
        error: "Email sender is not configured. Set NOTIFICATIONS_EMAIL_FROM, EMAIL_FROM, or ADMIN_EMAIL."
      }, { status: 503 });
    }
    const headers = {};
    const inReplyTo = inReplyToMessageId || email?.messageId;
    if (inReplyTo) {
      headers["In-Reply-To"] = inReplyTo;
    }
    const references = payloadReferences || email?.emailReferences;
    if (references && references.length > 0) {
      headers["References"] = references.join(" ");
    }
    const sendClaimedAt = (/* @__PURE__ */ new Date()).toISOString();
    const claimed = await ctx.em.nativeUpdate(
      InboxProposalAction,
      {
        id: action.id,
        proposalId: proposal.id,
        actionType: "draft_reply",
        organizationId: ctx.organizationId,
        tenantId: ctx.tenantId,
        deletedAt: null,
        status: "executed",
        [raw(`coalesce("metadata"->>'replySentAt', '')`)]: "",
        [raw(`coalesce("metadata"->>'replySendClaimedAt', '')`)]: ""
      },
      {
        metadata: raw(
          `coalesce("metadata", '{}'::jsonb) || jsonb_build_object('replySendClaimedAt', ?::text)`,
          [sendClaimedAt]
        )
      }
    );
    if (claimed === 0) {
      return NextResponse.json(
        { error: "Reply send is already in progress or has been sent for this action" },
        { status: 409 }
      );
    }
    const releaseSendClaim = async () => {
      try {
        await ctx.em.nativeUpdate(
          InboxProposalAction,
          {
            id: action.id,
            organizationId: ctx.organizationId,
            tenantId: ctx.tenantId,
            [raw(`coalesce("metadata"->>'replySentAt', '')`)]: "",
            [raw(`("metadata"->>'replySendClaimedAt')`)]: sendClaimedAt
          },
          { metadata: raw(`"metadata" - 'replySendClaimedAt'`) }
        );
      } catch (releaseError) {
        logger.error("Failed to release send claim", { err: releaseError });
      }
    };
    const resend = new Resend(apiKey);
    const { data: sendData, error: sendError } = await resend.emails.send({
      to: toAddress,
      from: fromAddress,
      subject,
      text: body,
      ...Object.keys(headers).length > 0 ? { headers } : {}
    });
    if (sendError) {
      await releaseSendClaim();
      const errorMessage = sendError.message || "Unknown error";
      return NextResponse.json({ error: `Failed to send email: ${errorMessage}` }, { status: 502 });
    }
    const sentMessageId = sendData?.id || null;
    const messagesResult = await createMessageRecordForReply(
      { to: toAddress, toName, subject, body },
      proposal.inboxEmailId,
      {
        container: ctx.container,
        scope: {
          tenantId: ctx.tenantId,
          organizationId: ctx.organizationId,
          userId: ctx.userId
        }
      }
    );
    action.metadata = {
      ...existingMetadata,
      replySentAt: (/* @__PURE__ */ new Date()).toISOString(),
      sentMessageId,
      ...messagesResult ? { messageRecordId: messagesResult.messageId } : {}
    };
    await ctx.em.flush();
    try {
      await emitInboxOpsEvent("inbox_ops.reply.sent", {
        proposalId: proposal.id,
        actionId: replyId,
        tenantId: ctx.tenantId,
        organizationId: ctx.organizationId,
        toAddress,
        sentMessageId,
        messageRecordId: messagesResult?.messageId ?? null
      });
    } catch (eventError) {
      logger.error("Failed to emit event", { err: eventError });
    }
    return NextResponse.json({
      ok: true,
      sentMessageId,
      ...messagesResult ? { messageRecordId: messagesResult.messageId } : {}
    });
  } catch (err) {
    return handleRouteError(err, "send reply");
  }
}
const openApi = {
  tag: "InboxOps",
  summary: "Send draft reply",
  methods: {
    POST: {
      summary: "Send a draft reply email and register it in messages when available",
      description: "Sends the draft_reply action payload via the configured email provider. When the messages module is available, also records the sent reply as an internal message record. Sets In-Reply-To and References headers for threading.",
      responses: [
        { status: 200, description: "Reply sent successfully" },
        { status: 400, description: "Missing required payload fields" },
        { status: 404, description: "Reply action not found" },
        { status: 409, description: "Action in invalid state for sending, or a send is already in progress / completed for this reply" },
        { status: 502, description: "Email delivery failed" },
        { status: 503, description: "Email service not configured or disabled" }
      ]
    }
  }
};
export {
  POST,
  metadata,
  openApi
};
//# sourceMappingURL=route.js.map
