import { NextResponse } from "next/server";
import { z } from "zod";
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
import { ChannelThreadMapping, ExternalConversation, MessageChannelLink } from "../../../data/entities.js";
import {
  COMMUNICATION_CHANNELS_CONNECT_CREDENTIAL_CHANNEL_COMMAND_ID
} from "../../../commands/connect-credential-channel.js";
import { emitCommunicationChannelsEvent } from "../../../events.js";
import {
  TEST_SEED_PROVIDER_KEY,
  ensureTestSeedAdapterRegistered,
  isTestChannelSeedingEnabled
} from "../../../lib/test-seed.js";
const metadata = {
  path: "/communication_channels/test-seed",
  POST: {
    requireAuth: true,
    requireFeatures: ["communication_channels.connect_user_channel"]
  }
};
const addressObjectSchema = z.object({ address: z.string(), name: z.string().optional() });
const addressFieldSchema = z.union([
  z.string(),
  addressObjectSchema,
  z.array(z.union([z.string(), addressObjectSchema]))
]);
const connectChannelSchema = z.object({
  action: z.literal("connect-channel"),
  displayName: z.string().min(1).max(255).optional(),
  externalIdentifier: z.string().min(1).max(255).optional()
});
const emitInboundSchema = z.object({
  action: z.literal("emit-inbound"),
  /** Channel that owns the inbound message; controls authorUserId + default visibility. */
  channelId: z.string().uuid(),
  /** Provider key persisted on the link (defaults to the stub provider). */
  providerKey: z.string().min(1).max(64).optional(),
  /** Normalized inbound addresses (stored under channelPayload). */
  from: addressFieldSchema.optional(),
  to: addressFieldSchema.optional(),
  cc: addressFieldSchema.optional(),
  subject: z.string().max(500).optional(),
  bodyText: z.string().max(2e5).optional(),
  /** RFC2822 Message-ID of this inbound message (for In-Reply-To matching). */
  messageId: z.string().max(500).optional(),
  /** RFC2822 In-Reply-To header (threading-inheritance fallback). */
  inReplyTo: z.string().max(500).optional(),
  references: z.array(z.string().max(500)).max(50).optional(),
  /**
   * Open Mercato `messages.message` thread id this inbound message belongs to.
   * When set, a `messages.message` row is created with this `threadId` so the
   * hub-thread inheritance join can resolve a Person from a sibling message.
   */
  messageThreadId: z.string().uuid().optional(),
  /**
   * Test-only: also create a `ChannelThreadMapping` for the seeded thread. The
   * reaction (`/messages/[id]/reactions`) and thread-assign
   * (`/threads/[id]/assign`) routes resolve the owning channel through this
   * mapping and return 409/404 without it. Opt-in so the existing CRM-link
   * seeds (which don't need a mapping) keep their current mapping-free shape.
   */
  createThreadMapping: z.boolean().optional()
});
const bodySchema = z.discriminatedUnion("action", [connectChannelSchema, emitInboundSchema]);
async function POST(req) {
  if (!isTestChannelSeedingEnabled()) {
    return NextResponse.json({ error: "Not found" }, { status: 404 });
  }
  const auth = await getAuthFromRequest(req);
  if (!auth?.sub || !auth?.tenantId) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }
  let body;
  try {
    body = bodySchema.parse(await readJsonSafe(req, null));
  } catch (err) {
    return NextResponse.json(
      { error: err instanceof Error ? err.message : "Invalid request body" },
      { status: 422 }
    );
  }
  const container = await createRequestContainer();
  ensureTestSeedAdapterRegistered();
  const tenantId = auth.tenantId;
  const organizationId = auth.orgId ?? null;
  const userId = auth.sub;
  if (body.action === "connect-channel") {
    const stamp = Date.now();
    const commandBus = container.resolve("commandBus");
    const input = {
      providerKey: TEST_SEED_PROVIDER_KEY,
      displayName: body.displayName ?? `Test Seed Channel ${stamp}`,
      credentials: {
        username: body.externalIdentifier ?? `test-seed-${stamp}@test-seed.local`,
        fromAddress: body.externalIdentifier ?? `test-seed-${stamp}@test-seed.local`
      },
      userId,
      scope: { tenantId, organizationId }
    };
    const { result } = await commandBus.execute(COMMUNICATION_CHANNELS_CONNECT_CREDENTIAL_CHANNEL_COMMAND_ID, {
      input,
      ctx: {
        container,
        auth,
        organizationScope: null,
        selectedOrganizationId: organizationId,
        organizationIds: organizationId ? [organizationId] : null
      }
    });
    if (result.status !== "connected") {
      return NextResponse.json(
        { error: "[internal] test-seed connect failed", detail: result },
        { status: 500 }
      );
    }
    return NextResponse.json(
      { channelId: result.channelId, externalIdentifier: result.externalIdentifier },
      { status: 201 }
    );
  }
  const em = container.resolve("em").fork();
  const providerKey = body.providerKey ?? TEST_SEED_PROVIDER_KEY;
  const conversation = em.create(ExternalConversation, {
    channelId: body.channelId,
    externalConversationId: `inbound-seed:${Date.now()}:${Math.random().toString(16).slice(2, 8)}`,
    subject: body.subject ?? null,
    tenantId,
    organizationId,
    lastMessageAt: /* @__PURE__ */ new Date()
  });
  em.persist(conversation);
  await em.flush();
  const messageRows = await em.getConnection().execute(
    `INSERT INTO messages
       (type, thread_id, sender_user_id, subject, body, body_format, priority, status,
        is_draft, sent_at, visibility, source_entity_type, source_entity_id,
        tenant_id, organization_id, created_at, updated_at)
     VALUES
       (?, ?, ?, ?, ?, 'text', 'normal', 'sent',
        false, now(), 'public', 'communication_channels.test_seed_inbound', ?,
        ?, ?, now(), now())
     RETURNING id`,
    [
      `channel.${providerKey}`,
      body.messageThreadId ?? null,
      userId,
      body.subject ?? "(no subject)",
      body.bodyText ?? "",
      body.channelId,
      tenantId,
      organizationId
    ]
  );
  const messageId = messageRows[0]?.id;
  if (!messageId) {
    return NextResponse.json({ error: "[internal] failed to seed message row" }, { status: 500 });
  }
  const link = em.create(MessageChannelLink, {
    messageId,
    externalConversationId: conversation.id,
    providerKey,
    channelType: "email",
    direction: "inbound",
    deliveryStatus: "delivered",
    channelPayload: {
      ...body.from !== void 0 ? { from: body.from } : {},
      ...body.to !== void 0 ? { to: body.to } : {},
      ...body.cc !== void 0 ? { cc: body.cc } : {},
      ...body.subject !== void 0 ? { subject: body.subject } : {},
      ...body.bodyText !== void 0 ? { text: body.bodyText } : {},
      ...body.inReplyTo !== void 0 ? { inReplyTo: body.inReplyTo } : {},
      ...body.references !== void 0 ? { references: body.references } : {}
    },
    channelContentType: "text/plain",
    channelMetadata: {
      ...body.messageId !== void 0 ? { messageId: body.messageId } : {}
    },
    tenantId,
    organizationId
  });
  em.persist(link);
  await em.flush();
  if (body.createThreadMapping) {
    const mapping = em.create(ChannelThreadMapping, {
      externalConversationId: conversation.id,
      messageThreadId: body.messageThreadId ?? messageId,
      channelId: body.channelId,
      providerKey,
      externalThreadRef: conversation.externalConversationId,
      tenantId,
      organizationId
    });
    em.persist(mapping);
    await em.flush();
  }
  await emitCommunicationChannelsEvent(
    "communication_channels.message.received",
    {
      channelLinkId: link.id,
      channelId: body.channelId,
      providerKey,
      direction: "inbound",
      tenantId,
      organizationId
    },
    { persistent: true }
  );
  return NextResponse.json(
    { channelLinkId: link.id, messageId, conversationId: conversation.id },
    { status: 201 }
  );
}
const openApi = {
  tags: ["CommunicationChannels"],
  methods: {
    POST: {
      summary: "Test-only: seed a connected channel or emit an inbound message (env-gated)",
      tags: ["CommunicationChannels"],
      responses: [
        { status: 201, description: "Channel seeded / inbound message emitted" },
        { status: 401, description: "Unauthorized" },
        { status: 404, description: "Test channel seeding disabled (production default)" },
        { status: 422, description: "Invalid request body" },
        { status: 500, description: "Seed failed" }
      ]
    }
  }
};
var route_default = POST;
export {
  POST,
  route_default as default,
  metadata,
  openApi
};
//# sourceMappingURL=route.js.map
