import { Buffer } from "node:buffer";
import { readdir } from "node:fs/promises";
import path from "node:path";
import { appendJsonl, createId, ensureDir, nowIso, readJsonFile, readJsonl, sanitizeText, withFileLock, writeJsonFile } from "../../shared/index.js";
import { appendEvent } from "../observability/index.js";
import { ensureTeamRuntime, teamInboxRoot, teamRoot } from "./store.js";
import { enqueueAgentMessage, patchAgentMessage } from "../message-bus/index.js";
function inboxDir(store, runId, member) {
    return path.join(teamInboxRoot(store, runId), member);
}
function messagePath(store, runId, member, messageId) {
    return path.join(inboxDir(store, runId, member), `${messageId}.json`);
}
export function teamConversationPath(store, runId) {
    return path.join(teamRoot(store, runId), "conversation.jsonl");
}
export async function appendTeamConversationEntry(store, runId, input) {
    const entry = {
        version: 1,
        at: input.at ?? nowIso(),
        run_id: input.run_id ?? runId,
        type: input.type,
        from: input.from,
        to: input.to,
        kind: input.kind,
        body: input.body,
        message_id: input.message_id,
        agent_call_id: input.agent_call_id,
        launch_id: input.launch_id,
        child_session_id: input.child_session_id,
        background_id: input.background_id,
        prompt_ref: input.prompt_ref,
        prompt_summary: input.prompt_summary,
        status: input.status,
        metadata: input.metadata ?? {},
    };
    await appendJsonl(teamConversationPath(store, runId), entry);
    return entry;
}
export async function readTeamConversation(store, runId) {
    return readJsonl(teamConversationPath(store, runId));
}
export async function sendTeamMessage(store, runId, input) {
    const runtime = await ensureTeamRuntime(store, runId);
    if (!runtime.members.some((member) => member.name === input.to)) {
        throw new Error(`team message recipient not found: ${input.to}`);
    }
    const body = sanitizeText(input.body);
    if (Buffer.byteLength(body, "utf8") > store.config.team_mode.message_payload_max_bytes) {
        throw new Error("team message payload exceeds configured limit");
    }
    const unread = await listTeamInbox(store, runId, input.to, { includeAcknowledged: false });
    if (unread.length >= store.config.team_mode.recipient_unread_max_messages) {
        throw new Error("recipient team inbox is full");
    }
    const envelope = await enqueueAgentMessage(store, runId, {
        from: input.from,
        to: input.to,
        body,
        kind: input.kind ?? "message",
        context_refs: input.context_refs,
        metadata: input.metadata,
    });
    const message = {
        message_id: envelope.message_id,
        run_id: runId,
        from: input.from,
        to: input.to,
        body,
        kind: input.kind ?? "message",
        created_at: nowIso(),
        acknowledged_at: null,
        metadata: input.metadata ?? {},
    };
    await ensureDir(inboxDir(store, runId, input.to));
    await writeJsonFile(messagePath(store, runId, input.to, message.message_id), message);
    await appendTeamConversationEntry(store, runId, {
        type: "message",
        at: message.created_at,
        message_id: message.message_id,
        from: message.from,
        to: message.to,
        kind: message.kind,
        body: message.body,
        status: envelope.status,
        metadata: {
            ...message.metadata,
            message_envelope_id: envelope.message_id,
            body_ref: envelope.body_ref,
            correlation_id: envelope.correlation_id,
            context_refs: envelope.context_refs,
        },
    });
    await appendEvent(store, runId, {
        type: "team_message",
        actor: input.from,
        summary: `to ${input.to}: ${message.body.slice(0, 120)}`,
        metadata: { message_id: message.message_id, to: input.to, kind: message.kind },
    });
    return message;
}
export async function sendTeamBroadcast(store, runId, input) {
    const runtime = await ensureTeamRuntime(store, runId);
    const explicitRecipients = input.to?.map((member) => sanitizeText(member).trim()).filter(Boolean);
    const recipients = unique(explicitRecipients?.length ? explicitRecipients : runtime.members.map((member) => String(member.name)).filter((member) => member !== input.from));
    if (!recipients.length)
        throw new Error("team broadcast has no recipients");
    const broadcastId = createId("team_broadcast");
    const messages = [];
    for (const recipient of recipients) {
        messages.push(await sendTeamMessage(store, runId, {
            from: input.from,
            to: recipient,
            body: input.body,
            kind: input.kind ?? "broadcast",
            metadata: {
                ...(input.metadata ?? {}),
                broadcast_id: broadcastId,
            },
        }));
    }
    await appendEvent(store, runId, {
        type: "team_broadcast",
        actor: input.from,
        summary: `broadcast to ${recipients.length} member(s): ${sanitizeText(input.body).slice(0, 120)}`,
        metadata: { broadcast_id: broadcastId, recipients, message_ids: messages.map((message) => message.message_id) },
    });
    return { broadcast_id: broadcastId, messages, recipients };
}
export async function listTeamInbox(store, runId, member, options = {}) {
    let files = [];
    try {
        files = await readdir(inboxDir(store, runId, member));
    }
    catch (error) {
        if (isMissingPathError(error))
            return [];
        throw error;
    }
    const messages = await Promise.all(files
        .filter((file) => file.endsWith(".json"))
        .map((file) => readJsonFile(path.join(inboxDir(store, runId, member), file))));
    return messages
        .filter((message) => message !== undefined)
        .filter((message) => options.includeAcknowledged === true || message.acknowledged_at === null)
        .sort((left, right) => left.created_at.localeCompare(right.created_at));
}
export async function acknowledgeTeamMessages(store, runId, member, messageIds) {
    await ensureTeamRuntime(store, runId);
    const acknowledged = [];
    for (const messageId of messageIds) {
        const file = messagePath(store, runId, member, messageId);
        await withFileLock(file, async () => {
            const message = await readJsonFile(file);
            if (!message)
                return;
            if (message.acknowledged_at === null) {
                message.acknowledged_at = nowIso();
                await writeJsonFile(file, message);
            }
            await patchAgentMessage(store, runId, messageId, {
                status: "acknowledged",
                acknowledged_at: message.acknowledged_at ?? nowIso(),
            });
            acknowledged.push(messageId);
        });
    }
    if (acknowledged.length) {
        await appendTeamConversationEntry(store, runId, {
            type: "ack",
            from: member,
            to: "team-runtime",
            status: "acknowledged",
            metadata: {
                member,
                message_ids: acknowledged,
            },
        });
        await appendEvent(store, runId, {
            type: "team_messages_acknowledged",
            actor: member,
            summary: `${member} acknowledged ${acknowledged.length} team message(s)`,
            metadata: { message_ids: acknowledged },
        });
    }
    return acknowledged;
}
function isMissingPathError(error) {
    return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
}
function unique(values) {
    return [...new Set(values)];
}
//# sourceMappingURL=mailbox.js.map